lovell / sharp

High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, AVIF and TIFF images. Uses the libvips library.
https://sharp.pixelplumbing.com
Apache License 2.0
29.29k stars 1.3k forks source link

use last version, i get empty slider #4068

Closed badboy-tian closed 7 months ago

badboy-tian commented 7 months ago

Possible bug

Is this a possible bug in a feature of sharp, unrelated to installation?

If you cannot confirm both of these, please open an installation issue instead.

Are you using the latest version of sharp?

If you cannot confirm this, please upgrade to the latest version and try again before opening an issue.

If you are using another package which depends on a version of sharp that is not the latest, please open an issue against that package instead.

What is the output of running npx envinfo --binaries --system --npmPackages=sharp --npmGlobalPackages=sharp?

System:
    OS: Windows 11 10.0.22631
    CPU: (24) x64 AMD Ryzen 9 5900X 12-Core Processor
    Memory: 31.21 GB / 63.91 GB
  Binaries:
    Node: 18.20.0 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.11 - ~\AppData\Roaming\npm\yarn.CMD
    npm: 10.5.0 - C:\Program Files\nodejs\npm.CMD
  npmPackages:
    sharp: ^0.33.3 => 0.33.3

What are the steps to reproduce?

in sharp 0.25.4 work nice, but in 0.33.3, the slide is empty, no color

What is the expected behaviour?

the slider has color.

Please provide a minimal, standalone code sample, without other dependencies, that demonstrates this problem

create.js

import sharp from 'sharp';
import {randInt, puzzlePieceSvg, backgroundSvg} from './generate';

const sizes = ({
    WIDTH: 310,
    HEIGHT: 155,
    PUZZLE: 60,
    PADDING: 20,
});

const createCaptcha = ({
                           image = Buffer.from(backgroundSvg(sizes.WIDTH, sizes.HEIGHT)),
                           distort = false,
                           rotate = false,
                           fill = '#000',
                           stroke = '#fff',
                           strokeWidth = '.4',
                           opacity = '0.5',
                       } = {}) => {
    const seed = randInt();
    const overlay = Buffer.from(
        puzzlePieceSvg({
            rotate,
            distort,
            fill,
            stroke,
            strokeWidth,
            opacity,
            seed,
        }),
    );
    const mask = Buffer.from(
        puzzlePieceSvg({
            rotate,
            distort,
            seed,
            strokeWidth,
            fill: '#fff',
            stroke: '#fff',
            opacity: '1',
        }),
    );
    const outline = Buffer.from(
        puzzlePieceSvg({
            rotate,
            distort,
            fill: 'none',
            stroke,
            strokeWidth,
            opacity: '1',
            seed,
        }),
    );
    const location = {
        // Solution for slider
        left: randInt(sizes.PUZZLE + sizes.PADDING, sizes.WIDTH - (sizes.PUZZLE + sizes.PADDING)),
        // Vertical offset
        top: randInt(sizes.PADDING, sizes.HEIGHT - (sizes.PUZZLE + sizes.PADDING)),
    };

    return new Promise((resolve) => {
        sharp(image)
            .resize({width: sizes.WIDTH, height: sizes.HEIGHT})
            .composite([
                {
                    input: overlay,
                    blend: 'over',
                    top: location.top,
                    left: location.left,
                },
            ])
            .png()
            .toBuffer()
            .then((background) => {
                sharp(image)
                    .resize({width: sizes.WIDTH, height: sizes.HEIGHT})
                    .composite([
                        {
                            input: mask,
                            blend: 'dest-in',
                            top: location.top,
                            left: location.left,
                        },
                        {
                            input: outline,
                            blend: 'over',
                            top: location.top,
                            left: location.left,
                        },
                    ])
                    .extract({
                        left: location.left,
                        top: 0,
                        width: sizes.PUZZLE,
                        height: sizes.HEIGHT,
                    })
                    .png()
                    .toBuffer()
                    .then((slider) => {
                        resolve({
                            data: {
                                background,
                                slider,
                            },
                            solution: location.left,
                        });
                    });
            });
    });
};

export default createCaptcha;

generate.js

const randInt = (min = 0, max = 2147483646) =>
    Math.floor(Math.random() * (max - min + 1) + min);

const randShade = (hue) => ({
    h: hue,
    s: randInt(50, 70),
    l: randInt(50, 60),
});

const hslString = (color) => `hsl(${color.h}, ${color.s}%, ${color.l}%)`;

const randScheme = (base) => [
    randShade(base),
    randShade((base + 60) % 360),
    randShade((base - 30) % 360),
    randShade((base + 30) % 360),
    randShade((base - 60) % 360),
].map((color) => hslString(color));

const svgRect = (x, y, gridWidth, gridHeight, color) =>
    `<rect filter="url(#noise)" x="${x}" y="${y}" width="${gridWidth}" height="${gridHeight}" fill="${color}"/>`;

const svgGridPattern = (width, height, gridWidth, gridHeight, scheme) =>
    [...Array(Math.floor(height / gridHeight)).keys()].map((y) =>
        [...Array(Math.floor(width / gridWidth)).keys()].map((x) =>
            svgRect(x * gridWidth, y * gridHeight, gridWidth, gridHeight, scheme[x % 2])))
        .flat()
        .join('');

const backgroundSvg = (
    width,
    height,
    {
        gridWidth = randInt(5, 50),
        gridHeight = randInt(5, 50),
        scheme = randScheme(randInt(0, 360)),
        seeds = [randInt(), randInt()],
    } = {},
) => `<svg width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><filter id="noise"><feTurbulence type="turbulence" baseFrequency="0.005" seed="${seeds[0]}" numOctaves="2" result="turbulence"/><feDisplacementMap in2="turbulence" in="SourceGraphic" scale="30" xChannelSelector="R" yChannelSelector="G"/></filter><filter id="heavy"><feTurbulence type="turbulence" baseFrequency="0.005" seed="${seeds[1]}" numOctaves="2" result="turbulence"/><feDisplacementMap in2="turbulence" in="SourceGraphic" scale="100" xChannelSelector="R" yChannelSelector="G"/></filter><rect width="${width}" height="${height}" fill="${scheme[4]}"/><rect filter="url(#heavy)"  width="${width / 2}" height="${height}" x="${width / 5}" fill="${scheme[2]}"/><rect filter="url(#heavy)" width="${width / 2}" height="${height}" x="${width / 2}" fill="${scheme[3]}"/>${svgGridPattern(width, height, gridWidth, gridHeight, scheme)}</svg>`; // eslint-disable-line

const puzzlePieceSvg = ({
                            distort = false,
                            rotate = false,
                            fill = '#000',
                            stroke = '#fff',
                            seed = 0,
                            opacity = '0.5',
                            strokeWidth = '0.25',
                        } = {}) => `<svg viewBox="0 0 20 20" height="60" width="60"><filter id="noise"><feTurbulence type="turbulence" baseFrequency="0.05" seed="${seed}" numOctaves="2" result="turbulence"/><feDisplacementMap in2="turbulence" in="SourceGraphic" scale="2.5" xChannelSelector="R" yChannelSelector="G"/></filter><path ${distort ? 'filter="url(#noise)"' : ''} ${rotate ? `transform="rotate(${seed}, 10, 10)"` : ''} d="M5.56.56a2.305 2.305 0 00-2.296 2.304 2.305 2.305 0 00.801 1.747H.135v4.295a2.305 2.305 0 011.8-.865 2.305 2.305 0 012.304 2.306 2.305 2.305 0 01-2.305 2.304 2.305 2.305 0 01-1.8-.865v4.226H11.26v-4.258a2.305 2.305 0 001.781.842 2.305 2.305 0 002.305-2.305 2.305 2.305 0 00-2.305-2.305 2.305 2.305 0 00-1.78.841V4.611H7.072a2.305 2.305 0 00.801-1.747A2.305 2.305 0 005.57.559a2.305 2.305 0 00-.009 0z" opacity="${opacity}" stroke="${stroke}" fill="${fill}" stroke-width="${strokeWidth}" stroke-linejoin="round"/></svg>`; // eslint-disable-line

export {
    puzzlePieceSvg,
    backgroundSvg,
    randInt,
};

use:

const path = require('path');
        const p = path.join(__dirname, '../utils', 'captcha', 'c4'.jpg');

        const r = await create({image: readFileSync(p)});

        const background = r.data.background;
        const slider = r.data.slider;
        const captchaId = UUID.v1();
        const captchaCode = r.solution;

Please provide sample image(s) that help explain this problem

c4.jpg c4 the right slider is image but in last version, the slider is empty

lovell commented 7 months ago

There's quite a lot going on here, please can you provide a more minimal code sample, perhaps as a standalone repo that allows someone else to reproduce.

badboy-tian commented 7 months ago

@lovell https://github.com/adrsch/slider-captcha please look this.

lovell commented 7 months ago

@badboy-tian It looks like you've provided a link to a third-party repo that was last updated over a year ago. This is not enough to allow someone else to easily reproduce the problem you describe.

There is an existing problem reported against the same downstream repo at https://github.com/adrsch/slider-captcha/issues/12 which you appear to have commented on in 2021.

There's nothing I can do until someone else does the work to isolate that problem. Ideally this will be a static SVG file, without any other logic, that renders in a way that is not expected.

badboy-tian commented 7 months ago

@lovell thank you. it is my fault