ag-psd icon indicating copy to clipboard operation
ag-psd copied to clipboard

how to get real vector mask shape

Open houhuiyao1 opened this issue 2 years ago • 9 comments

The vector mask is a white background image And I can get vector mask path , but the path seems incomplete,I can't know the line is curve image What can I do to get the real vector mask shape to compose and clip two canvas,

houhuiyao1 avatar Jul 28 '22 04:07 houhuiyao1

Each of the knots represents point with 2 control handles like this

image

So it's x & y of 1st control point, then x & y of the actual point and then x & y 2nd control point

The basic idea is that photoshop has: control-point -> point -> control-point and in HTML canvas you have control-point -> control-point -> point You can use it like this context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

Agamnentzar avatar Jul 28 '22 04:07 Agamnentzar

Each of the knots represents point with 2 control handles like this

image

So it's x & y of 1st control point, then x & y of the actual point and then x & y 2nd control point

The basic idea is that photoshop has: control-point -> point -> control-point and in HTML canvas you have control-point -> control-point -> point You can use it like this context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

yeah!thank you!it seems work.but I don't know if it's my reason. Aftrer parsing the curve is different. This is the original: image And this is the result image Did I do something wrong...

houhuiyao1 avatar Jul 28 '22 07:07 houhuiyao1

paste your code and I'll check it

Agamnentzar avatar Jul 28 '22 13:07 Agamnentzar

paste your code and I'll check it

that's my code and thank you:

const {canvas} = layer
  const newCanvas = document.createElement('canvas')
  const ctx = newCanvas.getContext('2d')
  if(layer.mask && layer.vectorMask){
    newCanvas.width = canvas!.width
    newCanvas.height = canvas!.height
    const knots = layer.vectorMask.paths[0]?.knots.map(it=>{
      const newPoints = it.points.map((point,index)=>{
        if(index%2 === 0){
          return point - layer.left!
        }else{
          return point - layer.top!
        }
      })
      return {...it, points : newPoints}
    })
    ctx?.beginPath()
    for(let i = 0;i<knots.length;i++){
        const cp1x = Math.floor(knots[i].points[0])
        const cp1y = Math.floor(knots[i].points[1])
        const cp2x = Math.floor(knots[i].points[4])
        const cp2y = Math.floor(knots[i].points[5])
        const x = Math.floor(knots[i].points[2])
        const y = Math.floor(knots[i].points[3])
        ctx?.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
    }
    ctx?.closePath()
    ctx?.clip()
    ctx?.drawImage(canvas!, 0, 0)
    return newCanvas
  }

houhuiyao1 avatar Jul 29 '22 03:07 houhuiyao1

Ok, so a little bit more details: in photoshop knots relate to points, but in HTML canvas it's curves with control points. to draw a curve you need one control point from previous knot, like this:

        const cp1x = Math.floor(knots[i - 1].points[4])
        const cp1y = Math.floor(knots[i - 1].points[5])
        const cp2x = Math.floor(knots[i].points[0])
        const cp2y = Math.floor(knots[i].points[1])
        const x = Math.floor(knots[i].points[2])
        const y = Math.floor(knots[i].points[3])
        ctx?.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

So you'll have to start from 2nd knot, not the first one, and then ad the end wrap it back to the first knot

Agamnentzar avatar Jul 29 '22 03:07 Agamnentzar

You probably also shouldn't floor the coordinates, that will distort the shape.

Agamnentzar avatar Jul 29 '22 03:07 Agamnentzar

Ok, so a little bit more details: in photoshop knots relate to points, but in HTML canvas it's curves with control points. to draw a curve you need one control point from previous knot, like this:

        const cp1x = Math.floor(knots[i - 1].points[4])
        const cp1y = Math.floor(knots[i - 1].points[5])
        const cp2x = Math.floor(knots[i].points[0])
        const cp2y = Math.floor(knots[i].points[1])
        const x = Math.floor(knots[i].points[2])
        const y = Math.floor(knots[i].points[3])
        ctx?.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

So you'll have to start from 2nd knot, not the first one, and then ad the end wrap it back to the first knot

thank you so much ,your tips are very useful ! I‘m very new to PS, very sorry and I have a little question here. I found a very strange problem,It seems that the last curve is a little wrong image and here is my code:

    for(let i = 0;i<knots.length;i++){
        const preIndex = i === 0?knots.length - 1:i - 1
        const cp1x = knots[preIndex].points[4]
        const cp1y = knots[preIndex].points[5]
        const cp2x = knots[i].points[0]
        const cp2y = knots[i].points[1]
        const x = knots[i].points[2]
        const y = knots[i].points[3]
        ctx?.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
    }

houhuiyao1 avatar Jul 29 '22 07:07 houhuiyao1

You need to set the starting point correctly before starting the loop:

ctx?.moveTo(knots[knots.length - 1].points[2], knots[knots.length - 1].points[3])

Agamnentzar avatar Jul 29 '22 14:07 Agamnentzar

You need to set the starting point correctly before starting the loop:

ctx?.moveTo(knots[knots.length - 1].points[2], knots[knots.length - 1].points[3])

it works! thank you very much

houhuiyao1 avatar Jul 31 '22 03:07 houhuiyao1