'From Squeak3.2alpha of 2 October 2001 [latest update: #4445] on 28 October 2001 at 11:10:40 am'! "Change Set: ReducePalette Date: 17 October 2001 Author: Dan Ingalls Adds an option to 'reduce color palette' in the SketchMorph 'paint...' menu. In this manner images may be compressed much better, and will work much better with the paint can and select-by-flood tools. Note: this has not been engineered. There are much fancier solutions in the literature. "! !Form methodsFor: 'image manipulation' stamp: 'di 10/16/2001 19:23'! copyWithColorsReducedTo: nColors "Note: this has not been engineered. There are better solutions in the literature." | palette colorMap pc closest | palette _ self reducedPaletteOfSize: nColors. colorMap _ (1 to: (1 bitShift: depth)) collect: [:i | pc _ Color colorFromPixelValue: i-1 depth: depth. closest _ palette detectMin: [:c | c diff: pc]. closest pixelValueForDepth: depth]. ^ self deepCopy copyBits: self boundingBox from: self at: 0@0 colorMap: (colorMap as: Bitmap) ! ! !Form methodsFor: 'image manipulation' stamp: 'di 10/16/2001 15:23'! reducedPaletteOfSize: nColors "Return an array of colors of size nColors, such that those colors represent well the pixel values actually found in this form." | threshold tallies colorTallies dist delta palette cts top cluster | tallies _ self tallyPixelValues. "An array of tallies for each pixel value" threshold _ width * height // 500. "Make an array of (color -> tally) for all tallies over threshold" colorTallies _ Array streamContents: [:s | tallies withIndexDo: [:v :i | v >= threshold ifTrue: [s nextPut: (Color colorFromPixelValue: i-1 depth: depth) -> v]]]. "Extract a set of clusters by picking the top tally, and then removing all others whose color is within dist of it. Iterate the process, adjusting dist until we get nColors." dist _ 0.2. delta _ dist / 2. [cts _ colorTallies copy. palette _ Array streamContents: [:s | [cts isEmpty] whileFalse: [top _ cts detectMax: [:a | a value]. cluster _ cts select: [:a | (a key diff: top key) < dist]. s nextPut: top key -> (cluster detectSum: [:a | a value]). cts _ cts copyWithoutAll: cluster]]. palette size = nColors or: [delta < 0.001]] whileFalse: [palette size > nColors ifTrue: [dist _ dist + delta] ifFalse: [dist _ dist - delta]. delta _ delta / 2]. ^ palette collect: [:a | a key] ! ! !Morph methodsFor: 'menus' stamp: 'di 10/20/2001 22:12'! addPaintingItemsTo: aMenu hand: aHandMorph | subMenu movies | subMenu _ MenuMorph new defaultTarget: self. subMenu add: 'repaint' action: #editDrawing. subMenu add: 'set rotation center' action: #setRotationCenter. subMenu add: 'reset forward-direction' action: #resetForwardDirection. subMenu add: 'set rotation style' action: #setRotationStyle. subMenu add: 'erase pixels of color' action: #erasePixelsOfColor:. subMenu add: 'recolor pixels of color' action: #recolorPixelsOfColor:. subMenu add: 'reduce color palette' action: #reduceColorPalette:. subMenu add: 'add a border around this shape...' action: #addBorderToShape:. movies _ (self world rootMorphsAt: aHandMorph targetOffset) select: [:m | (m isKindOf: MovieMorph) or: [m isKindOf: SketchMorph]]. (movies size > 1) ifTrue: [subMenu add: 'insert into movie' action: #insertIntoMovie:]. aMenu add: 'painting...' subMenu: subMenu! ! !SketchMorph methodsFor: 'menu' stamp: 'di 10/17/2001 13:02'! reduceColorPalette: evt "Let the user ask for a reduced number of colors in this sketch" | str nColors | str _ FillInTheBlank request: 'Please enter a number greater than one. (note: this cannot be undone, so answer zero to abort if you need to make a backup first)' initialAnswer: '0'. nColors _ Integer readFrom: (ReadStream on: str). (nColors between: 2 and: 256) ifFalse: [^ self]. originalForm _ originalForm copyWithColorsReducedTo: nColors. rotatedForm _ nil. self changed! !