Closed AvailableForTheWorld closed 6 months ago
I think you have to reset globalAlpha to 1
before drawing the mask, and you're also centering the mask on the layer canvas, instead you should use difference between mask.left and mask.top and layer.left and layer.top to correctly position the mask.
Even better way to do it is to draw the layer.canvas with globalAlpha = 1 but then draw compoundCanvas with globalAlpha = layer.alpha. That way masking will have correct calculation, as the mask is applied before the layer.opacity.
Thank you for answering me. I try to fix it with the code:
function drawLayer(layer, context) {
if (layer.children && !layer.hidden) {
layer.children.forEach(child => drawLayer(child, context))
}
const compoundCanvas = document.createElement('canvas')
const compoundCtx = compoundCanvas.getContext('2d')
if (layer.canvas && !layer.hidden) {
compoundCanvas.width = layer.right - layer.left
compoundCanvas.height = layer.bottom - layer.top
compoundCtx.globalAlpha = layer.opacity
compoundCtx.drawImage(layer.canvas, 0, 0)
if (layer.mask && !layer.hidden) {
// const offsetX = (layer.canvas.width - layer.mask.canvas.width) / 2
// const offsetY = (layer.canvas.height - layer.mask.canvas.height) / 2
const maskX = layer.mask.left - layer.left
const maskY = layer.mask.top - layer.top
compoundCtx.globalCompositeOperation = 'destination-in'
compoundCtx.drawImage(layer.mask.canvas, maskX, maskY)
}
// compoundCtx.globalCompositeOperation = 'source-over'
compoundCtx.globalAlpha = 1
context.drawImage(compoundCanvas, layer.left, layer.top)
}
}
but the position of the layer.mask is still not correct and for your advice I do assign the compoundCanvas with globalAlpha (not the context but the compoundCtx instead) but I suppose it works fine and does not impact the mask position. I am so confused of the mask position and don't know how to deal with the effects of psd layer, how can I access the knowledge of them? I'm really glad to receive your message, thanks.
I have fix it by transforming the mask to alpha channel:
function drawLayer(layer, context) {
if (layer.children && !layer.hidden) {
layer.children.forEach(child => drawLayer(child, context))
}
const compoundCanvas = document.createElement('canvas')
const compoundCtx = compoundCanvas.getContext('2d')
if (layer.canvas && !layer.hidden) {
compoundCanvas.width = layer.right - layer.left
compoundCanvas.height = layer.bottom - layer.top
compoundCtx.globalAlpha = layer.opacity
compoundCtx.drawImage(layer.canvas, 0, 0)
if (layer.mask && !layer.hidden) {
// debugger
console.log('layer mask: ', layer)
// const maskX = layer.mask.left - layer.left
// const maskY = layer.mask.top - layer.top
// compoundCtx.globalCompositeOperation = 'destination-in'
// compoundCtx.drawImage(layer.mask.canvas, maskX, maskY)
// Prepare mask
const maskCanvas = document.createElement('canvas')
const maskCtx = maskCanvas.getContext('2d')
maskCanvas.width = layer.mask.right - layer.mask.left
maskCanvas.height = layer.mask.bottom - layer.mask.top
// Draw mask
maskCtx.drawImage(layer.mask.canvas, 0, 0)
// Convert grayscale to alpha
const maskImageData = maskCtx.getImageData(0, 0, maskCanvas.width, maskCanvas.height)
const data = maskImageData.data
for (let i = 0; i < data.length; i += 4) {
// Assuming the mask is grayscale, the R, G, and B values should be approximately equal.
// The alpha value of each pixel is set based on the average of the RGB values.
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3
data[i + 3] = avg // Set alpha channel to the average of R, G, and B
}
maskCtx.putImageData(maskImageData, 0, 0)
// Apply mask
const maskX = layer.mask.left - layer.left
const maskY = layer.mask.top - layer.top
compoundCtx.globalCompositeOperation = 'destination-in'
compoundCtx.drawImage(maskCanvas, maskX, maskY)
// document.body.appendChild(layer.mask.canvas)
// document.body.appendChild(compoundCanvas)
// document.body.appendChild(layer.canvas)
// document.body.appendChild(maskCanvas)
}
compoundCtx.globalCompositeOperation = 'source-over'
compoundCtx.globalAlpha = 1
context.drawImage(compoundCanvas, layer.left, layer.top)
}
}
but I want to know: how to handle the positionRelativeToLayer attribute in layer.mask. When positionRelativeToLayer is true, I try to reckon it as relative position: left/right/bottom/top but failed to render, I saw no specific info in doc. And I still want to figure out how to handle effects in a layer (except fill color effects). I found that there lack of info to render the dropShadow innerGlow and so on. Could you please tell me?
Oh you're right, I forgot that mask is by default a grayscale representation instead of alpha channel.
I'm not sure how mask positioning is calculated, I think it should be the same as layer, but maybe positionRelativeToLayer
flag has impact on it, you'd have to experiment with it, I think for me it worked when I just used it the same as layer top/left.
Additionally you'll have to take into account how destination-in
works, because you want to remove the parts of the image with low alpha channel value on mask image. But the parts that you don't draw the mask on will not get clipped, because the composite operation will not get invoked for those pixels. So you need to manually cut off those parts of the layer bitmap. Alternatively draw the mask to a temporary canvas that's the same size as layer so all pixels will be covered by mask.
All layer affects info is stored in layer.effects
property. Unfortunately if you want to replicate them you'd have to research how photoshop is generating them and replicate that code yourself, the PSD file itself doesn't have any more information that what is in layer.effects
.
Thanks a lot for replying me. I asked UI designer for the range of mask, she said the mask contain all the section of the psd file so that I do not need to consider the outside mask section.
here is my code using vue3:
here is the psd file: psd.zip
and here is the result I render:
in this case the problem is that: some layer has mask, I cannot render the mask well for it, here is my render-mask-fix:
the code for mask rendering (the reflection of objects on the table ) (locate in the red circle of the last picture) cannot work, instead the compoundCtx.opacity works well, and the result generated image :
and I found the wordart on the top of the picture (locate in the blue circle of the last picture) don't show the color due to the effects, I don't know how to render the effects independently, could you please tell me? Thanks a lot.