Pixel-Composer/scripts/node_palette_extract/node_palette_extract.gml

320 lines
8.9 KiB
Text
Raw Normal View History

2023-12-10 02:48:10 +01:00
function Node_Palette_Extract(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) constructor {
2023-01-25 06:49:00 +01:00
name = "Palette Extract";
2024-05-02 12:13:35 +02:00
setDimension(96, 48);;
2023-01-17 08:11:55 +01:00
2024-08-18 06:16:20 +02:00
newInput(0, nodeValue_Surface("Surface in", self));
2023-01-17 08:11:55 +01:00
2024-08-18 09:13:41 +02:00
newInput(1, nodeValue_Int("Max colors", self, 5, "Amount of color in a palette."))
2023-02-14 05:32:32 +01:00
.rejectArray();
2023-01-17 08:11:55 +01:00
2024-08-18 09:13:41 +02:00
newInput(2, nodeValue_Int("Seed", self, seed_random(6), "Random seed to be used to initialize K-mean algorithm."))
2024-08-08 06:57:51 +02:00
.setDisplay(VALUE_DISPLAY._default, { side_button : button(function() { randomize(); inputs[2].setValue(seed_random(6)); }).setIcon(THEME.icon_random, 0, COLORS._main_icon) })
2023-02-14 05:32:32 +01:00
.rejectArray();
2023-01-25 06:49:00 +01:00
2024-08-18 09:13:41 +02:00
newInput(3, nodeValue_Enum_Scroll("Algorithm", self, 0, { data: [ "K-mean", "Frequency", "All colors" ], update_hover: false }))
2023-02-14 05:32:32 +01:00
.rejectArray();
2023-01-17 08:11:55 +01:00
2024-08-18 09:13:41 +02:00
newInput(4, nodeValue_Enum_Scroll("Color Space", self, 1, { data: [ "RGB", "HSV" ], update_hover: false }))
.rejectArray();
2024-09-04 03:57:11 +02:00
newOutput(0, nodeValue_Output("Palette", self, VALUE_TYPE.color, [ ]))
2023-01-17 08:11:55 +01:00
.setDisplay(VALUE_DISPLAY.palette);
static getPreviewValues = function() { return getInputData(0); }
2023-01-17 08:11:55 +01:00
input_display_list = [
["Surfaces", true], 0,
["Palette", false], 3, 4, 1, 2,
2023-01-17 08:11:55 +01:00
]
current_palette = [];
current_color = 0;
2023-03-19 09:17:39 +01:00
attribute_surface_depth();
2024-08-19 06:16:12 +02:00
function sortPalette(pal) {
2023-01-25 06:49:00 +01:00
array_sort(pal, function(c0, c1) {
2024-01-09 03:39:40 +01:00
var r0 = _color_get_red(c0);
var r1 = _color_get_red(c1);
var g0 = _color_get_green(c0);
var g1 = _color_get_green(c1);
var b0 = _color_get_blue(c0);
var b1 = _color_get_blue(c1);
2023-01-25 06:49:00 +01:00
var l0 = sqrt( .241 * r0 + .691 * g0 + .068 * b0 );
var l1 = sqrt( .241 * r1 + .691 * g1 + .068 * b1 );
if(abs(l0 - l1) > 0.05) return l0 > l1;
2024-01-09 03:39:40 +01:00
var h0 = _color_get_hue(c0);
var h1 = _color_get_hue(c1);
2023-01-25 06:49:00 +01:00
if(abs(h0 - h1) > 0.05) return h0 > h1;
2024-01-09 03:39:40 +01:00
var s0 = _color_get_saturation(c0);
var s1 = _color_get_saturation(c1);
2023-01-25 06:49:00 +01:00
2024-01-09 03:39:40 +01:00
var v0 = _color_get_value(c0);
var v1 = _color_get_value(c1);
2023-01-25 06:49:00 +01:00
return s0 * v0 > s1 * v1;
})
2024-08-19 06:16:12 +02:00
}
2023-01-25 06:49:00 +01:00
2024-08-19 06:16:12 +02:00
function extractKmean(_surfFull, _size, _seed) {
var _space = getInputData(4);
var _surf = surface_create_valid(min(32, surface_get_width_safe(_surfFull)), min(32, surface_get_height_safe(_surfFull)), attrDepth());
2023-01-17 08:11:55 +01:00
_size = max(1, _size);
2023-09-08 21:37:36 +02:00
var ww = surface_get_width_safe(_surf);
var hh = surface_get_height_safe(_surf);
2023-01-17 08:11:55 +01:00
surface_set_shader(_surf, noone);
draw_surface_stretched_safe(_surfFull, 0, 0, ww, hh);
surface_reset_shader();
2023-01-17 08:11:55 +01:00
var c_buffer = buffer_create(ww * hh * 4, buffer_fixed, 2);
2024-08-19 06:16:12 +02:00
var colors = [];
2023-01-17 08:11:55 +01:00
buffer_get_surface(c_buffer, _surf, 0);
buffer_seek(c_buffer, buffer_seek_start, 0);
var _min = [ 1, 1, 1 ];
var _max = [ 0, 0, 0 ];
var a, b, c, col;
2023-01-17 08:11:55 +01:00
for( var i = 0; i < ww * hh; i++ ) {
b = buffer_read(c_buffer, buffer_u32);
c = b & ~(0b11111111 << 24);
a = b & (0b11111111 << 24);
2023-02-19 02:13:19 +01:00
if(a == 0) continue;
switch(_space) {
2024-08-19 06:16:12 +02:00
case 0 : col = [ _color_get_red(c), _color_get_green(c), _color_get_blue(c), 0 ]; break;
2024-01-09 03:39:40 +01:00
case 1 : col = [ _color_get_hue(c), _color_get_saturation(c), _color_get_value(c), 0 ]; break;
case 2 : col = [ _color_get_hue(c), _color_get_saturation(c), _color_get_value(c), 0 ]; break;
}
2023-02-19 02:13:19 +01:00
array_push(colors, col);
2024-08-19 06:16:12 +02:00
_min[0] = min(_min[0], col[0]); _max[0] = max(_max[0], col[0]);
_min[1] = min(_min[1], col[1]); _max[1] = max(_max[1], col[1]);
_min[2] = min(_min[2], col[2]); _max[2] = max(_max[2], col[2]);
2023-01-17 08:11:55 +01:00
}
buffer_delete(c_buffer);
random_set_seed(_seed);
2024-08-19 06:16:12 +02:00
cnt = array_create_ext(_size, function() /*=>*/ {return [ random(1), random(1), random(1), 0 ]});
2023-01-17 08:11:55 +01:00
repeat(8) {
2024-08-19 06:16:12 +02:00
// var _cnt = array_create_ext(_size, (i) => [ cnt[i][0], cnt[i][1], cnt[i][2], 0 ]);
2023-01-17 08:11:55 +01:00
2023-07-25 20:12:40 +02:00
for( var i = 0, n = array_length(colors); i < n; i++ ) {
2024-08-19 06:16:12 +02:00
var ind = 0;
2023-01-17 08:11:55 +01:00
var dist = 999;
2024-08-19 06:16:12 +02:00
var _cl = colors[i];
2023-01-17 08:11:55 +01:00
for( var j = 0; j < _size; j++ ) {
var _cn = cnt[j];
2024-08-19 06:16:12 +02:00
var d = point_distance_3d(_cl[0], _cl[1], _cl[2], _cn[0], _cn[1], _cn[2]);
2023-01-17 08:11:55 +01:00
if(d < dist) {
dist = d;
ind = j;
}
}
colors[i][3] = ind;
}
for( var i = 0; i < _size; i++ )
cnt[i] = [ 0, 0, 0, 0 ];
2023-07-25 20:12:40 +02:00
for( var i = 0, n = array_length(colors); i < n; i++ ) {
2023-01-17 08:11:55 +01:00
var _cl = colors[i];
2024-08-19 06:16:12 +02:00
var _co = _cl[3];
cnt[_co][0] += _cl[0];
cnt[_co][1] += _cl[1];
cnt[_co][2] += _cl[2];
cnt[_co][3]++;
2023-01-17 08:11:55 +01:00
}
for( var i = 0; i < _size; i++ ) {
2024-08-19 06:16:12 +02:00
var _cc = cnt[i];
cnt[i][0] = _cc[3]? _cc[0] / _cc[3] : 0;
cnt[i][1] = _cc[3]? _cc[1] / _cc[3] : 0;
cnt[i][2] = _cc[3]? _cc[2] / _cc[3] : 0;
2023-01-17 08:11:55 +01:00
}
2024-08-19 06:16:12 +02:00
// var del = array_reduce(cnt, (prev, cur, i) => max(prev, point_distance_3d(cnt[i][0], cnt[i][1], cnt[i][2], cur[0], cur[1], cur[2])), 0);
// if(del < 0.001) break;
2023-01-17 08:11:55 +01:00
}
2023-01-25 06:49:00 +01:00
var palette = [];
var clr;
2023-01-17 08:11:55 +01:00
for( var i = 0; i < _size; i++ ) {
var closet = 0;
2024-08-19 06:16:12 +02:00
var dist = 999;
var _cl = cnt[i];
2023-01-17 08:11:55 +01:00
2024-08-19 06:16:12 +02:00
for( var j = 0, n = array_length(colors); j < n; j++ ) {
2023-01-17 08:11:55 +01:00
var _cn = colors[j];
2024-08-19 06:16:12 +02:00
var d = point_distance_3d(_cl[0], _cl[1], _cl[2], _cn[0], _cn[1], _cn[2]);
2023-01-17 08:11:55 +01:00
if(d < dist) {
2024-08-19 06:16:12 +02:00
dist = d;
2023-01-17 08:11:55 +01:00
closet = j;
}
}
2024-08-19 06:16:12 +02:00
var _cc = colors[closet];
switch(_space) {
2024-08-19 06:16:12 +02:00
case 0 : clr = make_color_rgba(_cc[0] * 255, _cc[1] * 255, _cc[2] * 255, 255); break;
case 1 : clr = make_color_hsva(_cc[0] * 255, _cc[1] * 255, _cc[2] * 255, 255); break;
case 2 : clr = make_color_hsva(_cc[0] * 255, _cc[1] * 255, _cc[2] * 255, 255); break;
}
array_push_unique(palette, clr);
2023-01-17 08:11:55 +01:00
}
surface_free(_surf);
2023-01-25 06:49:00 +01:00
sortPalette(palette);
2023-01-17 08:11:55 +01:00
return palette;
2024-08-19 06:16:12 +02:00
}
2023-01-17 08:11:55 +01:00
2024-08-19 06:16:12 +02:00
function extractAll(_surfFull) {
2023-09-08 21:37:36 +02:00
var ww = surface_get_width_safe(_surfFull);
var hh = surface_get_height_safe(_surfFull);
2023-02-14 05:32:32 +01:00
var c_buffer = buffer_create(ww * hh * 4, buffer_fixed, 2);
buffer_get_surface(c_buffer, _surfFull, 0);
buffer_seek(c_buffer, buffer_seek_start, 0);
var palette = [];
for( var i = 0; i < ww * hh; i++ ) {
2023-02-19 02:13:19 +01:00
var b = buffer_read(c_buffer, buffer_u32);
2024-08-19 06:16:12 +02:00
var c = b;
2023-02-19 02:13:19 +01:00
var a = b & (0b11111111 << 24);
if(a == 0) continue;
2024-08-19 06:16:12 +02:00
c = make_color_rgba(color_get_red(c), color_get_green(c), color_get_blue(c), color_get_alpha(c));
2023-02-14 05:32:32 +01:00
if(!array_exists(palette, c))
array_push(palette, c);
}
buffer_delete(c_buffer);
return palette;
2024-08-19 06:16:12 +02:00
}
2023-02-14 05:32:32 +01:00
2024-08-19 06:16:12 +02:00
function extractFrequence(_surfFull, _size) {
2023-02-14 05:32:32 +01:00
var msize = 128;
2023-09-08 21:37:36 +02:00
var _surf = surface_create_valid(min(msize, surface_get_width_safe(_surfFull)), min(msize, surface_get_height_safe(_surfFull)));
2023-01-25 06:49:00 +01:00
_size = max(1, _size);
2023-09-08 21:37:36 +02:00
var ww = surface_get_width_safe(_surf);
var hh = surface_get_height_safe(_surf);
2023-01-25 06:49:00 +01:00
surface_set_target(_surf);
2024-08-19 06:16:12 +02:00
DRAW_CLEAR
BLEND_OVERRIDE
draw_surface_stretched_safe(_surfFull, 0, 0, ww, hh);
BLEND_NORMAL
2023-01-25 06:49:00 +01:00
surface_reset_target();
var c_buffer = buffer_create(ww * hh * 4, buffer_fixed, 2);
2023-09-14 16:29:39 +02:00
var colors = array_create(ww * hh);
2023-01-25 06:49:00 +01:00
buffer_get_surface(c_buffer, _surf, 0);
buffer_seek(c_buffer, buffer_seek_start, 0);
2023-02-14 05:32:32 +01:00
var clrs = ds_map_create();
2023-01-25 06:49:00 +01:00
for( var i = 0; i < ww * hh; i++ ) {
2023-02-19 02:13:19 +01:00
var b = buffer_read(c_buffer, buffer_u32);
2024-08-19 06:16:12 +02:00
var c = b;
var a = b & (0b_1111_1111 << 24) >> 24;
2023-02-19 02:13:19 +01:00
if(a == 0) continue;
2024-08-19 06:16:12 +02:00
c = make_color_rgba(color_get_red(c), color_get_green(c), color_get_blue(c), color_get_alpha(c));
if(ds_map_exists(clrs, c)) clrs[? c].amount++;
else clrs[? c] = { color: c, amount: 1 };
2023-01-25 06:49:00 +01:00
}
buffer_delete(c_buffer);
2024-08-19 06:16:12 +02:00
var pr = ds_priority_create();
var k = ds_map_find_first(clrs);
2023-02-14 05:32:32 +01:00
var amo = ds_map_size(clrs);
2023-01-25 06:49:00 +01:00
2023-02-14 05:32:32 +01:00
repeat(amo) {
2024-08-19 06:16:12 +02:00
ds_priority_add(pr, clrs[? k].color, clrs[? k].amount);
2023-02-14 05:32:32 +01:00
k = ds_map_find_next(clrs, k);
2023-01-25 06:49:00 +01:00
}
2024-08-19 06:16:12 +02:00
var amo = min(_size, ds_priority_size(pr));
var pal = array_create(amo), ind = 0;
repeat(amo) { pal[ind++] = ds_priority_delete_max(pr); }
2023-02-14 05:32:32 +01:00
ds_priority_destroy(pr);
2023-01-25 06:49:00 +01:00
ds_map_destroy(clrs);
2024-08-19 06:16:12 +02:00
return pal;
}
2023-01-25 06:49:00 +01:00
2024-08-19 06:16:12 +02:00
static step = function() {
var _algo = getInputData(3);
2023-01-25 06:49:00 +01:00
2024-08-08 06:57:51 +02:00
inputs[1].setVisible(_algo != 2);
inputs[2].setVisible(_algo == 0);
inputs[4].setVisible(_algo == 0);
2024-08-19 06:16:12 +02:00
}
2023-01-25 06:49:00 +01:00
2024-08-19 06:16:12 +02:00
static extractPalette = function(_surf, _algo, _size, _seed) {
2023-01-17 08:11:55 +01:00
if(!is_surface(_surf)) return [];
2023-02-14 05:32:32 +01:00
switch(_algo) {
case 0 : return extractKmean(_surf, _size, _seed);
case 1 : return extractFrequence(_surf, _size);
case 2 : return extractAll(_surf);
}
return [];
2024-08-19 06:16:12 +02:00
}
2023-02-14 05:32:32 +01:00
2024-08-19 06:16:12 +02:00
static processData = function(_outSurf, _data, _output_index, _array_index) {
2023-12-10 02:48:10 +01:00
var _surf = _data[0];
var _size = _data[1];
var _seed = _data[2];
var _algo = _data[3];
2023-02-14 05:32:32 +01:00
2023-12-10 02:48:10 +01:00
return extractPalette(_surf, _algo, _size, _seed);
2024-08-19 06:16:12 +02:00
}
2023-02-14 05:32:32 +01:00
2024-08-19 06:16:12 +02:00
static onDrawNode = function(xx, yy, _mx, _my, _s, _hover, _focus) {
2023-01-17 08:11:55 +01:00
var bbox = drawGetBbox(xx, yy, _s);
if(bbox.h < 1) return;
2024-08-08 06:57:51 +02:00
var pal = outputs[0].getValue();
2023-12-10 02:48:10 +01:00
if(array_empty(pal)) return;
if(!is_array(pal[0])) pal = [ pal ];
var _h = array_length(pal) * 32;
var _y = bbox.y0;
var gh = bbox.h / array_length(pal);
for( var i = 0, n = array_length(pal); i < n; i++ ) {
drawPalette(pal[i], bbox.x0, _y, bbox.w, gh);
_y += gh;
}
if(_h != min_h) will_setHeight = true;
min_h = _h;
2024-08-19 06:16:12 +02:00
}
2023-01-17 08:11:55 +01:00
}