Spatio Chromathic Texture Synthesizer
This implementation of variation of AM-halftoning started as an experiment, trying to create a filter that did engraving style lines depending on the source hue/saturation for angle and frequency, the core structure of the filter thus developed was suited also for the core of a glsl shader.
When used with the GEGL tool in GIMP-2.9, it can often be advantagous to first apply a gaussian blur to the input image, this produces nicer smoother isoline/countour line like patterns.
The following code is the core of spachromtyzer, both for the GEGL operation for use with GIMP-2.9 and beyond, and the glsl webgl shader. The code is written for compatibility both with C and webgl's variation of glsl. It takes the coordinates and input colors of one pixel and returns the grayscale level of a corresponding halftoning mask for that pixel, multiple masks are combined to create the composite RGB/CMYK results.
/* * Copyright (c) 2017 Øyvind Kolås <pippin@gimp.org> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ float spachrotyze ( float x, float y, float part_white, float offset, float hue, int pattern, float period, float turbulence, float blocksize, float angleboost, float twist) { const float aa = 2.0; /* this gives 2x2 MSAA - increase value for better antialiasing*/ float acc = 0.0; float angle = (1.0-(hue * angleboost) + twist); float width = (period * (1.0 - turbulence) + (period * offset) * turbulence); float vec0 = cosf (-angle * 3.14151 / 2.0); float vec1 = sinf (-angle * 3.14151 / 2.0); float aa_sq = aa * aa; for (float xi = 0.0; xi < aa; xi += 1.0) { float u = fmodf (x + xi/aa + 0.5 * width, blocksize * width); for (float yi = 0.0; yi < aa; yi += 1.0) { float v = fmodf (y + yi/aa + 0.5 * width, blocksize * width); float w = vec0 * u + vec1 * v; float q = vec1 * u - vec0 * v; float wperiod = fmodf (w, width); float wphase = (wperiod / width) * 2.0 - 1.0; float qperiod = fmodf (q, width); float qphase = (qperiod / width) * 2.0 - 1.0; if (pattern == 0) { /* line */ if (fabsf (wphase) < part_white) acc += 1.0 / aa_sq; } else if (pattern == 1) { /* dot */ if (qphase * qphase + wphase * wphase < part_white * part_white * 2.0) acc += 1.0 / aa_sq; } else if (pattern == 2) { /* diamond */ if (fabsf(wphase) + fabsf(qphase) < (part_white*2.0) ) acc += 1.0 / aa_sq; } else if (pattern == 3) { /* dot-to-diamond-to-dot */ float ax = fabsf (wphase ) ; float ay = fabsf (qphase ) ; float v = 0.0; if (ax + ay > 1.0) { v = 2.0 - sqrtf((1.0-ay) * (1.0-ay) + (1.0-ax) * (1.0-ax)); } else { v = sqrtf (ay * ay + ax * ax); } v/=2.0; if (v < part_white) acc = acc + 1.0 / aa_sq; } } } return acc; }