obs-filters: Add Cube LUT file support
Tested Cube LUT examples from Photoshop, Adobe spec, and some homebrew. I don't know how to use the domain fields, so they are being ignored.
This commit is contained in:
parent
4d6cc442b2
commit
4ea7424ebb
@ -1,6 +1,8 @@
|
||||
#include <obs-module.h>
|
||||
#include <graphics/half.h>
|
||||
#include <graphics/image-file.h>
|
||||
#include <util/dstr.h>
|
||||
#include <util/platform.h>
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
@ -18,8 +20,12 @@ struct lut_filter_data {
|
||||
obs_source_t *context;
|
||||
gs_effect_t *effect;
|
||||
gs_texture_t *target;
|
||||
|
||||
gs_image_file_t image;
|
||||
|
||||
uint32_t cube_width;
|
||||
void *cube_data;
|
||||
|
||||
char *file;
|
||||
float clut_amount;
|
||||
float clut_scale;
|
||||
@ -32,10 +38,10 @@ static const char *color_grade_filter_get_name(void *unused)
|
||||
return obs_module_text("ColorGradeFilter");
|
||||
}
|
||||
|
||||
static gs_texture_t *make_clut_texture(const enum gs_color_format format,
|
||||
const uint32_t image_width,
|
||||
const uint32_t image_height,
|
||||
const uint8_t *data)
|
||||
static gs_texture_t *make_clut_texture_png(const enum gs_color_format format,
|
||||
const uint32_t image_width,
|
||||
const uint32_t image_height,
|
||||
const uint8_t *data)
|
||||
{
|
||||
if (image_width % LUT_WIDTH != 0)
|
||||
return NULL;
|
||||
@ -80,12 +86,179 @@ static gs_texture_t *make_clut_texture(const enum gs_color_format format,
|
||||
return texture;
|
||||
}
|
||||
|
||||
static bool get_cube_entry(FILE *const file, float *const red,
|
||||
float *const green, float *const blue)
|
||||
{
|
||||
bool data_found = false;
|
||||
|
||||
char line[256];
|
||||
while (fgets(line, sizeof(line), file)) {
|
||||
if (sscanf(line, "%f %f %f", red, green, blue) == 3) {
|
||||
data_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return data_found;
|
||||
}
|
||||
|
||||
static void *load_1d_lut(FILE *const file, const uint32_t width, float red,
|
||||
float green, float blue)
|
||||
{
|
||||
const uint32_t data_size =
|
||||
4 * width * width * width * sizeof(struct half);
|
||||
struct half *values = bmalloc(data_size);
|
||||
|
||||
bool data_found = true;
|
||||
for (uint32_t index = 0; index < width; ++index) {
|
||||
if (!data_found) {
|
||||
bfree(values);
|
||||
values = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint32_t z = 0; z < width; ++z) {
|
||||
const uint32_t z_offset = z * width * width;
|
||||
for (uint32_t y = 0; y < width; ++y) {
|
||||
const uint32_t y_offset = y * width;
|
||||
const uint32_t offset =
|
||||
4 * (index + y_offset + z_offset);
|
||||
values[offset] = half_from_float(red);
|
||||
values[offset + 3] =
|
||||
half_from_bits(0x3C00); // 1.0
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t z = 0; z < width; ++z) {
|
||||
const uint32_t z_offset = z * width * width;
|
||||
for (uint32_t x = 0; x < width; ++x) {
|
||||
const uint32_t offset =
|
||||
4 * (x + (index * width) + z_offset) +
|
||||
1;
|
||||
values[offset] = half_from_float(green);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t y = 0; y < width; ++y) {
|
||||
const uint32_t y_offset = y * width;
|
||||
for (uint32_t x = 0; x < width; ++x) {
|
||||
const uint32_t offset =
|
||||
4 * (x + y_offset +
|
||||
(index * width * width)) +
|
||||
2;
|
||||
values[offset] = half_from_float(blue);
|
||||
}
|
||||
}
|
||||
|
||||
data_found = get_cube_entry(file, &red, &green, &blue);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
static void *load_3d_lut(FILE *const file, const uint32_t width, float red,
|
||||
float green, float blue)
|
||||
{
|
||||
const uint32_t data_size =
|
||||
4 * width * width * width * sizeof(struct half);
|
||||
struct half *values = bmalloc(data_size);
|
||||
|
||||
size_t offset = 0;
|
||||
bool data_found = true;
|
||||
for (uint32_t z = 0; z < width; ++z) {
|
||||
for (uint32_t y = 0; y < width; ++y) {
|
||||
for (uint32_t x = 0; x < width; ++x) {
|
||||
if (!data_found) {
|
||||
bfree(values);
|
||||
values = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
values[offset++] = half_from_float(red);
|
||||
values[offset++] = half_from_float(green);
|
||||
values[offset++] = half_from_float(blue);
|
||||
values[offset++] =
|
||||
half_from_bits(0x3c00); // 1.0
|
||||
|
||||
data_found = get_cube_entry(file, &red, &green,
|
||||
&blue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
static void *load_cube_file(const char *const path, uint32_t *const width)
|
||||
{
|
||||
void *data = NULL;
|
||||
|
||||
FILE *const file = os_fopen(path, "rb");
|
||||
if (file) {
|
||||
float min_value[] = {0.0f, 0.0f, 0.0f};
|
||||
float max_value[] = {1.0f, 1.0f, 1.0f};
|
||||
float red, green, blue;
|
||||
unsigned width_1d = 0;
|
||||
unsigned width_3d = 0;
|
||||
|
||||
bool data_found = false;
|
||||
|
||||
char line[256];
|
||||
unsigned u;
|
||||
float f[3];
|
||||
while (fgets(line, sizeof(line), file)) {
|
||||
if (sscanf(line, "%f %f %f", &red, &green, &blue) ==
|
||||
3) {
|
||||
/* no more metadata */
|
||||
data_found = true;
|
||||
break;
|
||||
} else if (sscanf(line, "DOMAIN_MIN %f %f %f", &f[0],
|
||||
&f[1], &f[2]) == 3) {
|
||||
min_value[0] = f[0];
|
||||
min_value[1] = f[1];
|
||||
min_value[2] = f[2];
|
||||
} else if (sscanf(line, "DOMAIN_MAX %f %f %f", &f[0],
|
||||
&f[1], &f[2]) == 3) {
|
||||
max_value[0] = f[0];
|
||||
max_value[1] = f[1];
|
||||
max_value[2] = f[2];
|
||||
} else if (sscanf(line, "LUT_1D_SIZE %u", &u) == 1) {
|
||||
width_1d = u;
|
||||
} else if (sscanf(line, "LUT_3D_SIZE %u", &u) == 1) {
|
||||
width_3d = u;
|
||||
}
|
||||
}
|
||||
|
||||
if (data_found) {
|
||||
if (width_1d > 0) {
|
||||
data = load_1d_lut(file, width_1d, red, green,
|
||||
blue);
|
||||
if (data)
|
||||
*width = width_1d;
|
||||
} else if (width_3d > 0) {
|
||||
data = load_3d_lut(file, width_3d, red, green,
|
||||
blue);
|
||||
if (data)
|
||||
*width = width_3d;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void color_grade_filter_update(void *data, obs_data_t *settings)
|
||||
{
|
||||
struct lut_filter_data *filter = data;
|
||||
|
||||
const char *path = obs_data_get_string(settings, SETTING_IMAGE_PATH);
|
||||
double clut_amount = obs_data_get_double(settings, SETTING_CLUT_AMOUNT);
|
||||
if (path && (*path == '\0'))
|
||||
path = NULL;
|
||||
|
||||
const double clut_amount =
|
||||
obs_data_get_double(settings, SETTING_CLUT_AMOUNT);
|
||||
|
||||
bfree(filter->file);
|
||||
if (path)
|
||||
@ -93,25 +266,46 @@ static void color_grade_filter_update(void *data, obs_data_t *settings)
|
||||
else
|
||||
filter->file = NULL;
|
||||
|
||||
bfree(filter->cube_data);
|
||||
filter->cube_data = NULL;
|
||||
|
||||
obs_enter_graphics();
|
||||
gs_image_file_free(&filter->image);
|
||||
gs_voltexture_destroy(filter->target);
|
||||
filter->target = NULL;
|
||||
obs_leave_graphics();
|
||||
|
||||
gs_image_file_init(&filter->image, path);
|
||||
if (path) {
|
||||
const char *const ext = os_get_path_extension(path);
|
||||
if (ext && astrcmpi(ext, ".cube") == 0) {
|
||||
filter->cube_data =
|
||||
load_cube_file(path, &filter->cube_width);
|
||||
} else {
|
||||
gs_image_file_init(&filter->image, path);
|
||||
}
|
||||
}
|
||||
|
||||
obs_enter_graphics();
|
||||
|
||||
gs_voltexture_destroy(filter->target);
|
||||
if (filter->image.loaded) {
|
||||
filter->target = make_clut_texture(filter->image.format,
|
||||
filter->image.cx,
|
||||
filter->image.cy,
|
||||
filter->image.texture_data);
|
||||
if (path) {
|
||||
if (filter->image.loaded) {
|
||||
filter->target = make_clut_texture_png(
|
||||
filter->image.format, filter->image.cx,
|
||||
filter->image.cy, filter->image.texture_data);
|
||||
filter->clut_scale =
|
||||
(float)(LUT_WIDTH - 1) / (float)LUT_WIDTH;
|
||||
filter->clut_offset = 0.5f / (float)LUT_WIDTH;
|
||||
} else if (filter->cube_data) {
|
||||
const uint32_t width = filter->cube_width;
|
||||
filter->target = gs_voltexture_create(
|
||||
width, width, width, GS_RGBA16F, 1,
|
||||
(uint8_t **)&filter->cube_data, 0);
|
||||
filter->clut_scale = (float)(width - 1) / (float)width;
|
||||
filter->clut_offset = 0.5f / (float)width;
|
||||
}
|
||||
}
|
||||
|
||||
filter->clut_amount = (float)clut_amount;
|
||||
filter->clut_scale = (float)(LUT_WIDTH - 1) / (float)LUT_WIDTH;
|
||||
filter->clut_offset = 0.5f / (float)LUT_WIDTH;
|
||||
|
||||
char *effect_path = obs_module_file("color_grade_filter.effect");
|
||||
gs_effect_destroy(filter->effect);
|
||||
@ -135,7 +329,7 @@ static obs_properties_t *color_grade_filter_properties(void *data)
|
||||
obs_properties_t *props = obs_properties_create();
|
||||
struct dstr filter_str = {0};
|
||||
|
||||
dstr_cat(&filter_str, "(*.png)");
|
||||
dstr_cat(&filter_str, "(*.cube;*.png)");
|
||||
|
||||
if (s && s->file && *s->file) {
|
||||
dstr_copy(&path, s->file);
|
||||
@ -184,6 +378,7 @@ static void color_grade_filter_destroy(void *data)
|
||||
gs_image_file_free(&filter->image);
|
||||
obs_leave_graphics();
|
||||
|
||||
bfree(filter->cube_data);
|
||||
bfree(filter->file);
|
||||
bfree(filter);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user