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;
}