mirror of
https://github.com/CNCKitchen/stlTexturizer.git
synced 2026-04-07 22:11:32 +00:00
texture smoothing on mac
This commit is contained in:
+79
-4
@@ -59,6 +59,83 @@ const settings = {
|
||||
useDisplacement: false,
|
||||
};
|
||||
|
||||
// ── Canvas filter support (Safari / iOS WebView don't support ctx.filter) ────
|
||||
const CANVAS_FILTER_SUPPORTED = (() => {
|
||||
try {
|
||||
const ctx = document.createElement('canvas').getContext('2d');
|
||||
ctx.filter = 'blur(1px)';
|
||||
return ctx.filter === 'blur(1px)';
|
||||
} catch (e) { return false; }
|
||||
})();
|
||||
|
||||
/**
|
||||
* Box-blur one row of RGBA pixels (horizontal pass).
|
||||
* Operates in-place reading from `src` and writing to `dst`.
|
||||
*/
|
||||
function _boxBlurH(src, dst, w, h, r) {
|
||||
const iarr = 1 / (2 * r + 1);
|
||||
for (let y = 0; y < h; y++) {
|
||||
const row = y * w;
|
||||
for (let ch = 0; ch < 4; ch++) {
|
||||
let val = 0;
|
||||
// Seed with left-edge pixel repeated r+1 times plus the first r pixels
|
||||
for (let x = -r; x <= r; x++) val += src[(row + Math.max(0, Math.min(x, w - 1))) * 4 + ch];
|
||||
for (let x = 0; x < w; x++) {
|
||||
val += src[(row + Math.min(x + r, w - 1)) * 4 + ch]
|
||||
- src[(row + Math.max(x - r - 1, 0)) * 4 + ch];
|
||||
dst[(row + x) * 4 + ch] = Math.round(val * iarr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Box-blur one column of RGBA pixels (vertical pass). */
|
||||
function _boxBlurV(src, dst, w, h, r) {
|
||||
const iarr = 1 / (2 * r + 1);
|
||||
for (let x = 0; x < w; x++) {
|
||||
for (let ch = 0; ch < 4; ch++) {
|
||||
let val = 0;
|
||||
for (let y = -r; y <= r; y++) val += src[(Math.max(0, Math.min(y, h - 1)) * w + x) * 4 + ch];
|
||||
for (let y = 0; y < h; y++) {
|
||||
val += src[(Math.min(y + r, h - 1) * w + x) * 4 + ch]
|
||||
- src[(Math.max(y - r - 1, 0) * w + x) * 4 + ch];
|
||||
dst[(y * w + x) * 4 + ch] = Math.round(val * iarr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an approximate Gaussian blur (sigma px) to `canvas` in-place.
|
||||
* Uses the native CSS filter on Chrome/Firefox; falls back to a 3-pass
|
||||
* separable box blur for Safari / iOS WebKit.
|
||||
*/
|
||||
function blurCanvas(canvas, sigma) {
|
||||
if (sigma <= 0) return;
|
||||
if (CANVAS_FILTER_SUPPORTED) {
|
||||
const tmp = document.createElement('canvas');
|
||||
tmp.width = canvas.width; tmp.height = canvas.height;
|
||||
const tc = tmp.getContext('2d');
|
||||
tc.filter = `blur(${sigma}px)`;
|
||||
tc.drawImage(canvas, 0, 0);
|
||||
canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
|
||||
canvas.getContext('2d').drawImage(tmp, 0, 0);
|
||||
} else {
|
||||
// 3 passes of box blur ≈ Gaussian; radius r where r(r+1) ≈ sigma²
|
||||
const r = Math.max(1, Math.round((Math.sqrt(4 * sigma * sigma + 1) - 1) / 2));
|
||||
const ctx = canvas.getContext('2d');
|
||||
const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
const a = imgData.data;
|
||||
const b = new Uint8ClampedArray(a.length);
|
||||
const w = canvas.width, h = canvas.height;
|
||||
for (let pass = 0; pass < 3; pass++) {
|
||||
_boxBlurH(a, b, w, h, r);
|
||||
_boxBlurV(b, a, w, h, r);
|
||||
}
|
||||
ctx.putImageData(imgData, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Displacement preview state ────────────────────────────────────────────────
|
||||
let dispPreviewGeometry = null; // subdivided geometry with smoothNormal attribute
|
||||
let dispPreviewBusy = false; // true while async subdivision is running
|
||||
@@ -1332,10 +1409,8 @@ function getEffectiveMapEntry() {
|
||||
const blurred = document.createElement('canvas');
|
||||
blurred.width = width * 3;
|
||||
blurred.height = height * 3;
|
||||
const bc = blurred.getContext('2d');
|
||||
bc.filter = `blur(${settings.textureSmoothing}px)`;
|
||||
bc.drawImage(tiled, 0, 0);
|
||||
bc.filter = 'none';
|
||||
blurred.getContext('2d').drawImage(tiled, 0, 0);
|
||||
blurCanvas(blurred, settings.textureSmoothing);
|
||||
const offscreen = document.createElement('canvas');
|
||||
offscreen.width = width;
|
||||
offscreen.height = height;
|
||||
|
||||
@@ -35,24 +35,24 @@ const IMAGE_PRESETS = [
|
||||
{ name: 'Carbon Fiber', url: 'textures/carbonFiber.jpg', defaultScale: 0.5 },
|
||||
{ name: 'Crystal', url: 'textures/crystal.jpg', defaultScale: 0.5 },
|
||||
{ name: 'Dots', url: 'textures/dots.jpg', defaultScale: 0.1 },
|
||||
{ name: 'Grid', url: 'textures/grid.png', defaultScale: 1.0 },
|
||||
{ name: 'Grip Surface', url: 'textures/gripSurface.jpg', defaultScale: 0.5 },
|
||||
{ name: 'Hexagon', url: 'textures/hexagon.jpg', defaultScale: 0.5 },
|
||||
{ name: 'Hexagons', url: 'textures/hexagons.jpg', defaultScale: 1.0 },
|
||||
{ name: 'Isogrid', url: 'textures/isogrid.png', defaultScale: 0.5 },
|
||||
{ name: 'Knitting', url: 'textures/knitting.jpg', defaultScale: 0.25 },
|
||||
{ name: 'Knurling', url: 'textures/knurling.jpg', defaultScale: 0.15 },
|
||||
{ name: 'Leather', url: 'textures/leather.jpg', defaultScale: 0.5 },
|
||||
{ name: 'Leather 2', url: 'textures/leather2.jpg', defaultScale: 0.5 },
|
||||
{ name: 'Noise', url: 'textures/noise.jpg', defaultScale: 0.3 },
|
||||
{ name: 'Stripes', url: 'textures/stripes.png', defaultScale: 0.5 },
|
||||
{ name: 'Stripes 02', url: 'textures/stripes_02.png', defaultScale: 1.0 },
|
||||
{ name: 'Stripes 1', url: 'textures/stripes.png', defaultScale: 0.5 },
|
||||
{ name: 'Stripes 2', url: 'textures/stripes_02.png', defaultScale: 1.0 },
|
||||
{ name: 'Voronoi', url: 'textures/voronoi.jpg', defaultScale: 0.5 },
|
||||
{ name: 'Weave', url: 'textures/weave.jpg', defaultScale: 0.5 },
|
||||
{ name: 'Weave 02', url: 'textures/weave_02.jpg', defaultScale: 0.5 },
|
||||
{ name: 'Weave 03', url: 'textures/weave_03.jpg', defaultScale: 0.5 },
|
||||
{ name: 'Wood', url: 'textures/wood.jpg', defaultScale: 0.5 },
|
||||
{ name: 'Wood Grain 02',url: 'textures/woodgrain_02.jpg', defaultScale: 1.0 },
|
||||
{ name: 'Wood Grain 03',url: 'textures/woodgrain_03.jpg', defaultScale: 1.0 },
|
||||
{ name: 'Weave 1', url: 'textures/weave.jpg', defaultScale: 0.5 },
|
||||
{ name: 'Weave 2', url: 'textures/weave_02.jpg', defaultScale: 0.5 },
|
||||
{ name: 'Weave 3', url: 'textures/weave_03.jpg', defaultScale: 0.5 },
|
||||
{ name: 'Wood 1', url: 'textures/wood.jpg', defaultScale: 0.5 },
|
||||
{ name: 'Wood 2', url: 'textures/woodgrain_02.jpg', defaultScale: 1.0 },
|
||||
{ name: 'Wood 3', url: 'textures/woodgrain_03.jpg', defaultScale: 1.0 },
|
||||
];
|
||||
|
||||
function loadImagePreset(preset) {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 47 KiB |
Reference in New Issue
Block a user