Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(options): add blur option plus documentation #24

Merged
merged 1 commit into from
Feb 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,16 @@ SQIP - SVG-Based Image Placeholder
"SQIP" (pronounced \skwɪb\ like the non-magical folk of magical descent) is a
SVG-based [LQIP](https://www.guypo.com/introducing-lqip-low-quality-image-placeholders/) technique.

**LQIP | SQIP | Original in comparison**
[![LQIP vs. SQIP](demo/lqip-vs-sqip.jpg)](https://raw.githubusercontent.com/technopagan/sqip/master/demo/lqip-vs-sqip.jpg)
## Examples

| Original | LQIP | SQIP default | SQIP complex |
|----------|------|--------------|----------------------|
| <img width="180" src="demo/beach.jpg"> | <img width="180" src="demo/beach-lqip.jpg"> | <img width="180" src="demo/beach-sqip.svg"> | <img width="180" src="./demo/beach-sqip-no-blur-50-all-shapes.svg"> |
| Size: | 354B (gz: 282B) | 895B (gz: 479B)| 3104B (gz: 1241B)- 50 shapes |
| <img width="180" src="./demo/monkey-selfie.jpg"> | <img width="180" src="./demo/monkey-selfie-lqip.jpg"> | <img width="180" src="./demo/monkey-selfie-sqip.svg"> | <img width="180" src="./demo/monkey-selfie-sqip-no-blur-25-all-shapes.svg"> |
| Size: | 435B (gz: 369B) | 980B (gz: 513B)| 2258B (gz: 924B) - 25 shapes |
| <img width="180" src="./demo/mona-lisa.jpg"> | <img width="180" src="./demo/mona-lisa-lqip.jpg"> | <img width="180" src="./demo/mona-lisa-sqip.svg"> | <img width="180" src="./demo/mona-lisa-sqip-3-blur-50-triangles.svg"> |
| Size: | 442B (gz: 372B) | 937B (gz: 487B) | 3273B (gz: 1394B)- 50 triangles |

## Requirements
* Node.js >= v.6 (https://nodejs.org/en/)
Expand Down Expand Up @@ -97,6 +105,9 @@ sqip -n 4 input.jpg
# Specify the type of primitive shapes that will be used to generate the image (default=0)
# 0=combo, 1=triangle, 2=rect, 3=ellipse, 4=circle, 5=rotatedrect, 6=beziers, 7=rotatedellipse, 8=polygon
sqip -m 4 input.jpg

# Set the gaussian blur (default=12)
sqip -b 3 input.jpg
```

### NODE API
Expand All @@ -107,6 +118,7 @@ Input options:
- filename (required)
- numberOfPrimitives (default=8)
- mode (default=0)
- blur (default=12)

Returns:
- final_svg - string
Expand Down
Binary file added demo/beach-lqip.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions demo/beach-sqip-no-blur-50-all-shapes.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file removed demo/lqip-vs-sqip.jpg
Binary file not shown.
Binary file added demo/mona-lisa-lqip.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions demo/mona-lisa-sqip-3-blur-50-triangles.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
File renamed without changes
Binary file added demo/monkey-selfie-lqip.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions demo/monkey-selfie-sqip-no-blur-25-all-shapes.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
40 changes: 29 additions & 11 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ const argvOptions = [{
0=combo, 1=triangle, 2=rect, 3=ellipse, 4=circle, 5=rotatedrect,
6=beziers, 7=rotatedellipse, 8=polygon`,
example: "'sqip --mode=3' or 'sqip -m 3'"
},
{
name: 'blur',
short: 'b',
type: 'int',
description: `GaussianBlur SVG filter value. Disable via 0, defaults to 12`,
example: "'sqip --blur=3' or 'sqip -b 3'"
}
];
const getArguments = () => argv.option(argvOptions).run();
Expand Down Expand Up @@ -140,18 +147,22 @@ const patchSVGGroup = (svg) => {
// Add viewbox and preserveAspectRatio attributes as well as a Gaussian Blur filter to the SVG
// When missing, add group (element with blur applied) using patchSVGGroup()
// We initially worked with a proper DOM parser to manipulate the SVG's XML, but it was very opinionated about SVG syntax and kept introducing unwanted tags. So we had to resort to RegEx replacements
const replaceSVGAttrs = (svg, { width, height }) => {
let blurStdDev = 12;
const replaceSVGAttrs = (svg, { width, height, blur }) => {
let filter = '';
let blurStdDev = blur;
let blurFilterId = 'b';
let newSVG = svg;
if (svg.match(/<svg.*?><path.*?><g/) === null) {
blurStdDev = 55;
newSVG = patchSVGGroup(newSVG);
blurFilterId = 'c';
} else {
newSVG = newSVG.replace(/(<g)/, '<g filter="url(#b)"');
if (blur) {
if (svg.match(/<svg.*?><path.*?><g/) === null) {
blurStdDev = 55;
newSVG = patchSVGGroup(newSVG);
blurFilterId = 'c';
} else {
newSVG = newSVG.replace(/(<g)/, `<g filter="url(#${blurFilterId})"`);
}
filter = `<filter id="${blurFilterId}"><feGaussianBlur stdDeviation="${blurStdDev}" /></filter>`
}
return newSVG.replace(/(<svg)(.*?)(>)/,`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}"><filter id="${blurFilterId}"><feGaussianBlur stdDeviation="${blurStdDev}" /></filter>`);
return newSVG.replace(/(<svg)(.*?)(>)/,`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}">${filter}`);
}

// If the user chooses to save the SVG to a file using the --output parameter, write the file
Expand All @@ -174,11 +185,18 @@ const printFinalResult = ({ width, height }, filename, svg_base64encoded) => {
//#############################################################################

const main = (filename, options) => {
const img_dimensions = getDimensions(filename);
const img_dimensions = getDimensions(filename)
const svgOptions = Object.assign({
blur: options.blur
}, img_dimensions);

// Do not pass blur to primitive
delete options.blur

runPrimitive(filename, options, primitive_output_file, img_dimensions);
const primitive_output = readPrimitiveTempFile(primitive_output_file);
const svgo_output = runSVGO(primitive_output);
const final_svg = replaceSVGAttrs(svgo_output, img_dimensions);
const final_svg = replaceSVGAttrs(svgo_output, svgOptions);
const svg_base64encoded = encodeBase64(final_svg);

return { final_svg, svg_base64encoded, img_dimensions };
Expand Down