p5.asciify-accurate-renderer-plugin
Version:
A p5.asciify renderer plugin that converts textures into accurate ASCII representations.
182 lines (181 loc) • 14.8 kB
JavaScript
var m = Object.defineProperty;
var p = (e, r, t) => r in e ? m(e, r, { enumerable: !0, configurable: !0, writable: !0, value: t }) : e[r] = t;
var i = (e, r, t) => p(e, typeof r != "symbol" ? r + "" : r, t);
import { renderers as o } from "p5.asciify";
const u = (e) => `
precision mediump float;uniform sampler2D u_characterTexture;uniform float u_charsetCols,u_charsetRows;uniform sampler2D u_sketchTexture;uniform vec2 u_gridPixelDimensions,u_gridCellDimensions;uniform sampler2D u_charPaletteTexture;uniform vec2 u_charPaletteSize;const float u=float(${e}),f=u*u;void main(){vec2 v=floor(floor(gl_FragCoord.xy).xy),r=u_gridPixelDimensions/u_gridCellDimensions,e=v*r/u_gridPixelDimensions;v=(v+vec2(1))*r/u_gridPixelDimensions-e;bool s=true;float k=1./u;for(int u=0;u<${e};u++){if(!s)break;for(int f=0;f<${e};f++){if(!s)break;vec2 r=vec2(float(f)+.5,float(u)+.5)*k;vec4 i=texture2D(u_sketchTexture,e+r*v);if(i.w>0.)s=false;}}if(s){gl_FragColor=vec4(0);return;}float i=1e20,g=0.,t=u_charPaletteSize.x;for(int u=0;u<1024;u++){if(float(u)>=t)break;vec2 s=vec2((float(u)+.5)/t,.5/u_charPaletteSize.y);vec4 r=texture2D(u_charPaletteTexture,s);float m=r.x*255.+r.y*255.*256.+r.z*255.*65536.,y=floor(m/u_charsetCols);s=vec2((m-u_charsetCols*y)/u_charsetCols,y/u_charsetRows);vec2 C=vec2(1./u_charsetCols,1./u_charsetRows);y=0.;for(int u=0;u<${e};u++)for(int f=0;f<${e};f++){vec2 r=vec2(float(f)+.5,float(u)+.5)*k;float m=texture2D(u_sketchTexture,e+r*v).x-texture2D(u_characterTexture,s+r*C).x;y+=m*m;}y/=f;if(y<i)i=y,g=m;}i=mod(g,256.);g=floor(g/256.);gl_FragColor=vec4(i/255.,g/255.,0,1);}
`, f = (e, r) => `
precision mediump float;uniform sampler2D u_inputImage;uniform vec2 u_inputImageSize;uniform int u_gridCols,u_gridRows;const int u=${e},f=${r};void main(){vec2 v=floor(gl_FragCoord.xy),e=u_inputImageSize/vec2(float(u_gridCols),float(u_gridRows));v*=e;float i=0.,t=float(u*f);for(int s=0;s<u;s++)for(int g=0;g<f;g++){vec2 m=clamp((v+(vec2(float(s),float(g))+.5)*(e/vec2(float(u),float(f))))/u_inputImageSize,0.,1.);vec4 d=texture2D(u_inputImage,m);float t=.299*d.x+.587*d.y+.114*d.z;i+=t;}i/=t;gl_FragColor=vec4(vec3(i),1);}
`, c = (e, r, t) => `
precision mediump float;uniform sampler2D u_inputImage,u_inputImageBW;uniform vec2 u_inputImageSize;uniform int u_gridCols,u_gridRows,u_colorRank;const int e=${e},u=${r},f=${t};void main(){vec2 i=floor(gl_FragCoord.xy),t=u_inputImageSize/vec2(float(u_gridCols),float(u_gridRows));i*=t;vec2 k=(i+t*.5)/u_inputImageSize;vec4 v=texture2D(u_inputImage,k),c[e];float b[e];for(int i=0;i<e;i++)c[i]=vec4(0),b[i]=0.;for(int v=0;v<u;v++)for(int k=0;k<f;k++){vec2 s=clamp((i+(vec2(float(v),float(k))+.5)*(t/vec2(float(u),float(f))))/u_inputImageSize,0.,1.);vec4 m=texture2D(u_inputImage,s),d=texture2D(u_inputImageBW,s);float r=step(.5,d.x);bool z=false;if(u_colorRank==1&&r>.5)z=true;else if(u_colorRank==2&&r<=.5)z=true;if(!z)continue;z=false;for(int i=0;i<e;i++)if(m.xyz==c[i].xyz){b[i]+=1.;z=true;break;}if(!z)for(int i=0;i<e;i++)if(b[i]==0.){c[i]=m;b[i]=1.;break;}}float z=0.;vec4 m=vec4(0);for(int i=0;i<e;i++){float u=b[i];vec4 k=c[i];if(u>z)z=u,m=k;}if(u_colorRank==2&&z==0.)m=v;gl_FragColor=vec4(m.xyz,1);}
`, S = `precision mediump float;\r
\r
// Uniforms\r
uniform sampler2D u_inputImage; // Original input image\r
uniform sampler2D u_brightnessTexture; // Average brightness texture\r
uniform vec2 u_inputImageSize; // Size of the input image (e.g., 800.0, 800.0)\r
uniform int u_gridCols; // Number of grid columns (e.g., 100)\r
uniform int u_gridRows; // Number of grid rows (e.g., 100)\r
uniform float u_pixelRatio; // Device pixel ratio\r
\r
// Constants\r
const float EPSILON = 0.01; // Epsilon threshold for floating-point comparison\r
\r
void main() {\r
// Adjust fragment coordinates based on pixel ratio to get logical pixel position\r
vec2 logicalFragCoord = floor(gl_FragCoord.xy / u_pixelRatio);\r
\r
// Calculate the size of each grid cell in logical pixels\r
float cellWidth = u_inputImageSize.x / float(u_gridCols);\r
float cellHeight = u_inputImageSize.y / float(u_gridRows);\r
\r
// Normalize fragment coordinates to [0, 1] for sampling the original image\r
vec2 imageTexCoord = logicalFragCoord / u_inputImageSize;\r
\r
// Sample the original image color at the current fragment\r
vec4 originalColor = texture2D(u_inputImage, imageTexCoord);\r
\r
// Early return if pixel is transparent\r
if (originalColor.a < EPSILON) {\r
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);\r
return;\r
}\r
\r
// Rest of the brightness processing for non-transparent pixels\r
float gridX = floor(logicalFragCoord.x / cellWidth);\r
float gridY = floor(logicalFragCoord.y / cellHeight);\r
\r
gridX = clamp(gridX, 0.0, float(u_gridCols - 1));\r
gridY = clamp(gridY, 0.0, float(u_gridRows - 1));\r
\r
vec2 brightnessTexCoord = (vec2(gridX, gridY) + 0.5) / vec2(float(u_gridCols), float(u_gridRows));\r
float averageBrightness = texture2D(u_brightnessTexture, brightnessTexCoord).r;\r
\r
float fragmentBrightness = 0.299 * originalColor.r + 0.587 * originalColor.g + 0.114 * originalColor.b;\r
float brightnessDifference = fragmentBrightness - averageBrightness;\r
\r
float finalColorValue = brightnessDifference < -EPSILON ? 0.0 : 1.0;\r
gl_FragColor = vec4(vec3(finalColorValue), 1.0);\r
}\r
`, a = `precision mediump float;\r
\r
attribute vec3 aPosition;\r
attribute vec2 aTexCoord;\r
\r
varying vec2 v_texCoord;\r
\r
void main() {\r
vec4 positionVec4 = vec4(aPosition, 1.0);\r
\r
positionVec4.xy = positionVec4.xy * 2.0 - 1.0;\r
\r
gl_Position = positionVec4;\r
\r
v_texCoord = aTexCoord;\r
}`, l = {
/** Enable/disable the renderer */
enabled: !0,
/** Characters used for pattern matching */
characters: " .:-=+*%@#",
/** Color of the ASCII characters. Only used when `characterColorMode` is set to `fixed` */
characterColor: "#FFFFFF",
/** Character color mode */
characterColorMode: "sampled",
/** Cell background color. Only used when `characterColorMode` is set to `fixed` */
backgroundColor: "#000000",
/** Background color mode */
backgroundColorMode: "sampled",
/** Swap the cells ASCII character colors with it's cell background colors */
invertMode: !1,
/** Rotation angle of all characters in the grid in degrees */
rotationAngle: 0,
/** Flip the ASCII characters horizontally */
flipHorizontally: !1,
/** Flip the ASCII characters vertically */
flipVertically: !1
}, b = () => {
if (typeof o < "u" && o.renderer2d && o.renderer2d.feature && typeof o.renderer2d.feature.P5AsciifyAbstractFeatureRenderer2D < "u")
return o.renderer2d.feature.P5AsciifyAbstractFeatureRenderer2D;
if (typeof window < "u" && window.P5AsciifyAbstractFeatureRenderer2D && typeof window.P5AsciifyAbstractFeatureRenderer2D == "function")
return window.P5AsciifyAbstractFeatureRenderer2D;
const e = typeof window < "u" ? Object.keys(window).filter(
(r) => r.toLowerCase().includes("asciify") || r.toLowerCase().includes("p5")
) : [];
throw new Error(
"`P5AsciifyAbstractFeatureRenderer2D` not found. Please ensure p5.asciify is loaded before this plugin. " + (e.length > 0 ? `Found related globals: ${e.join(", ")}` : "No related globals found.")
);
};
let h = null;
const v = () => (h === null && (h = b()), h);
class C extends v() {
/**
* Creates a new `"accurate"` ASCII renderer instance.
* @param p5Instance The p5 instance.
* @param captureFramebuffer The framebuffer to apply the ASCII effect to.
* @param grid Grid object containing the relevant grid information.
* @param fontManager The font texture atlas containing the ASCII characters texture.
* @param options The options for the ASCII renderer.
* @ignore
*/
constructor(t, s, n, _, g = l) {
const d = { ...l, ...g };
super(t, s, n, _, d);
i(this, "_characterSelectionShader");
i(this, "_brightnessSampleShader");
i(this, "_colorSampleShader");
i(this, "_brightnessSplitShader");
i(this, "_brightnessSampleFramebuffer");
i(this, "_brightnessSplitFramebuffer");
this._characterSelectionShader = this._p.createShader(a, u(this._fontManager.fontSize)), this._brightnessSampleShader = this._p.createShader(a, f(this._grid.cellHeight, this._grid.cellWidth)), this._colorSampleShader = this._p.createShader(a, c(16, this._grid.cellHeight, this._grid.cellWidth)), this._brightnessSplitShader = this._p.createShader(a, S), this._brightnessSampleFramebuffer = this._p.createFramebuffer({
density: 1,
width: this._grid.cols,
height: this._grid.rows,
depthFormat: this._p.UNSIGNED_INT,
textureFiltering: this._p.NEAREST
}), this._brightnessSplitFramebuffer = this._p.createFramebuffer({
density: 1,
width: this._captureFramebuffer.width,
height: this._captureFramebuffer.height,
depthFormat: this._p.UNSIGNED_INT,
textureFiltering: this._p.NEAREST
});
}
resizeFramebuffers() {
super.resizeFramebuffers(), this._brightnessSampleFramebuffer.resize(this._grid.cols, this._grid.rows), this._brightnessSplitFramebuffer.resize(this._captureFramebuffer.width, this._captureFramebuffer.height);
}
resetShaders() {
this._characterSelectionShader = this._p.createShader(a, u(this._fontManager.fontSize)), this._brightnessSampleShader = this._p.createShader(a, f(this._grid.cellHeight, this._grid.cellWidth)), this._colorSampleShader = this._p.createShader(a, c(16, this._grid.cellHeight, this._grid.cellWidth));
}
render() {
this._brightnessSampleFramebuffer.begin(), this._p.clear(), this._p.shader(this._brightnessSampleShader), this._brightnessSampleShader.setUniform("u_inputImage", this._captureFramebuffer), this._brightnessSampleShader.setUniform("u_inputImageSize", [this._captureFramebuffer.width, this._captureFramebuffer.height]), this._brightnessSampleShader.setUniform("u_gridCols", this._grid.cols), this._brightnessSampleShader.setUniform("u_gridRows", this._grid.rows), this._p.rect(0, 0, this._brightnessSampleFramebuffer.width, this._brightnessSampleFramebuffer.height), this._brightnessSampleFramebuffer.end(), this._brightnessSplitFramebuffer.begin(), this._p.clear(), this._p.shader(this._brightnessSplitShader), this._brightnessSplitShader.setUniform("u_inputImage", this._captureFramebuffer), this._brightnessSplitShader.setUniform("u_brightnessTexture", this._brightnessSampleFramebuffer), this._brightnessSplitShader.setUniform("u_inputImageSize", [this._captureFramebuffer.width, this._captureFramebuffer.height]), this._brightnessSplitShader.setUniform("u_gridCols", this._grid.cols), this._brightnessSplitShader.setUniform("u_gridRows", this._grid.rows), this._brightnessSplitShader.setUniform("u_pixelRatio", this._p.pixelDensity()), this._p.rect(0, 0, this._brightnessSplitFramebuffer.width, this._brightnessSplitFramebuffer.height), this._brightnessSplitFramebuffer.end(), this._primaryColorFramebuffer.begin(), this._options.characterColorMode === 1 ? this._p.background(this._options.characterColor) : (this._p.clear(), this._p.shader(this._colorSampleShader), this._colorSampleShader.setUniform("u_inputImage", this._captureFramebuffer), this._colorSampleShader.setUniform("u_inputImageBW", this._brightnessSplitFramebuffer), this._colorSampleShader.setUniform("u_inputImageSize", [this._captureFramebuffer.width, this._captureFramebuffer.height]), this._colorSampleShader.setUniform("u_gridCols", this._grid.cols), this._colorSampleShader.setUniform("u_gridRows", this._grid.rows), this._colorSampleShader.setUniform("u_colorRank", 1), this._p.rect(0, 0, this._primaryColorFramebuffer.width, this._primaryColorFramebuffer.height)), this._primaryColorFramebuffer.end(), this._secondaryColorFramebuffer.begin(), this._options.backgroundColorMode === 1 ? this._p.background(this._options.backgroundColor) : (this._p.clear(), this._p.shader(this._colorSampleShader), this._colorSampleShader.setUniform("u_inputImage", this._captureFramebuffer), this._colorSampleShader.setUniform("u_inputImageBW", this._brightnessSplitFramebuffer), this._colorSampleShader.setUniform("u_inputImageSize", [this._captureFramebuffer.width, this._captureFramebuffer.height]), this._colorSampleShader.setUniform("u_gridCols", this._grid.cols), this._colorSampleShader.setUniform("u_gridRows", this._grid.rows), this._colorSampleShader.setUniform("u_colorRank", 2), this._p.rect(0, 0, this._secondaryColorFramebuffer.width, this._secondaryColorFramebuffer.height)), this._secondaryColorFramebuffer.end(), this._transformFramebuffer.begin(), this._p.background(this._options.invertMode ? 255 : 0, this._options.flipHorizontally ? 255 : 0, this._options.flipVertically ? 255 : 0), this._transformFramebuffer.end(), this._rotationFramebuffer.begin(), this._p.background(this._options.rotationAngle), this._rotationFramebuffer.end(), this._characterFramebuffer.begin(), this._p.clear(), this._p.shader(this._characterSelectionShader), this._characterSelectionShader.setUniform("u_characterTexture", this._fontManager.texture), this._characterSelectionShader.setUniform("u_charsetCols", this._fontManager.textureColumns), this._characterSelectionShader.setUniform("u_charsetRows", this._fontManager.textureRows), this._characterSelectionShader.setUniform("u_charPaletteTexture", this._characterColorPalette.framebuffer), this._characterSelectionShader.setUniform("u_charPaletteSize", [this._characterColorPalette.colors.length, 1]), this._characterSelectionShader.setUniform("u_sketchTexture", this._brightnessSplitFramebuffer), this._characterSelectionShader.setUniform("u_gridCellDimensions", [this._grid.cols, this._grid.rows]), this._characterSelectionShader.setUniform("u_gridPixelDimensions", [this._grid.width, this._grid.height]), this._p.rect(0, 0, this._characterFramebuffer.width, this._characterFramebuffer.height), this._characterFramebuffer.end();
}
}
const F = {
id: "accurate",
name: "Accurate ASCII Renderer",
description: "An ASCII renderer that attempts picking the most fitting ASCII representation to accurately represent the input sketch using the available ASCII characters.",
version: "1.0.0",
author: "humanbydefinition",
/**
* Creates a new instance of the accurate ASCII renderer.
* @param p The p5 instance.
* @param captureFramebuffer The framebuffer to apply the ASCII effect to.
* @param grid The grid to use.
* @param fontManager The font manager to use.
* @param options The options to use.
* @returns The new instance of the accurate ASCII renderer.
*/
create(e, r, t, s, n) {
return new C(
e,
r,
t,
s,
n || l
);
}
};
typeof window < "u" && (window.AccurateRendererPlugin = F);
export {
F as AccurateRendererPlugin
};