mirror of
https://fietkau.software/QRSVG.git
synced 2024-10-06 19:41:17 -05:00
Add scanlines shape style
This commit is contained in:
parent
59c0d2d5cd
commit
e7c2764a05
105
qrsvg-v1.0.1.js
105
qrsvg-v1.0.1.js
|
@ -123,7 +123,7 @@ class PRNG {
|
||||||
// Used by `rounded` and `dots` styles.
|
// Used by `rounded` and `dots` styles.
|
||||||
function makePathSpecRound(oldPathSpec) {
|
function makePathSpecRound(oldPathSpec) {
|
||||||
let isInnerContour;
|
let isInnerContour;
|
||||||
let newPathSpec = new Array();
|
let newPathSpec = [];
|
||||||
for(let i = 0; i < oldPathSpec.length; i++) {
|
for(let i = 0; i < oldPathSpec.length; i++) {
|
||||||
if(oldPathSpec[i].startsWith('M')) {
|
if(oldPathSpec[i].startsWith('M')) {
|
||||||
let coords = oldPathSpec[i].substring(1).split(' ').map(c => parseInt(c, 10));
|
let coords = oldPathSpec[i].substring(1).split(' ').map(c => parseInt(c, 10));
|
||||||
|
@ -228,7 +228,7 @@ function compactPathSpec(oldPathSpec) {
|
||||||
for(let step of oldPathSpec.slice(1)) {
|
for(let step of oldPathSpec.slice(1)) {
|
||||||
let prev = newPathSpec[newPathSpec.length - 1];
|
let prev = newPathSpec[newPathSpec.length - 1];
|
||||||
if((step[0] == 'h' || step[0] == 'v') && step[0] == prev[0]) {
|
if((step[0] == 'h' || step[0] == 'v') && step[0] == prev[0]) {
|
||||||
let distance = parseInt(prev.substring(1), 10) + parseInt(step.substring(1), 10);
|
let distance = parseFloat(prev.substring(1), 10) + parseFloat(step.substring(1), 10);
|
||||||
newPathSpec[newPathSpec.length - 1] = step[0] + distance;
|
newPathSpec[newPathSpec.length - 1] = step[0] + distance;
|
||||||
} else if(step[0] == 'm' && (prev[0] == 'm' || prev[0] == 'M')) {
|
} else if(step[0] == 'm' && (prev[0] == 'm' || prev[0] == 'M')) {
|
||||||
let prevPos = prev.substring(1).split(' ').map(c => parseFloat(c));
|
let prevPos = prev.substring(1).split(' ').map(c => parseFloat(c));
|
||||||
|
@ -241,6 +241,93 @@ function compactPathSpec(oldPathSpec) {
|
||||||
return newPathSpec;
|
return newPathSpec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Special case method to calculate contours for the styles
|
||||||
|
// (currently only scanlines) where shapes follow the horizontal
|
||||||
|
// image grid.
|
||||||
|
function calculateHorizontalStyleContour(bitmask, margin, style) {
|
||||||
|
if(style != 'scanlines') {
|
||||||
|
throw Error('Unsupported horizontal render style: ' + style);
|
||||||
|
}
|
||||||
|
let contour = new Contour();
|
||||||
|
let lineOffWidth = 0.06;
|
||||||
|
let lineOnWidth = 0.9;
|
||||||
|
for(let y = 0; y < bitmask.height; y++) {
|
||||||
|
let start = 0;
|
||||||
|
let end = bitmask.width;
|
||||||
|
if(bitmask.width > 16 && bitmask.height > 16) {
|
||||||
|
// Check if we are inside a PDP area, because they have already been handled separately.
|
||||||
|
if(y < 8) {
|
||||||
|
start = 8;
|
||||||
|
end = bitmask.width - 8;
|
||||||
|
} else if(y > bitmask.height - 8) {
|
||||||
|
start = 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We need to slightly move the "off" scanline from the vertical middle to make
|
||||||
|
// the code easier to scan. Vertical displacement:
|
||||||
|
let vOffDisplace = 0.1;
|
||||||
|
let newShapePathSpec = [];
|
||||||
|
let reversePathSpec = [];
|
||||||
|
let newDotPathSpec = [];
|
||||||
|
let capRadius = lineOffWidth / 2;
|
||||||
|
let vDisplace = vOffDisplace;
|
||||||
|
if(bitmask.get(start, y)) {
|
||||||
|
capRadius = lineOnWidth / 2;
|
||||||
|
vDisplace = 0;
|
||||||
|
}
|
||||||
|
newShapePathSpec.push('M' + (start + capRadius + margin).toFixed(3) + ' ' + (y + 0.5 + capRadius + vDisplace + margin).toFixed(3));
|
||||||
|
newShapePathSpec.push('a' + capRadius + ' ' + capRadius + ' 0 0 1 0 ' + (capRadius * -2).toFixed(3));
|
||||||
|
newShapePathSpec.push('h' + (0.8 - capRadius).toFixed(3));
|
||||||
|
reversePathSpec.push('h-0.8');
|
||||||
|
for(let x = start + 1; x < end; x++) {
|
||||||
|
let vDelta = (lineOnWidth - lineOffWidth) / 2;
|
||||||
|
if(bitmask.get(x, y) && !bitmask.get(x - 1, y) && !bitmask.get(x + 1, y) && x < end - 1) {
|
||||||
|
newDotPathSpec.push('M' + (x - 0.2 + margin).toFixed(3) + ' ' + (y + 0.5 - lineOffWidth / 2 + vOffDisplace + margin).toFixed(3));
|
||||||
|
newDotPathSpec.push('c0.2 0 0.2 ' + (vDelta * -1 - vOffDisplace).toFixed(3) + ' 0.4 ' + (vDelta * -1 - vOffDisplace).toFixed(3));
|
||||||
|
newDotPathSpec.push('h0.6');
|
||||||
|
newDotPathSpec.push('c0.2 0 0.2 ' + (vDelta + vOffDisplace).toFixed(3) + ' 0.4 ' + (vDelta + vOffDisplace).toFixed(3));
|
||||||
|
newDotPathSpec.push('v' + lineOffWidth);
|
||||||
|
newDotPathSpec.push('c-0.2 0 -0.2 ' + (vDelta - vOffDisplace).toFixed(3) + ' -0.4 ' + (vDelta - vOffDisplace).toFixed(3));
|
||||||
|
newDotPathSpec.push('h-0.6');
|
||||||
|
newDotPathSpec.push('c-0.2 0 -0.2 ' + (vDelta * -1 + vOffDisplace).toFixed(3) + ' -0.4 ' + (vDelta * -1 + vOffDisplace).toFixed(3));
|
||||||
|
newDotPathSpec.push('z');
|
||||||
|
newShapePathSpec.push('v' + lineOffWidth);
|
||||||
|
newShapePathSpec.push(...reversePathSpec.reverse());
|
||||||
|
reversePathSpec = [];
|
||||||
|
newShapePathSpec.push('z');
|
||||||
|
newShapePathSpec.push('M' + (x + 1.2 + margin).toFixed(3) + ' ' + (y + 0.5 + lineOffWidth / 2 + vOffDisplace + margin).toFixed(3));
|
||||||
|
newShapePathSpec.push('v' + (-1 * lineOffWidth));
|
||||||
|
x += 1;
|
||||||
|
} else if(bitmask.get(x, y) && !bitmask.get(x - 1, y)) {
|
||||||
|
newShapePathSpec.push('c0.2 0 0.2 ' + (vDelta * -1 - vOffDisplace).toFixed(3) + ' 0.4 ' + (vDelta * -1 - vOffDisplace).toFixed(3));
|
||||||
|
reversePathSpec.push('c-0.2 0 -0.2 ' + (vDelta * -1 + vOffDisplace).toFixed(3) + ' -0.4 ' + (vDelta * -1 + vOffDisplace).toFixed(3));
|
||||||
|
} else if(!bitmask.get(x, y) && bitmask.get(x - 1, y)) {
|
||||||
|
newShapePathSpec.push('c0.2 0 0.2 ' + (vDelta + vOffDisplace).toFixed(3) + ' 0.4 ' + (vDelta + vOffDisplace).toFixed(3));
|
||||||
|
reversePathSpec.push('c-0.2 0 -0.2 ' + (vDelta - vOffDisplace).toFixed(3) + ' -0.4 ' + (vDelta - vOffDisplace).toFixed(3));
|
||||||
|
} else {
|
||||||
|
newShapePathSpec.push('h0.4');
|
||||||
|
reversePathSpec.push('h-0.4');
|
||||||
|
}
|
||||||
|
newShapePathSpec.push('h0.6');
|
||||||
|
reversePathSpec.push('h-0.6');
|
||||||
|
}
|
||||||
|
newShapePathSpec.push('h0.2');
|
||||||
|
reversePathSpec.push('h-0.2');
|
||||||
|
capRadius = lineOffWidth / 2;
|
||||||
|
if(bitmask.get(end - 1, y)) {
|
||||||
|
capRadius = lineOnWidth / 2;
|
||||||
|
}
|
||||||
|
newShapePathSpec.push('h' + (-1 * capRadius));
|
||||||
|
reversePathSpec.push('h' + capRadius);
|
||||||
|
newShapePathSpec.push('a' + capRadius + ' ' + capRadius + ' 0 0 1 0 ' + (capRadius * 2));
|
||||||
|
newShapePathSpec.push(...reversePathSpec.reverse());
|
||||||
|
newShapePathSpec.push('z');
|
||||||
|
contour.shapes = contour.shapes.concat(newShapePathSpec);
|
||||||
|
contour.dots = contour.dots.concat(newDotPathSpec);
|
||||||
|
}
|
||||||
|
return contour;
|
||||||
|
}
|
||||||
|
|
||||||
// Special case method to calculate contours for the styles
|
// Special case method to calculate contours for the styles
|
||||||
// where shapes are not contiguous. This also skips the PDP.
|
// where shapes are not contiguous. This also skips the PDP.
|
||||||
function calculateTileStyleContour(bitmask, margin, style) {
|
function calculateTileStyleContour(bitmask, margin, style) {
|
||||||
|
@ -314,7 +401,7 @@ function calculateTileStyleContour(bitmask, margin, style) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(bitmask.get(x, y)) {
|
if(bitmask.get(x, y)) {
|
||||||
let newPathSpec = new Array();
|
let newPathSpec = [];
|
||||||
if(style == 'dots') {
|
if(style == 'dots') {
|
||||||
newPathSpec.push('M' + (x + margin + 0.5) + ' ' + (y + margin));
|
newPathSpec.push('M' + (x + margin + 0.5) + ' ' + (y + margin));
|
||||||
newPathSpec.push('a0.5 0.5 0 0 1 0.5 0.5');
|
newPathSpec.push('a0.5 0.5 0 0 1 0.5 0.5');
|
||||||
|
@ -387,7 +474,7 @@ function calculateTileStyleContour(bitmask, margin, style) {
|
||||||
// the PDP and assumes it is handled separately.
|
// the PDP and assumes it is handled separately.
|
||||||
function calculateShapeContour(bitmask, margin, style) {
|
function calculateShapeContour(bitmask, margin, style) {
|
||||||
let contour = new Contour();
|
let contour = new Contour();
|
||||||
let corners = new Array();
|
let corners = [];
|
||||||
let width = bitmask.width + 1;
|
let width = bitmask.width + 1;
|
||||||
let height = bitmask.height + 1;
|
let height = bitmask.height + 1;
|
||||||
for(let y = 0; y < height; y++) {
|
for(let y = 0; y < height; y++) {
|
||||||
|
@ -466,7 +553,7 @@ function calculateShapeContour(bitmask, margin, style) {
|
||||||
}
|
}
|
||||||
if(Object.keys(corners[y * width + x]).length == 0) continue;
|
if(Object.keys(corners[y * width + x]).length == 0) continue;
|
||||||
let direction = Object.keys(corners[y * width + x])[0];
|
let direction = Object.keys(corners[y * width + x])[0];
|
||||||
let newPathSpec = new Array();
|
let newPathSpec = [];
|
||||||
newPathSpec.push('M' + (x + margin) + ' ' + (y + margin));
|
newPathSpec.push('M' + (x + margin) + ' ' + (y + margin));
|
||||||
let contourX = x;
|
let contourX = x;
|
||||||
let contourY = y;
|
let contourY = y;
|
||||||
|
@ -550,13 +637,15 @@ function calculateContour(bitmask, margin = 1, style = 'basic') {
|
||||||
contour.pdpInner.push(...Array(3).fill('v-1'));
|
contour.pdpInner.push(...Array(3).fill('v-1'));
|
||||||
contour.pdpInner.push('z');
|
contour.pdpInner.push('z');
|
||||||
}
|
}
|
||||||
if(['dots', 'rounded', 'confetti'].includes(style)) {
|
if(['dots', 'rounded', 'confetti', 'scanlines'].includes(style)) {
|
||||||
contour.pdpInner = makePathSpecRound(contour.pdpInner);
|
contour.pdpInner = makePathSpecRound(contour.pdpInner);
|
||||||
contour.pdpOuter = makePathSpecRound(contour.pdpOuter);
|
contour.pdpOuter = makePathSpecRound(contour.pdpOuter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let newContour;
|
let newContour;
|
||||||
if(['dots', 'mosaic', 'confetti'].includes(style)) {
|
if(style == 'scanlines') {
|
||||||
|
newContour = calculateHorizontalStyleContour(bitmask, margin, style);
|
||||||
|
} else if(['dots', 'mosaic', 'confetti'].includes(style)) {
|
||||||
newContour = calculateTileStyleContour(bitmask, margin, style);
|
newContour = calculateTileStyleContour(bitmask, margin, style);
|
||||||
} else {
|
} else {
|
||||||
newContour = calculateShapeContour(bitmask, margin, style);
|
newContour = calculateShapeContour(bitmask, margin, style);
|
||||||
|
@ -601,7 +690,7 @@ function calculateContour(bitmask, margin = 1, style = 'basic') {
|
||||||
// outer parts, dots, and other shapes. See a few lines below for
|
// outer parts, dots, and other shapes. See a few lines below for
|
||||||
// the list of valid styles.
|
// the list of valid styles.
|
||||||
function render(bitmask, renderTarget, style = 'basic') {
|
function render(bitmask, renderTarget, style = 'basic') {
|
||||||
if(!['basic', 'rounded', 'dots', 'mosaic', 'confetti', 'jitter-light', 'jitter-heavy'].includes(style)) {
|
if(!['basic', 'rounded', 'dots', 'mosaic', 'confetti', 'scanlines', 'jitter-light', 'jitter-heavy'].includes(style)) {
|
||||||
throw Error('Unsupported render style: ' + style);
|
throw Error('Unsupported render style: ' + style);
|
||||||
}
|
}
|
||||||
renderTarget.setAttribute('viewBox', '0 0 ' + (bitmask.width + 2) + ' ' + (bitmask.height + 2));
|
renderTarget.setAttribute('viewBox', '0 0 ' + (bitmask.width + 2) + ' ' + (bitmask.height + 2));
|
||||||
|
|
Loading…
Reference in New Issue
Block a user