preserve import { Palette } from '../../utils/palette' ; import { Point } from '../../utils/point' ; import { PointContainer } from '../../utils/pointContainer' ; import { AbstractDistanceCalculator } from '../../distance/distanceCalculator' ; import { ColorHistogram } from './colorHistogram' ; import { AbstractPaletteQuantizer } from '../paletteQuantizer' ; import { PaletteQuantizerYieldValue } from '../paletteQuantizerYieldValue' ; import { stableSort } from '../../utils/arithmetic' ; import { ProgressTracker } from '../../utils' ; class RemovedColor { readonly index : number ; readonly color : Point ; readonly distance : number ; constructor ( index , color , distance ) { this . index = index ; this . color = color ; this . distance = distance ; } } export class RGBQuant extends AbstractPaletteQuantizer { private readonly _colors : number ; private readonly _initialDistance : number ; private readonly _distanceIncrement : number ; private readonly _histogram : ColorHistogram ; private readonly _distance : AbstractDistanceCalculator ; constructor ( colorDistanceCalculator , colors = 256 , method = 2 ) { super ( ) ; this . _distance = colorDistanceCalculator ; this . _colors = colors ; this . _histogram = new ColorHistogram ( method , colors ) ; this . _initialDistance = 0.01 ; this . _distanceIncrement = 0.005 ; } sample ( image ) { this . _histogram . sample ( image ) ; } * quantize ( ) { const idxi32 = this . _histogram . getImportanceSortedColorsIDXI32 ( ) ; if ( idxi32 . length === 0 ) { throw new Error ( 'No colors in image' ) ; } yield * this . _buildPalette ( idxi32 ) ; } private * _buildPalette ( idxi32 ) { const palette = new Palette ( ) ; const colorArray = palette . getPointContainer ( ) . getPointArray ( ) ; const usageArray = new Array ( idxi32 . length ) ; for ( let i = 0 ; i < idxi32 . length ; i ++ ) { colorArray . push ( Point . createByUint32 ( idxi32 [ i ] ) ) ; usageArray [ i ] = 1 ; } const len = colorArray . length ; const memDist = [ ] ; let palLen = len ; let thold = this . _initialDistance ; const tracker = new ProgressTracker ( palLen - this . _colors , 99 ) ; while ( palLen > this . _colors ) { memDist . length = 0 ; for ( let i = 0 ; i < len ; i ++ ) { if ( tracker . shouldNotify ( len - palLen ) ) { yield { progress : tracker . progress , } ; } if ( usageArray [ i ] === 0 ) continue ; const pxi = colorArray [ i ] ; for ( let j = i + 1 ; j < len ; j ++ ) { if ( usageArray [ j ] === 0 ) continue ; const pxj = colorArray [ j ] ; const dist = this . _distance . calculateNormalized ( pxi , pxj ) ; if ( dist < thold ) { memDist . push ( new RemovedColor ( j , pxj , dist ) ) ; usageArray [ j ] = 0 ; palLen -- ; } } } thold += palLen > this . _colors * 3 ? this . _initialDistance : this . _distanceIncrement ; } if ( palLen < this . _colors ) { stableSort ( memDist , ( a , b ) => b . distance - a . distance ) ; let k = 0 ; while ( palLen < this . _colors && k < memDist . length ) { const removedColor = memDist [ k ] ; usageArray [ removedColor . index ] = 1 ; palLen ++ ; k ++ ; } } let colors = colorArray . length ; for ( let colorIndex = colors - 1 ; colorIndex >= 0 ; colorIndex -- ) { if ( usageArray [ colorIndex ] === 0 ) { if ( colorIndex !== colors - 1 ) { colorArray [ colorIndex ] = colorArray [ colors - 1 ] ; } -- colors ; } } colorArray . length = colors ; palette . sort ( ) ; yield { palette , progress : 100 , } ; } }