preserve import { Palette } from '../../utils/palette' ; import { Point } from '../../utils/point' ; import { PointContainer } from '../../utils/pointContainer' ; import { AbstractDistanceCalculator } from '../../distance/distanceCalculator' ; import { AbstractPaletteQuantizer } from '../paletteQuantizer' ; import { PaletteQuantizerYieldValue } from '../paletteQuantizerYieldValue' ; import { ProgressTracker } from '../../utils' ; function createArray1D ( dimension1 ) { const a = [ ] ; for ( let k = 0 ; k < dimension1 ; k ++ ) { a [ k ] = 0 ; } return a ; } function createArray4D ( dimension1 , dimension2 , dimension3 , dimension4 ) { const a = new Array ( dimension1 ) ; for ( let i = 0 ; i < dimension1 ; i ++ ) { a [ i ] = new Array ( dimension2 ) ; for ( let j = 0 ; j < dimension2 ; j ++ ) { a [ i ] [ j ] = new Array ( dimension3 ) ; for ( let k = 0 ; k < dimension3 ; k ++ ) { a [ i ] [ j ] [ k ] = new Array ( dimension4 ) ; for ( let l = 0 ; l < dimension4 ; l ++ ) { a [ i ] [ j ] [ k ] [ l ] = 0 ; } } } } return a ; } function createArray3D ( dimension1 , dimension2 , dimension3 ) { const a = new Array ( dimension1 ) ; for ( let i = 0 ; i < dimension1 ; i ++ ) { a [ i ] = new Array ( dimension2 ) ; for ( let j = 0 ; j < dimension2 ; j ++ ) { a [ i ] [ j ] = new Array ( dimension3 ) ; for ( let k = 0 ; k < dimension3 ; k ++ ) { a [ i ] [ j ] [ k ] = 0 ; } } } return a ; } function fillArray3D < T > ( a , dimension1 , dimension2 , dimension3 , value ) { for ( let i = 0 ; i < dimension1 ; i ++ ) { a [ i ] = [ ] ; for ( let j = 0 ; j < dimension2 ; j ++ ) { a [ i ] [ j ] = [ ] ; for ( let k = 0 ; k < dimension3 ; k ++ ) { a [ i ] [ j ] [ k ] = value ; } } } } function fillArray1D < T > ( a , dimension1 , value ) { for ( let i = 0 ; i < dimension1 ; i ++ ) { a [ i ] = value ; } } export class WuColorCube { redMinimum ! : number ; redMaximum ! : number ; greenMinimum ! : number ; greenMaximum ! : number ; blueMinimum ! : number ; blueMaximum ! : number ; volume ! : number ; alphaMinimum ! : number ; alphaMaximum ! : number ; } export class WuQuant extends AbstractPaletteQuantizer { private static readonly _alpha = 3 ; private static readonly _red = 2 ; private static readonly _green = 1 ; private static readonly _blue = 0 ; private _reds ! : number [ ] ; private _greens ! : number [ ] ; private _blues ! : number [ ] ; private _alphas ! : number [ ] ; private _sums ! : number [ ] ; private _weights ! : number [ ] [ ] [ ] [ ] ; private _momentsRed ! : number [ ] [ ] [ ] [ ] ; private _momentsGreen ! : number [ ] [ ] [ ] [ ] ; private _momentsBlue ! : number [ ] [ ] [ ] [ ] ; private _momentsAlpha ! : number [ ] [ ] [ ] [ ] ; private _moments ! : number [ ] [ ] [ ] [ ] ; private _table ! : number [ ] ; private _pixels ! : Point [ ] ; private _cubes ! : WuColorCube [ ] ; private _colors ! : number ; private _significantBitsPerChannel ! : number ; private _maxSideIndex ! : number ; private _alphaMaxSideIndex ! : number ; private _sideSize ! : number ; private _alphaSideSize ! : number ; private readonly _distance : AbstractDistanceCalculator ; constructor ( colorDistanceCalculator , colors = 256 , significantBitsPerChannel = 5 ) { super ( ) ; this . _distance = colorDistanceCalculator ; this . _setQuality ( significantBitsPerChannel ) ; this . _initialize ( colors ) ; } sample ( image ) { const pointArray = image . getPointArray ( ) ; for ( let i = 0 , l = pointArray . length ; i < l ; i ++ ) { this . _addColor ( pointArray [ i ] ) ; } this . _pixels = this . _pixels . concat ( pointArray ) ; } * quantize ( ) { yield * this . _preparePalette ( ) ; const palette = new Palette ( ) ; for ( let paletteIndex = 0 ; paletteIndex < this . _colors ; paletteIndex ++ ) { if ( this . _sums [ paletteIndex ] > 0 ) { const sum = this . _sums [ paletteIndex ] ; const r = this . _reds [ paletteIndex ] / sum ; const g = this . _greens [ paletteIndex ] / sum ; const b = this . _blues [ paletteIndex ] / sum ; const a = this . _alphas [ paletteIndex ] / sum ; const color = Point . createByRGBA ( r | 0 , g | 0 , b | 0 , a | 0 ) ; palette . add ( color ) ; } } palette . sort ( ) ; yield { palette , progress : 100 , } ; } private * _preparePalette ( ) { yield * this . _calculateMoments ( ) ; let next = 0 ; const volumeVariance = createArray1D ( this . _colors ) ; for ( let cubeIndex = 1 ; cubeIndex < this . _colors ; ++ cubeIndex ) { if ( this . _cut ( this . _cubes [ next ] , this . _cubes [ cubeIndex ] ) ) { volumeVariance [ next ] = this . _cubes [ next ] . volume > 1 ? this . _calculateVariance ( this . _cubes [ next ] ) : 0.0 ; volumeVariance [ cubeIndex ] = this . _cubes [ cubeIndex ] . volume > 1 ? this . _calculateVariance ( this . _cubes [ cubeIndex ] ) : 0.0 ; } else { volumeVariance [ next ] = 0.0 ; cubeIndex -- ; } next = 0 ; let temp = volumeVariance [ 0 ] ; for ( let index = 1 ; index <= cubeIndex ; ++ index ) { if ( volumeVariance [ index ] > temp ) { temp = volumeVariance [ index ] ; next = index ; } } if ( temp <= 0.0 ) { this . _colors = cubeIndex + 1 ; break ; } } const lookupRed = [ ] ; const lookupGreen = [ ] ; const lookupBlue = [ ] ; const lookupAlpha = [ ] ; for ( let k = 0 ; k < this . _colors ; ++ k ) { const weight = WuQuant . _volume ( this . _cubes [ k ] , this . _weights ) ; if ( weight > 0 ) { lookupRed [ k ] = ( WuQuant . _volume ( this . _cubes [ k ] , this . _momentsRed ) / weight ) | 0 ; lookupGreen [ k ] = ( WuQuant . _volume ( this . _cubes [ k ] , this . _momentsGreen ) / weight ) | 0 ; lookupBlue [ k ] = ( WuQuant . _volume ( this . _cubes [ k ] , this . _momentsBlue ) / weight ) | 0 ; lookupAlpha [ k ] = ( WuQuant . _volume ( this . _cubes [ k ] , this . _momentsAlpha ) / weight ) | 0 ; } else { lookupRed [ k ] = 0 ; lookupGreen [ k ] = 0 ; lookupBlue [ k ] = 0 ; lookupAlpha [ k ] = 0 ; } } this . _reds = createArray1D ( this . _colors + 1 ) ; this . _greens = createArray1D ( this . _colors + 1 ) ; this . _blues = createArray1D ( this . _colors + 1 ) ; this . _alphas = createArray1D ( this . _colors + 1 ) ; this . _sums = createArray1D ( this . _colors + 1 ) ; for ( let index = 0 , l = this . _pixels . length ; index < l ; index ++ ) { const color = this . _pixels [ index ] ; const match = - 1 ; let bestMatch = match ; let bestDistance = Number . MAX_VALUE ; for ( let lookup = 0 ; lookup < this . _colors ; lookup ++ ) { const foundRed = lookupRed [ lookup ] ; const foundGreen = lookupGreen [ lookup ] ; const foundBlue = lookupBlue [ lookup ] ; const foundAlpha = lookupAlpha [ lookup ] ; const distance = this . _distance . calculateRaw ( foundRed , foundGreen , foundBlue , foundAlpha , color . r , color . g , color . b , color . a ) ; if ( distance < bestDistance ) { bestDistance = distance ; bestMatch = lookup ; } } this . _reds [ bestMatch ] += color . r ; this . _greens [ bestMatch ] += color . g ; this . _blues [ bestMatch ] += color . b ; this . _alphas [ bestMatch ] += color . a ; this . _sums [ bestMatch ] ++ ; } } private _addColor ( color ) { const bitsToRemove = 8 - this . _significantBitsPerChannel ; const indexRed = ( color . r >> bitsToRemove ) + 1 ; const indexGreen = ( color . g >> bitsToRemove ) + 1 ; const indexBlue = ( color . b >> bitsToRemove ) + 1 ; const indexAlpha = ( color . a >> bitsToRemove ) + 1 ; this . _weights [ indexAlpha ] [ indexRed ] [ indexGreen ] [ indexBlue ] ++ ; this . _momentsRed [ indexAlpha ] [ indexRed ] [ indexGreen ] [ indexBlue ] += color . r ; this . _momentsGreen [ indexAlpha ] [ indexRed ] [ indexGreen ] [ indexBlue ] += color . g ; this . _momentsBlue [ indexAlpha ] [ indexRed ] [ indexGreen ] [ indexBlue ] += color . b ; this . _momentsAlpha [ indexAlpha ] [ indexRed ] [ indexGreen ] [ indexBlue ] += color . a ; this . _moments [ indexAlpha ] [ indexRed ] [ indexGreen ] [ indexBlue ] += this . _table [ color . r ] + this . _table [ color . g ] + this . _table [ color . b ] + this . _table [ color . a ] ; } /**
     * Converts the histogram to a series of _moments.
     */ private * _calculateMoments ( ) { const area = [ ] ; const areaRed = [ ] ; const areaGreen = [ ] ; const areaBlue = [ ] ; const areaAlpha = [ ] ; const area2 = [ ] ; const xarea = createArray3D ( this . _sideSize , this . _sideSize , this . _sideSize ) ; const xareaRed = createArray3D ( this . _sideSize , this . _sideSize , this . _sideSize ) ; const xareaGreen = createArray3D ( this . _sideSize , this . _sideSize , this . _sideSize ) ; const xareaBlue = createArray3D ( this . _sideSize , this . _sideSize , this . _sideSize ) ; const xareaAlpha = createArray3D ( this . _sideSize , this . _sideSize , this . _sideSize ) ; const xarea2 = createArray3D ( this . _sideSize , this . _sideSize , this . _sideSize ) ; let trackerProgress = 0 ; const tracker = new ProgressTracker ( this . _alphaMaxSideIndex * this . _maxSideIndex , 99 ) ; for ( let alphaIndex = 1 ; alphaIndex <= this . _alphaMaxSideIndex ; ++ alphaIndex ) { fillArray3D < number > ( xarea , this . _sideSize , this . _sideSize , this . _sideSize , 0 ) ; fillArray3D < number > ( xareaRed , this . _sideSize , this . _sideSize , this . _sideSize , 0 ) ; fillArray3D < number > ( xareaGreen , this . _sideSize , this . _sideSize , this . _sideSize , 0 ) ; fillArray3D < number > ( xareaBlue , this . _sideSize , this . _sideSize , this . _sideSize , 0 ) ; fillArray3D < number > ( xareaAlpha , this . _sideSize , this . _sideSize , this . _sideSize , 0 ) ; fillArray3D < number > ( xarea2 , this . _sideSize , this . _sideSize , this . _sideSize , 0 ) ; for ( let redIndex = 1 ; redIndex <= this . _maxSideIndex ; ++ redIndex , ++ trackerProgress ) { if ( tracker . shouldNotify ( trackerProgress ) ) { yield { progress : tracker . progress , } ; } fillArray1D < number > ( area , this . _sideSize , 0 ) ; fillArray1D < number > ( areaRed , this . _sideSize , 0 ) ; fillArray1D < number > ( areaGreen , this . _sideSize , 0 ) ; fillArray1D < number > ( areaBlue , this . _sideSize , 0 ) ; fillArray1D < number > ( areaAlpha , this . _sideSize , 0 ) ; fillArray1D < number > ( area2 , this . _sideSize , 0 ) ; for ( let greenIndex = 1 ; greenIndex <= this . _maxSideIndex ; ++ greenIndex ) { let line = 0 ; let lineRed = 0 ; let lineGreen = 0 ; let lineBlue = 0 ; let lineAlpha = 0 ; let line2 = 0.0 ; for ( let blueIndex = 1 ; blueIndex <= this . _maxSideIndex ; ++ blueIndex ) { line += this . _weights [ alphaIndex ] [ redIndex ] [ greenIndex ] [ blueIndex ] ; lineRed += this . _momentsRed [ alphaIndex ] [ redIndex ] [ greenIndex ] [ blueIndex ] ; lineGreen += this . _momentsGreen [ alphaIndex ] [ redIndex ] [ greenIndex ] [ blueIndex ] ; lineBlue += this . _momentsBlue [ alphaIndex ] [ redIndex ] [ greenIndex ] [ blueIndex ] ; lineAlpha += this . _momentsAlpha [ alphaIndex ] [ redIndex ] [ greenIndex ] [ blueIndex ] ; line2 += this . _moments [ alphaIndex ] [ redIndex ] [ greenIndex ] [ blueIndex ] ; area [ blueIndex ] += line ; areaRed [ blueIndex ] += lineRed ; areaGreen [ blueIndex ] += lineGreen ; areaBlue [ blueIndex ] += lineBlue ; areaAlpha [ blueIndex ] += lineAlpha ; area2 [ blueIndex ] += line2 ; xarea [ redIndex ] [ greenIndex ] [ blueIndex ] = xarea [ redIndex - 1 ] [ greenIndex ] [ blueIndex ] + area [ blueIndex ] ; xareaRed [ redIndex ] [ greenIndex ] [ blueIndex ] = xareaRed [ redIndex - 1 ] [ greenIndex ] [ blueIndex ] + areaRed [ blueIndex ] ; xareaGreen [ redIndex ] [ greenIndex ] [ blueIndex ] = xareaGreen [ redIndex - 1 ] [ greenIndex ] [ blueIndex ] + areaGreen [ blueIndex ] ; xareaBlue [ redIndex ] [ greenIndex ] [ blueIndex ] = xareaBlue [ redIndex - 1 ] [ greenIndex ] [ blueIndex ] + areaBlue [ blueIndex ] ; xareaAlpha [ redIndex ] [ greenIndex ] [ blueIndex ] = xareaAlpha [ redIndex - 1 ] [ greenIndex ] [ blueIndex ] + areaAlpha [ blueIndex ] ; xarea2 [ redIndex ] [ greenIndex ] [ blueIndex ] = xarea2 [ redIndex - 1 ] [ greenIndex ] [ blueIndex ] + area2 [ blueIndex ] ; this . _weights [ alphaIndex ] [ redIndex ] [ greenIndex ] [ blueIndex ] = this . _weights [ alphaIndex - 1 ] [ redIndex ] [ greenIndex ] [ blueIndex ] + xarea [ redIndex ] [ greenIndex ] [ blueIndex ] ; this . _momentsRed [ alphaIndex ] [ redIndex ] [ greenIndex ] [ blueIndex ] = this . _momentsRed [ alphaIndex - 1 ] [ redIndex ] [ greenIndex ] [ blueIndex ] + xareaRed [ redIndex ] [ greenIndex ] [ blueIndex ] ; this . _momentsGreen [ alphaIndex ] [ redIndex ] [ greenIndex ] [ blueIndex ] = this . _momentsGreen [ alphaIndex - 1 ] [ redIndex ] [ greenIndex ] [ blueIndex ] + xareaGreen [ redIndex ] [ greenIndex ] [ blueIndex ] ; this . _momentsBlue [ alphaIndex ] [ redIndex ] [ greenIndex ] [ blueIndex ] = this . _momentsBlue [ alphaIndex - 1 ] [ redIndex ] [ greenIndex ] [ blueIndex ] + xareaBlue [ redIndex ] [ greenIndex ] [ blueIndex ] ; this . _momentsAlpha [ alphaIndex ] [ redIndex ] [ greenIndex ] [ blueIndex ] = this . _momentsAlpha [ alphaIndex - 1 ] [ redIndex ] [ greenIndex ] [ blueIndex ] + xareaAlpha [ redIndex ] [ greenIndex ] [ blueIndex ] ; this . _moments [ alphaIndex ] [ redIndex ] [ greenIndex ] [ blueIndex ] = this . _moments [ alphaIndex - 1 ] [ redIndex ] [ greenIndex ] [ blueIndex ] + xarea2 [ redIndex ] [ greenIndex ] [ blueIndex ] ; } } } } } /**
     * Computes the volume of the cube in a specific moment.
     */ private static _volumeFloat ( cube , moment ) { return ( moment [ cube . alphaMaximum ] [ cube . redMaximum ] [ cube . greenMaximum ] [ cube . blueMaximum ] - moment [ cube . alphaMaximum ] [ cube . redMaximum ] [ cube . greenMinimum ] [ cube . blueMaximum ] - moment [ cube . alphaMaximum ] [ cube . redMinimum ] [ cube . greenMaximum ] [ cube . blueMaximum ] + moment [ cube . alphaMaximum ] [ cube . redMinimum ] [ cube . greenMinimum ] [ cube . blueMaximum ] - moment [ cube . alphaMinimum ] [ cube . redMaximum ] [ cube . greenMaximum ] [ cube . blueMaximum ] + moment [ cube . alphaMinimum ] [ cube . redMaximum ] [ cube . greenMinimum ] [ cube . blueMaximum ] + moment [ cube . alphaMinimum ] [ cube . redMinimum ] [ cube . greenMaximum ] [ cube . blueMaximum ] - moment [ cube . alphaMinimum ] [ cube . redMinimum ] [ cube . greenMinimum ] [ cube . blueMaximum ] - ( moment [ cube . alphaMaximum ] [ cube . redMaximum ] [ cube . greenMaximum ] [ cube . blueMinimum ] - moment [ cube . alphaMinimum ] [ cube . redMaximum ] [ cube . greenMaximum ] [ cube . blueMinimum ] - moment [ cube . alphaMaximum ] [ cube . redMaximum ] [ cube . greenMinimum ] [ cube . blueMinimum ] + moment [ cube . alphaMinimum ] [ cube . redMaximum ] [ cube . greenMinimum ] [ cube . blueMinimum ] - moment [ cube . alphaMaximum ] [ cube . redMinimum ] [ cube . greenMaximum ] [ cube . blueMinimum ] + moment [ cube . alphaMinimum ] [ cube . redMinimum ] [ cube . greenMaximum ] [ cube . blueMinimum ] + moment [ cube . alphaMaximum ] [ cube . redMinimum ] [ cube . greenMinimum ] [ cube . blueMinimum ] - moment [ cube . alphaMinimum ] [ cube . redMinimum ] [ cube . greenMinimum ] [ cube . blueMinimum ] ) ) ; } /**
     * Computes the volume of the cube in a specific moment.
     */ private static _volume ( cube , moment ) { return WuQuant . _volumeFloat ( cube , moment ) | 0 ; } /**
     * Splits the cube in given position][and color direction.
     */ private static _top ( cube , direction , position , moment ) { let result ; switch ( direction ) { case WuQuant . _alpha : result = moment [ position ] [ cube . redMaximum ] [ cube . greenMaximum ] [ cube . blueMaximum ] - moment [ position ] [ cube . redMaximum ] [ cube . greenMinimum ] [ cube . blueMaximum ] - moment [ position ] [ cube . redMinimum ] [ cube . greenMaximum ] [ cube . blueMaximum ] + moment [ position ] [ cube . redMinimum ] [ cube . greenMinimum ] [ cube . blueMaximum ] - ( moment [ position ] [ cube . redMaximum ] [ cube . greenMaximum ] [ cube . blueMinimum ] - moment [ position ] [ cube . redMaximum ] [ cube . greenMinimum ] [ cube . blueMinimum ] - moment [ position ] [ cube . redMinimum ] [ cube . greenMaximum ] [ cube . blueMinimum ] + moment [ position ] [ cube . redMinimum ] [ cube . greenMinimum ] [ cube . blueMinimum ] ) ; break ; case WuQuant . _red : result = moment [ cube . alphaMaximum ] [ position ] [ cube . greenMaximum ] [ cube . blueMaximum ] - moment [ cube . alphaMaximum ] [ position ] [ cube . greenMinimum ] [ cube . blueMaximum ] - moment [ cube . alphaMinimum ] [ position ] [ cube . greenMaximum ] [ cube . blueMaximum ] + moment [ cube . alphaMinimum ] [ position ] [ cube . greenMinimum ] [ cube . blueMaximum ] - ( moment [ cube . alphaMaximum ] [ position ] [ cube . greenMaximum ] [ cube . blueMinimum ] - moment [ cube . alphaMaximum ] [ position ] [ cube . greenMinimum ] [ cube . blueMinimum ] - moment [ cube . alphaMinimum ] [ position ] [ cube . greenMaximum ] [ cube . blueMinimum ] + moment [ cube . alphaMinimum ] [ position ] [ cube . greenMinimum ] [ cube . blueMinimum ] ) ; break ; case WuQuant . _green : result = moment [ cube . alphaMaximum ] [ cube . redMaximum ] [ position ] [ cube . blueMaximum ] - moment [ cube . alphaMaximum ] [ cube . redMinimum ] [ position ] [ cube . blueMaximum ] - moment [ cube . alphaMinimum ] [ cube . redMaximum ] [ position ] [ cube . blueMaximum ] + moment [ cube . alphaMinimum ] [ cube . redMinimum ] [ position ] [ cube . blueMaximum ] - ( moment [ cube . alphaMaximum ] [ cube . redMaximum ] [ position ] [ cube . blueMinimum ] - moment [ cube . alphaMaximum ] [ cube . redMinimum ] [ position ] [ cube . blueMinimum ] - moment [ cube . alphaMinimum ] [ cube . redMaximum ] [ position ] [ cube . blueMinimum ] + moment [ cube . alphaMinimum ] [ cube . redMinimum ] [ position ] [ cube . blueMinimum ] ) ; break ; case WuQuant . _blue : result = moment [ cube . alphaMaximum ] [ cube . redMaximum ] [ cube . greenMaximum ] [ position ] - moment [ cube . alphaMaximum ] [ cube . redMaximum ] [ cube . greenMinimum ] [ position ] - moment [ cube . alphaMaximum ] [ cube . redMinimum ] [ cube . greenMaximum ] [ position ] + moment [ cube . alphaMaximum ] [ cube . redMinimum ] [ cube . greenMinimum ] [ position ] - ( moment [ cube . alphaMinimum ] [ cube . redMaximum ] [ cube . greenMaximum ] [ position ] - moment [ cube . alphaMinimum ] [ cube . redMaximum ] [ cube . greenMinimum ] [ position ] - moment [ cube . alphaMinimum ] [ cube . redMinimum ] [ cube . greenMaximum ] [ position ] + moment [ cube . alphaMinimum ] [ cube . redMinimum ] [ cube . greenMinimum ] [ position ] ) ; break ; default : throw new Error ( 'impossible' ) ; } return result | 0 ; } /**
     * Splits the cube in a given color direction at its minimum.
     */ private static _bottom ( cube , direction , moment ) { switch ( direction ) { case WuQuant . _alpha : return ( - moment [ cube . alphaMinimum ] [ cube . redMaximum ] [ cube . greenMaximum ] [ cube . blueMaximum ] + moment [ cube . alphaMinimum ] [ cube . redMaximum ] [ cube . greenMinimum ] [ cube . blueMaximum ] + moment [ cube . alphaMinimum ] [ cube . redMinimum ] [ cube . greenMaximum ] [ cube . blueMaximum ] - moment [ cube . alphaMinimum ] [ cube . redMinimum ] [ cube . greenMinimum ] [ cube . blueMaximum ] - ( - moment [ cube . alphaMinimum ] [ cube . redMaximum ] [ cube . greenMaximum ] [ cube . blueMinimum ] + moment [ cube . alphaMinimum ] [ cube . redMaximum ] [ cube . greenMinimum ] [ cube . blueMinimum ] + moment [ cube . alphaMinimum ] [ cube . redMinimum ] [ cube . greenMaximum ] [ cube . blueMinimum ] - moment [ cube . alphaMinimum ] [ cube . redMinimum ] [ cube . greenMinimum ] [ cube . blueMinimum ] ) ) ; case WuQuant . _red : return ( - moment [ cube . alphaMaximum ] [ cube . redMinimum ] [ cube . greenMaximum ] [ cube . blueMaximum ] + moment [ cube . alphaMaximum ] [ cube . redMinimum ] [ cube . greenMinimum ] [ cube . blueMaximum ] + moment [ cube . alphaMinimum ] [ cube . redMinimum ] [ cube . greenMaximum ] [ cube . blueMaximum ] - moment [ cube . alphaMinimum ] [ cube . redMinimum ] [ cube . greenMinimum ] [ cube . blueMaximum ] - ( - moment [ cube . alphaMaximum ] [ cube . redMinimum ] [ cube . greenMaximum ] [ cube . blueMinimum ] + moment [ cube . alphaMaximum ] [ cube . redMinimum ] [ cube . greenMinimum ] [ cube . blueMinimum ] + moment [ cube . alphaMinimum ] [ cube . redMinimum ] [ cube . greenMaximum ] [ cube . blueMinimum ] - moment [ cube . alphaMinimum ] [ cube . redMinimum ] [ cube . greenMinimum ] [ cube . blueMinimum ] ) ) ; case WuQuant . _green : return ( - moment [ cube . alphaMaximum ] [ cube . redMaximum ] [ cube . greenMinimum ] [ cube . blueMaximum ] + moment [ cube . alphaMaximum ] [ cube . redMinimum ] [ cube . greenMinimum ] [ cube . blueMaximum ] + moment [ cube . alphaMinimum ] [ cube . redMaximum ] [ cube . greenMinimum ] [ cube . blueMaximum ] - moment [ cube . alphaMinimum ] [ cube . redMinimum ] [ cube . greenMinimum ] [ cube . blueMaximum ] - ( - moment [ cube . alphaMaximum ] [ cube . redMaximum ] [ cube . greenMinimum ] [ cube . blueMinimum ] + moment [ cube . alphaMaximum ] [ cube . redMinimum ] [ cube . greenMinimum ] [ cube . blueMinimum ] + moment [ cube . alphaMinimum ] [ cube . redMaximum ] [ cube . greenMinimum ] [ cube . blueMinimum ] - moment [ cube . alphaMinimum ] [ cube . redMinimum ] [ cube . greenMinimum ] [ cube . blueMinimum ] ) ) ; case WuQuant . _blue : return ( - moment [ cube . alphaMaximum ] [ cube . redMaximum ] [ cube . greenMaximum ] [ cube . blueMinimum ] + moment [ cube . alphaMaximum ] [ cube . redMaximum ] [ cube . greenMinimum ] [ cube . blueMinimum ] + moment [ cube . alphaMaximum ] [ cube . redMinimum ] [ cube . greenMaximum ] [ cube . blueMinimum ] - moment [ cube . alphaMaximum ] [ cube . redMinimum ] [ cube . greenMinimum ] [ cube . blueMinimum ] - ( - moment [ cube . alphaMinimum ] [ cube . redMaximum ] [ cube . greenMaximum ] [ cube . blueMinimum ] + moment [ cube . alphaMinimum ] [ cube . redMaximum ] [ cube . greenMinimum ] [ cube . blueMinimum ] + moment [ cube . alphaMinimum ] [ cube . redMinimum ] [ cube . greenMaximum ] [ cube . blueMinimum ] - moment [ cube . alphaMinimum ] [ cube . redMinimum ] [ cube . greenMinimum ] [ cube . blueMinimum ] ) ) ; default : return 0 ; } } /**
     * Calculates statistical variance for a given cube.
     */ private _calculateVariance ( cube ) { const volumeRed = WuQuant . _volume ( cube , this . _momentsRed ) ; const volumeGreen = WuQuant . _volume ( cube , this . _momentsGreen ) ; const volumeBlue = WuQuant . _volume ( cube , this . _momentsBlue ) ; const volumeAlpha = WuQuant . _volume ( cube , this . _momentsAlpha ) ; const volumeMoment = WuQuant . _volumeFloat ( cube , this . _moments ) ; const volumeWeight = WuQuant . _volume ( cube , this . _weights ) ; const distance = volumeRed * volumeRed + volumeGreen * volumeGreen + volumeBlue * volumeBlue + volumeAlpha * volumeAlpha ; return volumeMoment - distance / volumeWeight ; } /**
     * Finds the optimal (maximal) position for the cut.
     */ private _maximize ( cube , direction , first , last , wholeRed , wholeGreen , wholeBlue , wholeAlpha , wholeWeight ) { const bottomRed = WuQuant . _bottom ( cube , direction , this . _momentsRed ) | 0 ; const bottomGreen = WuQuant . _bottom ( cube , direction , this . _momentsGreen ) | 0 ; const bottomBlue = WuQuant . _bottom ( cube , direction , this . _momentsBlue ) | 0 ; const bottomAlpha = WuQuant . _bottom ( cube , direction , this . _momentsAlpha ) | 0 ; const bottomWeight = WuQuant . _bottom ( cube , direction , this . _weights ) | 0 ; let result = 0.0 ; let cutPosition = - 1 ; for ( let position = first ; position < last ; ++ position ) { let halfRed = bottomRed + WuQuant . _top ( cube , direction , position , this . _momentsRed ) ; let halfGreen = bottomGreen + WuQuant . _top ( cube , direction , position , this . _momentsGreen ) ; let halfBlue = bottomBlue + WuQuant . _top ( cube , direction , position , this . _momentsBlue ) ; let halfAlpha = bottomAlpha + WuQuant . _top ( cube , direction , position , this . _momentsAlpha ) ; let halfWeight = bottomWeight + WuQuant . _top ( cube , direction , position , this . _weights ) ; if ( halfWeight !== 0 ) { let halfDistance = halfRed * halfRed + halfGreen * halfGreen + halfBlue * halfBlue + halfAlpha * halfAlpha ; let temp = halfDistance / halfWeight ; halfRed = wholeRed - halfRed ; halfGreen = wholeGreen - halfGreen ; halfBlue = wholeBlue - halfBlue ; halfAlpha = wholeAlpha - halfAlpha ; halfWeight = wholeWeight - halfWeight ; if ( halfWeight !== 0 ) { halfDistance = halfRed * halfRed + halfGreen * halfGreen + halfBlue * halfBlue + halfAlpha * halfAlpha ; temp += halfDistance / halfWeight ; if ( temp > result ) { result = temp ; cutPosition = position ; } } } } return { max : result , position : cutPosition } ; } private _cut ( first , second ) { let direction ; const wholeRed = WuQuant . _volume ( first , this . _momentsRed ) ; const wholeGreen = WuQuant . _volume ( first , this . _momentsGreen ) ; const wholeBlue = WuQuant . _volume ( first , this . _momentsBlue ) ; const wholeAlpha = WuQuant . _volume ( first , this . _momentsAlpha ) ; const wholeWeight = WuQuant . _volume ( first , this . _weights ) ; const red = this . _maximize ( first , WuQuant . _red , first . redMinimum + 1 , first . redMaximum , wholeRed , wholeGreen , wholeBlue , wholeAlpha , wholeWeight ) ; const green = this . _maximize ( first , WuQuant . _green , first . greenMinimum + 1 , first . greenMaximum , wholeRed , wholeGreen , wholeBlue , wholeAlpha , wholeWeight ) ; const blue = this . _maximize ( first , WuQuant . _blue , first . blueMinimum + 1 , first . blueMaximum , wholeRed , wholeGreen , wholeBlue , wholeAlpha , wholeWeight ) ; const alpha = this . _maximize ( first , WuQuant . _alpha , first . alphaMinimum + 1 , first . alphaMaximum , wholeRed , wholeGreen , wholeBlue , wholeAlpha , wholeWeight ) ; if ( alpha . max >= red . max && alpha . max >= green . max && alpha . max >= blue . max ) { direction = WuQuant . _alpha ; if ( alpha . position < 0 ) return false ; } else if ( red . max >= alpha . max && red . max >= green . max && red . max >= blue . max ) { direction = WuQuant . _red ; } else if ( green . max >= alpha . max && green . max >= red . max && green . max >= blue . max ) { direction = WuQuant . _green ; } else { direction = WuQuant . _blue ; } second . redMaximum = first . redMaximum ; second . greenMaximum = first . greenMaximum ; second . blueMaximum = first . blueMaximum ; second . alphaMaximum = first . alphaMaximum ; switch ( direction ) { case WuQuant . _red : second . redMinimum = first . redMaximum = red . position ; second . greenMinimum = first . greenMinimum ; second . blueMinimum = first . blueMinimum ; second . alphaMinimum = first . alphaMinimum ; break ; case WuQuant . _green : second . greenMinimum = first . greenMaximum = green . position ; second . redMinimum = first . redMinimum ; second . blueMinimum = first . blueMinimum ; second . alphaMinimum = first . alphaMinimum ; break ; case WuQuant . _blue : second . blueMinimum = first . blueMaximum = blue . position ; second . redMinimum = first . redMinimum ; second . greenMinimum = first . greenMinimum ; second . alphaMinimum = first . alphaMinimum ; break ; case WuQuant . _alpha : second . alphaMinimum = first . alphaMaximum = alpha . position ; second . blueMinimum = first . blueMinimum ; second . redMinimum = first . redMinimum ; second . greenMinimum = first . greenMinimum ; break ; } first . volume = ( first . redMaximum - first . redMinimum ) * ( first . greenMaximum - first . greenMinimum ) * ( first . blueMaximum - first . blueMinimum ) * ( first . alphaMaximum - first . alphaMinimum ) ; second . volume = ( second . redMaximum - second . redMinimum ) * ( second . greenMaximum - second . greenMinimum ) * ( second . blueMaximum - second . blueMinimum ) * ( second . alphaMaximum - second . alphaMinimum ) ; return true ; } private _initialize ( colors ) { this . _colors = colors ; this . _cubes = [ ] ; for ( let cubeIndex = 0 ; cubeIndex < colors ; cubeIndex ++ ) { this . _cubes [ cubeIndex ] = new WuColorCube ( ) ; } this . _cubes [ 0 ] . redMinimum = 0 ; this . _cubes [ 0 ] . greenMinimum = 0 ; this . _cubes [ 0 ] . blueMinimum = 0 ; this . _cubes [ 0 ] . alphaMinimum = 0 ; this . _cubes [ 0 ] . redMaximum = this . _maxSideIndex ; this . _cubes [ 0 ] . greenMaximum = this . _maxSideIndex ; this . _cubes [ 0 ] . blueMaximum = this . _maxSideIndex ; this . _cubes [ 0 ] . alphaMaximum = this . _alphaMaxSideIndex ; this . _weights = createArray4D ( this . _alphaSideSize , this . _sideSize , this . _sideSize , this . _sideSize ) ; this . _momentsRed = createArray4D ( this . _alphaSideSize , this . _sideSize , this . _sideSize , this . _sideSize ) ; this . _momentsGreen = createArray4D ( this . _alphaSideSize , this . _sideSize , this . _sideSize , this . _sideSize ) ; this . _momentsBlue = createArray4D ( this . _alphaSideSize , this . _sideSize , this . _sideSize , this . _sideSize ) ; this . _momentsAlpha = createArray4D ( this . _alphaSideSize , this . _sideSize , this . _sideSize , this . _sideSize ) ; this . _moments = createArray4D ( this . _alphaSideSize , this . _sideSize , this . _sideSize , this . _sideSize ) ; this . _table = [ ] ; for ( let tableIndex = 0 ; tableIndex < 256 ; ++ tableIndex ) { this . _table [ tableIndex ] = tableIndex * tableIndex ; } this . _pixels = [ ] ; } private _setQuality ( significantBitsPerChannel = 5 ) { this . _significantBitsPerChannel = significantBitsPerChannel ; this . _maxSideIndex = 1 << this . _significantBitsPerChannel ; this . _alphaMaxSideIndex = this . _maxSideIndex ; this . _sideSize = this . _maxSideIndex + 1 ; this . _alphaSideSize = this . _alphaMaxSideIndex + 1 ; } }