import { fromByteArray } from 'base64-js' ; import { Layer , BlendMode , LayerColor } from './psd' ; export const MOCK_HANDLERS = false ; export const RAW_IMAGE_DATA = false ; export const fromBlendMode = { } ; export const toBlendMode = { 'pass' : 'pass through' , 'norm' : 'normal' , 'diss' : 'dissolve' , 'dark' : 'darken' , 'mul ' : 'multiply' , 'idiv' : 'color burn' , 'lbrn' : 'linear burn' , 'dkCl' : 'darker color' , 'lite' : 'lighten' , 'scrn' : 'screen' , 'div ' : 'color dodge' , 'lddg' : 'linear dodge' , 'lgCl' : 'lighter color' , 'over' : 'overlay' , 'sLit' : 'soft light' , 'hLit' : 'hard light' , 'vLit' : 'vivid light' , 'lLit' : 'linear light' , 'pLit' : 'pin light' , 'hMix' : 'hard mix' , 'diff' : 'difference' , 'smud' : 'exclusion' , 'fsub' : 'subtract' , 'fdiv' : 'divide' , 'hue ' : 'hue' , 'sat ' : 'saturation' , 'colr' : 'color' , 'lum ' : 'luminosity' , } ; Object . keys ( toBlendMode ) . forEach ( key => fromBlendMode [ toBlendMode [ key ] ] = key ) ; export const layerColors = [ 'none' , 'red' , 'orange' , 'yellow' , 'green' , 'blue' , 'violet' , 'gray' ] ; export const largeAdditionalInfoKeys = [ 'LMsk' , 'Lr16' , 'Lr32' , 'Layr' , 'Mt16' , 'Mt32' , 'Mtrn' , 'Alph' , 'FMsk' , 'lnk2' , 'FEid' , 'FXid' , 'PxSD' , 'cinf' , ] ; export interface Dict { [ key ] : string ; } export function revMap ( map ) { const result = { } ; Object . keys ( map ) . forEach ( key => result [ map [ key ] ] = key ) ; return result ; } export function createEnum < T > ( prefix , def , map ) { const rev = revMap ( map ) ; const decode = ( val ) : T => { const value = val . split ( '.' ) [ 1 ] ; if ( value && ! rev [ value ] ) throw new Error ( `Unrecognized value for enum: '${ val }'` ) ; return ( rev [ value ] as any ) || def ; } ; const encode = ( val ) : string => { if ( val && ! map [ val as any ] ) throw new Error ( `Invalid value for enum: '${ val }'` ) ; return `${ prefix }.${ map [ val as any ] || map [ def ] }` ; } ; return { decode , encode } ; } export const enum ColorSpace { RGB = 0 , HSB = 1 , CMYK = 2 , Lab = 7 , Grayscale = 8 } export const enum LayerMaskFlags { PositionRelativeToLayer = 1 , LayerMaskDisabled = 2 , InvertLayerMaskWhenBlending = 4 , LayerMaskFromRenderingOtherData = 8 , MaskHasParametersAppliedToIt = 16 } export const enum MaskParams { UserMaskDensity = 1 , UserMaskFeather = 2 , VectorMaskDensity = 4 , VectorMaskFeather = 8 } export const enum ChannelID { Color0 = 0 , Color1 = 1 , Color2 = 2 , Color3 = 3 , Transparency = - 1 , UserMask = - 2 , RealUserMask = - 3 } export const enum Compression { RawData = 0 , RleCompressed = 1 , ZipWithoutPrediction = 2 , ZipWithPrediction = 3 } export interface ChannelData { channelId : ChannelID ; compression : Compression ; buffer : Uint8Array | undefined ; length : number ; } export interface Bounds { top : number ; left : number ; right : number ; bottom : number ; } export interface LayerChannelData { layer : Layer ; channels : ChannelData [ ] ; top : number ; left : number ; right : number ; bottom : number ; mask ? : Bounds ; } export type PixelArray = Uint8ClampedArray | Uint8Array ; export interface PixelData { data : PixelArray ; width : number ; height : number ; } export function offsetForChannel ( channelId , cmyk ) { switch ( channelId ) { case ChannelID . Color0 : return 0 ; case ChannelID . Color1 : return 1 ; case ChannelID . Color2 : return 2 ; case ChannelID . Color3 : return cmyk ? 3 : channelId + 1 ; case ChannelID . Transparency : return cmyk ? 4 : 3 ; default : return channelId + 1 ; } } export function clamp ( value , min , max ) { return value < min ? min : ( value > max ? max : value ) ; } export function hasAlpha ( data ) { const size = data . width * data . height * 4 ; for ( let i = 3 ; i < size ; i += 4 ) { if ( data . data [ i ] !== 255 ) { return true ; } } return false ; } export function resetImageData ( { data } ) { const buffer = new Uint32Array ( data . buffer ) ; const size = buffer . length | 0 ; for ( let p = 0 ; p < size ; p = ( p + 1 ) | 0 ) { buffer [ p ] = 0xff000000 ; } } export function decodeBitmap ( input , output , width , height ) { for ( let y = 0 , p = 0 , o = 0 ; y < height ; y ++ ) { for ( let x = 0 ; x < width ; ) { let b = input [ o ++ ] ; for ( let i = 0 ; i < 8 && x < width ; i ++ , x ++ ) { const v = b & 0x80 ? 0 : 255 ; b = b << 1 ; output [ p ++ ] = v ; output [ p ++ ] = v ; output [ p ++ ] = v ; output [ p ++ ] = 255 ; } } } } export function writeDataRaw ( data , offset , width , height ) { if ( ! width || ! height ) return undefined ; const array = new Uint8Array ( width * height ) ; for ( let i = 0 ; i < array . length ; i ++ ) { array [ i ] = data . data [ i * 4 + offset ] ; } return array ; } export function writeDataRLE ( buffer , { data } , width , height , offsets , large ) { if ( ! width || ! height ) return undefined ; const stride = ( 4 * width ) | 0 ; let ol = 0 ; let o = ( offsets . length * ( large ? 4 : 2 ) * height ) | 0 ; for ( const offset of offsets ) { for ( let y = 0 , p = offset | 0 ; y < height ; y ++ ) { const strideStart = ( y * stride ) | 0 ; const strideEnd = ( strideStart + stride ) | 0 ; const lastIndex = ( strideEnd + offset - 4 ) | 0 ; const lastIndex2 = ( lastIndex - 4 ) | 0 ; const startOffset = o ; for ( p = ( strideStart + offset ) | 0 ; p < strideEnd ; p = ( p + 4 ) | 0 ) { if ( p < lastIndex2 ) { let value1 = data [ p ] ; p = ( p + 4 ) | 0 ; let value2 = data [ p ] ; p = ( p + 4 ) | 0 ; let value3 = data [ p ] ; if ( value1 === value2 && value1 === value3 ) { let count = 3 ; while ( count < 128 && p < lastIndex && data [ ( p + 4 ) | 0 ] === value1 ) { count = ( count + 1 ) | 0 ; p = ( p + 4 ) | 0 ; } buffer [ o ++ ] = 1 - count ; buffer [ o ++ ] = value1 ; } else { const countIndex = o ; let writeLast = true ; let count = 1 ; buffer [ o ++ ] = 0 ; buffer [ o ++ ] = value1 ; while ( p < lastIndex && count < 128 ) { p = ( p + 4 ) | 0 ; value1 = value2 ; value2 = value3 ; value3 = data [ p ] ; if ( value1 === value2 && value1 === value3 ) { p = ( p - 12 ) | 0 ; writeLast = false ; break ; } else { count ++ ; buffer [ o ++ ] = value1 ; } } if ( writeLast ) { if ( count < 127 ) { buffer [ o ++ ] = value2 ; buffer [ o ++ ] = value3 ; count += 2 ; } else if ( count < 128 ) { buffer [ o ++ ] = value2 ; count ++ ; p = ( p - 4 ) | 0 ; } else { p = ( p - 8 ) | 0 ; } } buffer [ countIndex ] = count - 1 ; } } else if ( p === lastIndex ) { buffer [ o ++ ] = 0 ; buffer [ o ++ ] = data [ p ] ; } else { buffer [ o ++ ] = 1 ; buffer [ o ++ ] = data [ p ] ; p = ( p + 4 ) | 0 ; buffer [ o ++ ] = data [ p ] ; } } const length = o - startOffset ; if ( large ) { buffer [ ol ++ ] = ( length >> 24 ) & 0xff ; buffer [ ol ++ ] = ( length >> 16 ) & 0xff ; } buffer [ ol ++ ] = ( length >> 8 ) & 0xff ; buffer [ ol ++ ] = length & 0xff ; } } return buffer . slice ( 0 , o ) ; } export let createCanvas = ( ) => { throw new Error ( 'Canvas not initialized, use initializeCanvas method to set up createCanvas method' ) ; } ; export let createCanvasFromData = ( ) => { throw new Error ( 'Canvas not initialized, use initializeCanvas method to set up createCanvasFromData method' ) ; } ; let tempCanvas = undefined ; export let createImageData = ( width , height ) => { if ( ! tempCanvas ) tempCanvas = createCanvas ( 1 , 1 ) ; return tempCanvas . getContext ( '2d' ) ! . createImageData ( width , height ) ; } ; if ( typeof document !== 'undefined' ) { createCanvas = ( width , height ) => { const canvas = document . createElement ( 'canvas' ) ; canvas . width = width ; canvas . height = height ; return canvas ; } ; createCanvasFromData = ( data ) => { const image = new Image ( ) ; image . src = 'data:image/jpeg;base64,' + fromByteArray ( data ) ; const canvas = document . createElement ( 'canvas' ) ; canvas . width = image . width ; canvas . height = image . height ; canvas . getContext ( '2d' ) ! . drawImage ( image , 0 , 0 ) ; return canvas ; } ; } export function initializeCanvas ( createCanvasMethod , createCanvasFromDataMethod ? , createImageDataMethod ? ) { createCanvas = createCanvasMethod ; createCanvasFromData = createCanvasFromDataMethod || createCanvasFromData ; createImageData = createImageDataMethod || createImageData ; }