const psapi = require('./psapi')
const html_manip = require('./utility/html_manip')
function finalWidthHeight(
selectionWidth,
selectionHeight,
minWidth,
minHeight
) {
// const minWidth = 512
// const minHeight = 512
// const selectionWidth = 256
// const selectionHeight = 1000
let finalWidth = 0
let finalHeight = 0
if (selectionWidth <= selectionHeight) {
//do operation on the smaller dimension
const scaleRatio = selectionWidth / minWidth
finalWidth = minWidth
finalHeight = selectionHeight / scaleRatio
} else {
const scaleRatio = selectionHeight / minHeight
finalHeight = minHeight
finalWidth = selectionWidth / scaleRatio
}
return [finalWidth, finalHeight]
}
async function selectionToFinalWidthHeight() {
// const { getSelectionInfoExe } = require('./psapi')
try {
const selectionInfo = await psapi.getSelectionInfoExe()
const [finalWidth, finalHeight] = finalWidthHeight(
selectionInfo.width,
selectionInfo.height,
512,
512
)
return [
parseInt(finalWidth),
parseInt(finalHeight),
selectionInfo.width,
selectionInfo.height,
]
} catch (e) {
console.warn('you need a rectangular selection', e)
}
}
async function selectBoundingBox() {
let l = await app.activeDocument.activeLayers[0]
let bounds = await l.boundsNoEffects
let selectionInfo = convertSelectionObjectToSelectionInfo(bounds)
await psapi.reSelectMarqueeExe(selectionInfo)
return selectionInfo
}
function convertSelectionObjectToSelectionInfo(selection_obj) {
let selection_info = {
left: selection_obj._left,
right: selection_obj._right,
bottom: selection_obj._bottom,
top: selection_obj._top,
height: selection_obj._bottom - selection_obj._top,
width: selection_obj._right - selection_obj._left,
}
return selection_info
}
const SelectionInfoDesc = () => ({
_obj: 'get',
_target: [
{
_property: 'selection',
},
{
_ref: 'document',
_id: app.activeDocument._id,
},
],
_options: {
dialogOptions: 'dontDisplay',
},
})
async function createChannelIfNotExists(channelName) {
// const photoshop = require('photoshop')
// const app = photoshop.app
// const batchPlay = photoshop.action.batchPlay
// // Check if the channel exists
// let channelExists = false
// for (const channel of app.activeDocument.channels) {
// if (channel.name === channelName) {
// channelExists = true
// break
// }
// }
// // Create the channel if it doesn't exist
// if (!channelExists) {
// await batchPlay(
// [
// {
// _obj: 'make',
// _target: [
// {
// _ref: 'channel',
// },
// ],
// using: {
// _obj: 'channel',
// name: channelName,
// },
// },
// ],
// {}
// )
// }
if (!app.activeDocument.channels.getByName(channelName)) {
}
}
const makeMaskChannel = () => ({
_obj: 'set',
_target: { _ref: 'channel', _property: 'selection' },
to: { _ref: 'channel', _name: 'inpaint_mask' },
})
async function makeMaskChannelExe() {
await executeAsModal(async () => {
const result = await batchPlay(
[
// SelectionInfoDesc(),
// makeMaskChannel(),
{
_obj: 'duplicate',
_target: [
{
_ref: 'channel',
_property: 'selection',
},
],
name: 'inpaint_mask_2',
_isCommand: true,
},
// {
// _obj: 'set',
// _target: { _ref: 'channel', _property: 'selection' },
// to: { _ref: 'channel', _name: 'inpaint_mask' },
// },
{
_obj: 'make',
new: { _class: 'channel' },
at: { _ref: 'channel', _enum: 'channel', _value: 'mask' },
using: {
_enum: 'userMaskEnabled',
_value: 'revealSelection',
},
},
],
{
synchronousExecution: true,
modalBehavior: 'execute',
}
)
console.log('result: ', result)
})
}
async function fillSelectionWhiteOutsideBlack() {
// Create a new layer
const layer_name = 'inpaint_laso_mask'
const getSelectionDesc = () => ({
_obj: 'get',
_target: [
{
_property: 'selection',
},
{
_ref: 'document',
_id: app.activeDocument._id,
},
],
_options: {
dialogOptions: 'dontDisplay',
},
})
const invertSelection = () => ({
_obj: 'inverse',
_isCommand: true,
})
await batchPlay(
[
getSelectionDesc(),
{
_obj: 'fill',
using: {
_enum: 'fillContents',
_value: 'white',
},
opacity: {
_unit: 'percentUnit',
_value: 100,
},
mode: {
_enum: 'blendMode',
_value: 'normal',
},
},
// {
// _obj: 'select',
// _target: [
// {
// _ref: 'channel',
// _property: 'selection',
// },
// ],
// },
invertSelection(),
{
_obj: 'fill',
using: {
_enum: 'fillContents',
_value: 'black',
},
opacity: {
_unit: 'percentUnit',
_value: 100,
},
mode: {
_enum: 'blendMode',
_value: 'normal',
},
},
// {
// _obj: 'invert',
// _target: [
// {
// _ref: 'channel',
// _property: 'selection',
// },
// ],
// },
//make new layer
// {
// _obj: 'make',
// _target: [
// {
// _ref: 'layer',
// },
// ],
// using: {
// _obj: 'layer',
// name: 'Fill Layer',
// },
// },
{
_obj: 'set',
_target: [
{ _ref: 'layer', _enum: 'ordinal', _value: 'targetEnum' },
],
to: { _obj: 'layer', name: layer_name },
_options: { dialogOptions: 'dontDisplay' },
_isCommand: true,
},
// getSelectionDesc(),
//undo the first inversion of the selection
invertSelection(),
],
{ modalBehavior: 'execute' }
)
//get the rectangular bounding box selection
const rect_selection_info = await psapi.getSelectionInfoExe()
await psapi.reSelectMarqueeExe(rect_selection_info)
const width = html_manip.getWidth()
const height = html_manip.getHeight()
//convert the selection area on the canvas to base64 image
const base64 = await io.IO.getSelectionFromCanvasAsBase64Interface_New(
width,
height,
rect_selection_info,
true,
layer_name + '.png'
)
//import the base64 image into the canvas as a new layer
await io.IO.base64ToLayer(
base64,
'base64_to_layer_temp.png',
rect_selection_info.left,
rect_selection_info.top,
rect_selection_info.width,
rect_selection_info.height
)
// Fill the current selection with white
// await batchPlay(
// [
// {
// _obj: 'fill',
// using: {
// _enum: 'fillContents',
// _value: 'white',
// },
// opacity: {
// _unit: 'percentUnit',
// _value: 100,
// },
// mode: {
// _enum: 'blendMode',
// _value: 'normal',
// },
// },
// ],
// { modalBehavior: 'execute' }
// )
// // Invert the selection
// await batchPlay(
// [
// {
// _obj: 'invert',
// _target: [
// {
// _ref: 'channel',
// _property: 'selection',
// },
// ],
// },
// ],
// { modalBehavior: 'execute' }
// )
// // Fill the inverted selection with black
// await batchPlay(
// [
// {
// _obj: 'fill',
// using: {
// _enum: 'fillContents',
// _value: 'black',
// },
// opacity: {
// _unit: 'percentUnit',
// _value: 100,
// },
// mode: {
// _enum: 'blendMode',
// _value: 'normal',
// },
// },
// ],
// { modalBehavior: 'execute' }
// )
}
async function inpaintLasoInitImage() {
// Create a new layer
const layer_name = 'inpaint_laso_init_image'
const getSelectionDesc = () => ({
_obj: 'get',
_target: [
{
_property: 'selection',
},
{
_ref: 'document',
_id: app.activeDocument._id,
},
],
_options: {
dialogOptions: 'dontDisplay',
},
})
const invertSelection = () => ({
_obj: 'inverse',
_isCommand: true,
})
await batchPlay(
[
getSelectionDesc(),
{
_obj: 'fill',
using: {
_enum: 'fillContents',
_value: 'white',
},
opacity: {
_unit: 'percentUnit',
_value: 100,
},
mode: {
_enum: 'blendMode',
_value: 'normal',
},
},
// {
// _obj: 'select',
// _target: [
// {
// _ref: 'channel',
// _property: 'selection',
// },
// ],
// },
invertSelection(),
{
_obj: 'fill',
using: {
_enum: 'fillContents',
_value: 'black',
},
opacity: {
_unit: 'percentUnit',
_value: 100,
},
mode: {
_enum: 'blendMode',
_value: 'normal',
},
},
// {
// _obj: 'invert',
// _target: [
// {
// _ref: 'channel',
// _property: 'selection',
// },
// ],
// },
//make new layer
// {
// _obj: 'make',
// _target: [
// {
// _ref: 'layer',
// },
// ],
// using: {
// _obj: 'layer',
// name: 'Fill Layer',
// },
// },
{
_obj: 'set',
_target: [
{ _ref: 'layer', _enum: 'ordinal', _value: 'targetEnum' },
],
to: { _obj: 'layer', name: layer_name },
_options: { dialogOptions: 'dontDisplay' },
_isCommand: true,
},
// getSelectionDesc(),
//undo the first inversion of the selection
invertSelection(),
],
{ modalBehavior: 'execute' }
)
//get the rectangular bounding box selection
const rect_selection_info = await psapi.getSelectionInfoExe()
await psapi.reSelectMarqueeExe(rect_selection_info)
const width = html_manip.getWidth()
const height = html_manip.getHeight()
//convert the selection area on the canvas to base64 image
const base64 = await io.IO.getSelectionFromCanvasAsBase64Interface_New(
width,
height,
rect_selection_info,
true,
layer_name + '.png'
)
//import the base64 image into the canvas as a new layer
await io.IO.base64ToLayer(
base64,
'base64_to_layer_temp.png',
rect_selection_info.left,
rect_selection_info.top,
rect_selection_info.width,
rect_selection_info.height
)
// Fill the current selection with white
// await batchPlay(
// [
// {
// _obj: 'fill',
// using: {
// _enum: 'fillContents',
// _value: 'white',
// },
// opacity: {
// _unit: 'percentUnit',
// _value: 100,
// },
// mode: {
// _enum: 'blendMode',
// _value: 'normal',
// },
// },
// ],
// { modalBehavior: 'execute' }
// )
// // Invert the selection
// await batchPlay(
// [
// {
// _obj: 'invert',
// _target: [
// {
// _ref: 'channel',
// _property: 'selection',
// },
// ],
// },
// ],
// { modalBehavior: 'execute' }
// )
// // Fill the inverted selection with black
// await batchPlay(
// [
// {
// _obj: 'fill',
// using: {
// _enum: 'fillContents',
// _value: 'black',
// },
// opacity: {
// _unit: 'percentUnit',
// _value: 100,
// },
// mode: {
// _enum: 'blendMode',
// _value: 'normal',
// },
// },
// ],
// { modalBehavior: 'execute' }
// )
}
class Selection {
static async getSelectionInfoExe() {
//return a selectionInfo object or undefined
try {
const selection = await executeAsModal(async () => {
const result = await batchPlay([SelectionInfoDesc()], {
synchronousExecution: true,
modalBehavior: 'execute',
})
return result[0]?.selection
})
if (this.isSelectionValid(selection)) {
let selection_info = {
left: selection.left._value,
right: selection.right._value,
bottom: selection.bottom._value,
top: selection.top._value,
height: selection.bottom._value - selection.top._value,
width: selection.right._value - selection.left._value,
}
// console.dir({selection_info})
return selection_info
}
} catch (e) {
console.warn('selection info error', e)
}
}
static isSelectionValid(selection) {
console.warn(
'isSelectionValid is deprecated use selection.isSelectionValid instead'
)
if (
selection && // check if the selection is defined
selection.hasOwnProperty('left') &&
selection.hasOwnProperty('right') &&
selection.hasOwnProperty('top') &&
selection.hasOwnProperty('bottom')
) {
return true
}
return false
}
static reselectArea(selection_info) {}
static isSameSelection(selection_info_1, selection_info_2) {}
static async getImageToSelectionDifference() {
const selectionInfo = await psapi.getSelectionInfoExe()
const width = html_manip.getWidth()
const height = html_manip.getHeight()
const scale_info_str = `${parseInt(width)}x${parseInt(
height
)} => ${parseInt(selectionInfo.width)}x${parseInt(
selectionInfo.height
)} `
let ratio =
(width * height) / (selectionInfo.width * selectionInfo.height)
// const arrow = percentage >= 1 ? '↑' : '↓'
// percentage = percentage >= 1 ? percentage : 1 / percentage
// const percentage_str = `${arrow}X${percentage.toFixed(2)}`
// console.log('scale_info_str: ', scale_info_str)
// console.log('percentage_str: ', percentage_str)
return ratio
}
static {}
}
module.exports = {
finalWidthHeight,
selectionToFinalWidthHeight,
selectBoundingBox,
convertSelectionObjectToSelectionInfo,
Selection,
makeMaskChannel,
makeMaskChannelExe,
fillSelectionWhiteOutsideBlack,
}