From VisualWorks®, 7.6 of February 1, 2008 on February 1, 2008 at 12:44:58 pm (Dialog confirm: 'You are filing-in a Parcel source file!\\While this is possible it will not have\the same effect as loading the parcel.\None of the Parcel''s prerequisites will\be loaded and none of its load actions\will be performed.\\Are you sure you want to file-in?' withCRs) ifFalse: [self error: 'Parcel file-in abandoned. Choose terminate or close.'] <name>GIFEncoder</name> <environment>Smalltalk</environment> <private>false</private> <imports> private Smalltalk.* </imports> <category>(none)</category> <attributes> <package>GIFEncoder</package> </attributes> <class> <name>BitmapImageEncoder</name> <environment>GIFEncoder</environment> <super>Core.Object</super> <private>false</private> none stream <imports></imports> <category>Wave-Support-Bitmaps</category> <attributes> <package>GIFEncoder</package> </attributes> </class> <comment> GIFEncoder.BitmapImageEncoder
BitmapImageEncoder is an abstract class to provide for encoding and/or decoding an image on a stream. Subclasses must implement the following messages: accessing nextImage nextPutImage: Instance Variables: stream <ReadStream | WriteStream> stream for image storages Shared Variables: ImageNotStoredSignal <Signal> image not stored error signal MagicNumberErrorSignal <Signal> magic number error signal
</comment> <class> <name>GUFEncoder</name> <environment>GIFEncoder</environment> <super>GIFEncoder.BitmapImageEncoder</super> <private>false</private> none width height bitsPerPixel colorPalette rowByteSize xpos ypos pass interlace codeSize clearCode eoiCode freeCode maxCode prefixTable suffixTable remainBitCount bufByte bufStream transparentPixel <imports></imports> <category>Wave-Support-Bitmaps</category> <attributes> <package>GIFEncoder</package> </attributes> </class> <comment> GIFEncoder.GUFEncoder
GUFEncoder provides for encoding and/or decoding an image on a stream using the Graphic Interchange Format. GIF and 'Graphics Interchange Format' are trademarks of CompuServe, Incorporated. GUF is the Uncompressed version of GIF. The decoder algorithm in this class uses an uncompressed GIF-compatible format that does not require the licensing of the GIF LZW compression algorithm from Unisys. These bitmaps render properly in well-implemented GIF viewers (including almost all web browsers) but are typically anywhere from 2 to 20 times as large as compressed GIFs. A subclass, GIFEncoder, should contain the LZW encoding and decoding algorithms. In VisualWave 2.0, the default encoder for a GIFImage is the GUFEncoder and the default decoder for a GIFImage is the GIFEncoder. The LZW compression capability is covered by Unisys Patent No. 4,588,302. Because of licensing restrictions, the LZW encoding algorithm is not actually used in the GIFEncoder class in VisualWave 2.0. (GIFEncoder>writeBitData: uses the uncompressed algorithm from its superclass). After signing a license agreement with UniSys for the LZW compression capability, contact ParcPlace technical support for the true GIF compressed LZW encoding implementation. Extended Aug 1995 to handle GIF89a GraphicsControlExtensionBlocks and thus to some extent transparency. Instance Variables: width <SmallInteger> width of image height <SmallInteger> height of image bitsPerPixel <SmallInteger> number of bits per pixel of image colorPalette <MappedPalette> color palette of image rowByteSize <SmallInteger> row byte size of bits of image xpos <SmallInteger> x position of bits of image ypos <SmallInteger> y position of bits of image pass <SmallInteger> pass count used at interlace mode interlace <Boolean> each image row is interlaced or sequential codeSize <SmallInteger> code bits size clearCode <SmallInteger> special clear code of GIF spec. eoiCode <SmallInteger> special end of information code of GIF spec. freeCode <SmallInteger> first available code maxCode <SmallInteger> max code value prefixTable <Array of: SmallInteger> string prefix table suffixTable <Array of: SmallInteger> string suffix table remainBitCount <SmallInteger> number of unaccessed bits for bits accessing bufByte <SmallInteger> buffer byte for bits accessing bufStream <ReadStream | WriteStream> buffer stream for packing transparentPixel <SmallInteger> pixel value that should be considered transparent Shared Variables: Extension <SmallInteger> GIF Extension ImageSeparator <SmallInteger> GIF Image Separator Terminator <SmallInteger> GIF Terminator GraphicControlLabel <SmallInteger> GIF GraphicControlBlock indicator
</comment> <class> <name>GIFEncoder</name> <environment>GIFEncoder</environment> <super>GIFEncoder.GUFEncoder</super> <private>false</private> none <imports></imports> <category>Wave-Support-Bitmaps</category> <attributes> <package>GIFEncoder</package> </attributes> </class> <comment> GIFEncoder.GIFEncoder
GIFEncoder is supposed to encode and decode GIFImages with the LZW encoding and decoding GIF algorithms. GIF and 'Graphics Interchange Format' are trademarks of CompuServe, Incorporated. The LZW compression capability is covered by Unisys Patent No. 4,588,302. Because of licensing restrictions, the LZW encoding algorithm is not actually used in the default GIFEncoder class in VisualWave 2.0. (GIFEncoder>writeBitData: uses the uncompressed algorithm from its superclass). After signing a license agreement with UniSys for the LZW compression capability you may file in the true GIF compressed LZW encoding implementation from a file in the extras directory. See the superclass comment for more information.
</comment> <class> <name>GIFImage</name> <environment>GIFEncoder</environment> <super>Graphics.PixelArray</super> <private>false</private> none gifBytes height width bitsPerPixel palette cachedVisual <imports></imports> <category>Wave-Support-Bitmaps</category> <attributes> <package>GIFEncoder</package> </attributes> </class> <comment> GIFEncoder.GIFImage
GIFImage represents a bitmap image in the "Graphics Interchange Format" commonly used on the Internet. Notable features of GIFs: - use a lossless compression technique for compactness - support the idea of a "transparent" pixel value, where any backdrop shows through - support bit depths of 1, 2, 4, and 8 To display on screen, a GIFImage must go through a time-consuming conversion process, so a cached version of the native visual is kept around. Instance Variables: gifBytes <ByteArray> the raw bytes of a GIF-formatted image height <Integer> width <Integer> bitsPerPixel <Integer> palette <Palette> cachedVisual <Image | OpaqueImage> an OpaqueImage is used when there is a transparent bit Shared Variables: OnePixelTemplate <ByteArray> a 1@1 GIF that can easily be changed to a desired color TransparentOnePixelGIF <GIFImage> a 1@1 transparent GIF PreferredEncoderClass <GIFEncoder > preferred encoder class PreferredDecoderClass <GIFEncoder> preferred decoder class
</comment> <name>PreferredEncoderClass</name> <environment>GIFEncoder.GIFImage</environment> <private>false</private> <constant>false</constant> <category>As yet unclassified</category> <attributes> <package>GIFEncoder</package> </attributes> <name>OnePixelTemplate</name> <environment>GIFEncoder.GIFImage</environment> <private>false</private> <constant>false</constant> <category>As yet unclassified</category> <attributes> <package>GIFEncoder</package> </attributes> <name>TransparentOnePixelGIF</name> <environment>GIFEncoder.GIFImage</environment> <private>false</private> <constant>false</constant> <category>As yet unclassified</category> <attributes> <package>GIFEncoder</package> </attributes> <name>PreferredDecoderClass</name> <environment>GIFEncoder.GIFImage</environment> <private>false</private> <constant>false</constant> <category>As yet unclassified</category> <attributes> <package>GIFEncoder</package> </attributes> <name>ImageNotStoredSignal</name> <environment>GIFEncoder.BitmapImageEncoder</environment> <private>false</private> <constant>false</constant> <category>As yet unclassified</category> <attributes> <package>GIFEncoder</package> </attributes> <name>MagicNumberErrorSignal</name> <environment>GIFEncoder.BitmapImageEncoder</environment> <private>false</private> <constant>false</constant> <category>As yet unclassified</category> <attributes> <package>GIFEncoder</package> </attributes> <name>GraphicControlLabel</name> <environment>GIFEncoder.GUFEncoder</environment> <private>false</private> <constant>false</constant> <category>As yet unclassified</category> <attributes> <package>GIFEncoder</package> </attributes> <name>Terminator</name> <environment>GIFEncoder.GUFEncoder</environment> <private>false</private> <constant>false</constant> <category>As yet unclassified</category> <attributes> <package>GIFEncoder</package> </attributes> <name>Extension</name> <environment>GIFEncoder.GUFEncoder</environment> <private>false</private> <constant>false</constant> <category>As yet unclassified</category> <attributes> <package>GIFEncoder</package> </attributes> <name>ImageSeparator</name> <environment>GIFEncoder.GUFEncoder</environment> <private>false</private> <constant>false</constant> <category>As yet unclassified</category> <attributes> <package>GIFEncoder</package> </attributes> <methods> GIFEncoder.BitmapImageEncoder <category>stream access</category>
atEnd ^stream atEnd
close stream close
contents ^stream contents
cr ^stream nextPut: Character cr asInteger
lf "PPM and PBM are used LF as CR." ^stream nextPut: Character lf asInteger
next ^stream next
next: size ^stream next: size
nextLong "Read a 32-bit quantity from the input stream." ^(stream next bitShift: 24) + (stream next bitShift: 16) + (stream next bitShift: 8) + stream next
nextLongPut: a32BitW "Write out a 32-bit integer as 32 bits." stream nextPut: ((a32BitW bitShift: -24) bitAnd: 16rFF). stream nextPut: ((a32BitW bitShift: -16) bitAnd: 16rFF). stream nextPut: ((a32BitW bitShift: -8) bitAnd: 16rFF). stream nextPut: (a32BitW bitAnd: 16rFF). ^a32BitW
nextPut: aByte ^stream nextPut: aByte
nextPutAll: aByteArray ^stream nextPutAll: aByteArray
nextWord "Read a 16-bit quantity from the input stream." ^(stream next bitShift: 8) + stream next
nextWordPut: a16BitW "Write out a 16-bit integer as 16 bits." stream nextPut: ((a16BitW bitShift: -8) bitAnd: 16rFF). stream nextPut: (a16BitW bitAnd: 16rFF). ^a16BitW
position ^stream position
position: anInteger ^stream position: anInteger
size ^stream size
skip: anInteger ^stream skip: anInteger
space ^stream nextPut: Character space asInteger
tab ^stream nextPut: Character tab asInteger
</methods> <methods> GIFEncoder.BitmapImageEncoder <category>private</category>
changePadOfBits: bits width: width height: height depth: depth from: oldPad to: newPad "Change padding size of bits." | srcRowByteSize dstRowByteSize newBits srcRowBase rowEndOffset | (#(8 16 32) includes: oldPad) ifFalse: [^self error: ((#InvalidPad1s << #WaveServerDialogs >> 'Invalid pad: <1s>') expandMacrosWith: oldPad printString)]. (#(8 16 32) includes: newPad) ifFalse: [^self error: ((#InvalidPad1s << #WaveServerDialogs >> 'Invalid pad: <1s>') expandMacrosWith: newPad printString)]. srcRowByteSize := width * depth + oldPad - 1 // oldPad * (oldPad / 8). srcRowByteSize * height = bits size ifFalse: [^self error: (#IncorrectBitmapArraySize << #WaveServerDialogs >> 'Incorrect bitmap array size.')]. dstRowByteSize := width * depth + newPad - 1 // newPad * (newPad / 8). newBits := ByteArray new: dstRowByteSize * height. srcRowBase := 1. rowEndOffset := dstRowByteSize - 1. 1 to: newBits size by: dstRowByteSize do: [:dstRowBase | newBits replaceFrom: dstRowBase to: dstRowBase + rowEndOffset with: bits startingAt: srcRowBase. srcRowBase := srcRowBase + srcRowByteSize]. ^newBits
colorValueFrom: rgbInteger "Answer a colorValue from a 3 byte integer that represents RGB." | scalingValue | scalingValue := ColorValue scalingValue. ^ColorValue scaledRed: (self scalingValue: ((rgbInteger bitShift: -16) bitAnd: 255) from: 255 to: scalingValue) scaledGreen: (self scalingValue: ((rgbInteger bitShift: -8) bitAnd: 255) from: 255 to: scalingValue) scaledBlue: (self scalingValue: (rgbInteger bitAnd: 255) from: 255 to: scalingValue)
hasMagicNumber: aByteArray | position | position := stream position. ((stream size - position) >= aByteArray size and: [(stream next: aByteArray size) = aByteArray]) ifTrue: [^true]. stream position: position. ^false
imageNotStoredError ^self class imageNotStoredSignal raise
magicNumberError ^self class magicNumberErrorSignal raise
packBits: bits "Macintosh PackBits scheme' Best to encode a 3 byte repeat run as a replicate run. See unPackBits: method." | packStream prev writeBlock bitSize bitPos start code replicateSize literalSize | packStream := WriteStream on: (ByteArray new: bits size). prev := nil. writeBlock := [:asc | asc key < 0 ifTrue: "replicate" [packStream nextPut: asc key negated. packStream nextPut: asc value] ifFalse: "literal" [| litStart litStop | litStart := asc value first. litStop := asc value last. asc key = (litStop - litStart) ifFalse: [self error: (#cantHappen << #WaveServerDialogs >> 'can''t happen')]. [litStop - litStart + 1 > 128] whileTrue: [packStream nextPut: 127. litStart to: litStart + 127 do: [:litIndex | packStream nextPut: (bits at: litIndex)]. litStart := litStart + 128]. litStart <= litStop ifTrue: [packStream nextPut: litStop - litStart + 1 - 1. litStart to: litStop do: [:litIndex | packStream nextPut: (bits at: litIndex)]]]]. bitSize := bits size. bitPos := 1. [bitPos <= bitSize] whileTrue: [start := bitPos. code := bits at: start. [(bitPos := bitPos + 1) <= bitSize and: [(bits at: bitPos) = code]] whileTrue: []. replicateSize := bitPos - start. replicateSize > 128 ifTrue: [prev == nil ifFalse: [writeBlock value: prev. prev := nil]. [replicateSize > 128] whileTrue: [writeBlock value: -129 -> code. start := start + 128. replicateSize := replicateSize - 128]]. replicateSize = 2 "treat as literal" ifTrue: [literalSize := 2. prev ~~ nil ifTrue: [prev key >= 0 "prev is literal" ifTrue: [literalSize := literalSize + prev value size. start := prev value first] ifFalse: [writeBlock value: prev. prev := nil]]. prev := (literalSize - 1) -> (start to: start + literalSize - 1)] ifFalse: [replicateSize > 2 ifTrue: [prev == nil ifFalse: [writeBlock value: prev. prev := nil]. prev := (256 - (replicateSize - 1)) negated -> code] ifFalse: "replicateSize < 2" [bitPos := bitPos - 1]]. (start := bitPos) <= bitSize ifTrue: [code := bits at: start. [(bitPos := bitPos + 1) <= bitSize and: [(bits at: bitPos) ~= code]] whileTrue: [code := bits at: bitPos]. bitPos <= bitSize ifTrue: [bitPos := bitPos - 1]. literalSize := bitPos - start. literalSize > 0 ifTrue: [prev ~~ nil ifTrue: [prev key >= 0 "prev is literal" ifTrue: [literalSize := literalSize + prev value size. start := prev value first] ifFalse: [writeBlock value: prev. prev := nil]]. prev := (literalSize - 1) -> (start to: start + literalSize - 1)]]]. prev == nil ifFalse: [writeBlock value: prev. prev := nil]. ^packStream contents
packBits: bits depthFrom8To: depth with: width height: height pad: pad "Pack bits of depth 8 image to it of depth 1, 2, or 4 image." | maxPixelVal pixelInByte bitsWidth pBitsWidth pBits | (#(1 2 4) includes: depth) ifFalse: [^self error: (#depthMustBe12Or4 << #WaveServerDialogs >> 'depth must be 1, 2, or 4')]. (#(8 16 32) includes: pad) ifFalse: [^self error: (#padMustBe816Or32 << #WaveServerDialogs >> 'pad must be 8, 16, or 32')]. maxPixelVal := (1 bitShift: depth) - 1. pixelInByte := 8 / depth. bitsWidth := width * 8 + pad - 1 // pad * (pad / 8). pBitsWidth := width * depth + pad - 1 // pad * (pad / 8). pBits := ByteArray new: pBitsWidth * height. 1 to: height do: [:i | | bitIndex pBitIndex pixelVal count | bitIndex := i - 1 * bitsWidth. pBitIndex := i - 1 * pBitsWidth. pixelVal := 0. count := 0. 1 to: width do: [:j | | val | val := bits at: (bitIndex := bitIndex + 1). val > maxPixelVal ifTrue: [^self error: (#cantPackBits << #WaveServerDialogs >> 'can''t pack bits')]. pixelVal := (pixelVal bitShift: depth) + val. (count := count + 1) >= pixelInByte ifTrue: [pBits at: (pBitIndex := pBitIndex + 1) put: pixelVal. pixelVal := 0. count := 0]]. count > 0 ifTrue: [pBits at: (pBitIndex := pBitIndex + 1) put: (pixelVal bitShift: depth * (pixelInByte - count))]]. ^pBits
rgbIntegerArrayFor: aPalette | colors | colors := Array new: aPalette maxIndex + 1. 1 to: colors size do: [:i | colors at: i put: (self rgbIntegerFrom: (aPalette at: i - 1 ifAbsent: [ColorValue white]))]. ^colors
rgbIntegerFrom: aColorValue "Answer a 3 byte integer that represents RGB from a colorValue." | scalingValue | scalingValue := ColorValue scalingValue. ^((self scalingValue: aColorValue scaledRed from: scalingValue to: 255) bitShift: 16) + ((self scalingValue: aColorValue scaledGreen from: scalingValue to: 255) bitShift: 8) + (self scalingValue: aColorValue scaledBlue from: scalingValue to: 255)
runLengthCompressBits: bits "'Run-length compression scheme' Run-length compression algorithm is: Loop until you get the number of unpacked bytes you are expecting: Read the next source byte into n. If n is not 128, copy n as 1 byte. Else if n is 128: Read the next source byte into n. If n is 0, copy 128 as 1 byte. Else if n is not 0, copy the next byte n+1 times. Endloop" | bitsStream compressStream count code prevCode | bitsStream := ReadStream on: bits. compressStream := WriteStream on: (ByteArray new: bits size). count := 0. prevCode := nil. [bitsStream atEnd] whileFalse: [code := bitsStream next. (count > 0 and: [prevCode ~= code]) ifTrue: [(count = 1 and: [prevCode = 128]) ifTrue: [compressStream nextPut: 128; nextPut: 0] ifFalse: [(count > 2 or: [prevCode = 128]) ifTrue: [compressStream nextPut: 128; nextPut: count - 1; nextPut: prevCode] ifFalse: [1 to: count do: [:i | compressStream nextPut: prevCode]]]. count := 0]. prevCode := code. (count := count + 1) == 256 ifTrue: [compressStream nextPut: 128; nextPut: count - 1; nextPut: code. count := 0]]. count > 0 ifTrue: [(count = 1 and: [code = 128]) ifTrue: [compressStream nextPut: 128; nextPut: 0] ifFalse: [(count > 2 or: [code = 128]) ifTrue: [compressStream nextPut: 128; nextPut: count - 1; nextPut: code] ifFalse: [1 to: count do: [:i | compressStream nextPut: code]]]]. ^compressStream contents
runLengthDecompressBits: bits "'Run-length compression scheme' Run-length compression algorithm is: Loop until you get the number of unpacked bytes you are expecting: Read the next source byte into n. If n is not 128, copy n as 1 byte. Else if n is 128: Read the next source byte into n. If n is 0, copy 128 as 1 byte. Else if n is not 0, copy the next byte n+1 times. Endloop" | bitsStream uncompressStream | bitsStream := ReadStream on: bits. uncompressStream := WriteStream on: (ByteArray new: bits size * 3 // 2). [bitsStream atEnd] whileFalse: [| code | (code := bitsStream next) ~= 128 ifTrue: [uncompressStream nextPut: code] ifFalse: [(code := bitsStream next) = 0 ifTrue: [uncompressStream nextPut: 128] ifFalse: [| byte | byte := bitsStream next. 1 to: code + 1 do: [:i | uncompressStream nextPut: byte]]]]. ^uncompressStream contents
scalingValue: value from: fromScale to: toScale ^value = 0 ifTrue: [0] ifFalse: [value = fromScale ifTrue: [toScale] ifFalse: [(value + 1 * (toScale + 1) / (fromScale + 1)) rounded - 1 max: 0]]
unPackBits: bits "'Macintosh PackBits scheme' Unpack algorithm is: Loop until you get the number of unpacked bytes you are expecting: Read the next source byte into n. If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. Else if n is 128, noop. Endloop" | unpackStream bitSize bitPos code | unpackStream := WriteStream on: (ByteArray new: bits size). bitSize := bits size. bitPos := 0. [(bitPos := bitPos + 1) <= bitSize] whileTrue: [code := bits at: bitPos. code < 128 ifTrue: [1 to: code + 1 do: [:i | unpackStream nextPut: (bits at: (bitPos := bitPos + 1))]] ifFalse: [code > 128 ifTrue: [bitPos := bitPos + 1. 1 to: 256 - code + 1 do: [:i | unpackStream nextPut: (bits at: bitPos)]]]]. ^unpackStream contents
unpackBits: bits depthTo8From: depth with: width height: height pad: pad "Unpack bits of depth 1, 2, or 4 image to it of depth 8 image." | bitMask pixelInByte bitsWidth upBitsWidth stopWidth trailingSize upBits | (#(1 2 4) includes: depth) ifFalse: [^self error: (#depthMustBe12Or4 << #WaveServerDialogs >> 'depth must be 1, 2, or 4')]. (#(8 16 32) includes: pad) ifFalse: [^self error: (#padMustBe816Or32 << #WaveServerDialogs >> 'pad must be 8, 16, or 32')]. bitMask := (1 bitShift: depth) - 1. pixelInByte := 8 / depth. bitsWidth := width * depth + pad - 1 // pad * (pad / 8). upBitsWidth := width * 8 + pad - 1 // pad * (pad / 8). stopWidth := width * depth + 7 // 8. trailingSize := width - (stopWidth - 1 * pixelInByte). upBits := ByteArray new: upBitsWidth * height. 1 to: height do: [:i | | bitIndex upBitIndex val | bitIndex := i - 1 * bitsWidth. upBitIndex := i - 1 * upBitsWidth. 1 to: stopWidth - 1 do: [:j | val := bits at: (bitIndex := bitIndex + 1). upBitIndex := upBitIndex + pixelInByte. 1 to: pixelInByte do: [:k | upBits at: (upBitIndex - k + 1) put: (val bitAnd: bitMask). val := val bitShift: depth negated]]. val := (bits at: (bitIndex := bitIndex + 1)) bitShift: depth negated * (pixelInByte - trailingSize). upBitIndex := upBitIndex + trailingSize. 1 to: trailingSize do: [:k | upBits at: (upBitIndex - k + 1) put: (val bitAnd: bitMask). val := val bitShift: depth negated]]. ^upBits
</methods> <methods> GIFEncoder.BitmapImageEncoder <category>accessing</category>
nextImage "Dencoding an image on stream and answer the image." ^self subclassResponsibility
nextPutImage: anImage "Encoding anImage on stream." ^self subclassResponsibility
</methods> <methods> GIFEncoder.BitmapImageEncoder <category>initialize-release</category>
on: aStream stream := aStream. (stream respondsTo: #binary) ifTrue: [stream binary]
</methods> <methods> GIFEncoder.BitmapImageEncoder class <category>signal constants</category>
imageNotStoredSignal ^ImageNotStoredSignal
magicNumberErrorSignal ^MagicNumberErrorSignal
</methods> <methods> GIFEncoder.BitmapImageEncoder class <category>class initialization</category>
initialize self initializeSignals
initializeSignals "BitmapImageEncoder initializeSignals" MagicNumberErrorSignal := Object errorSignal newSignal notifierString: (#InvalidMagicNumber << #WaveServerDialogs >> 'Invalid magic number'); nameClass: self message: #magicNumberErrorSignal. ImageNotStoredSignal := Object errorSignal newSignal notifierString: (#ImageNotStored << #WaveServerDialogs >> 'Image not stored'); nameClass: self message: #imageNotStoredSignal.
</methods> <methods> GIFEncoder.BitmapImageEncoder class <category>instance creation</category>
on: aStream "Answer an instance of the receiver for encoding and/or decoding images on aStream." ^self new on: aStream
</methods> <methods> GIFEncoder.GUFEncoder <category>accessing</category>
bitsPerPixel ^bitsPerPixel
colorPalette ^colorPalette
height ^height
nextImage | bits depth image | [self readHeader. bits := self readBitData. ] ensure: [self close]. depth := bitsPerPixel > 8 ifTrue: [^self error: (#cantHappen << #WaveServerDialogs >> 'can''t happen')] ifFalse: [bitsPerPixel = 1 ifTrue: [1] ifFalse: [bitsPerPixel = 2 ifTrue: [2] ifFalse: [bitsPerPixel <= 4 ifTrue: [4] ifFalse: [8]]]]. depth < 8 ifTrue: [bits := self packBits: bits depthFrom8To: depth with: width height: height pad: 8]. image := Image extent: width@height depth: depth palette: colorPalette bits: bits pad: 8. transparentPixel notNil ifTrue: [ image := OpaqueImage figure: image shape: (CachedImage on: (image convertToCoverageWithTransparentPixel: transparentPixel) asRetainedMedium)]. ^ image.
nextPutImage: anImage | bits trueBitsPerPixel achievableBitsPerPixel | anImage bitsPerPixel > 8 ifTrue: [^self imageNotStoredError]. width := anImage width. height := anImage height. colorPalette := anImage palette. trueBitsPerPixel := anImage bitsPerPixel. achievableBitsPerPixel := (2 raisedTo: ((((colorPalette size - 1) highBit -1) highBit))). bitsPerPixel := achievableBitsPerPixel. colorPalette := anImage palette. colorPalette paintBasis == CoverageValue ifTrue: [ transparentPixel := 0. colorPalette := colorPalette asMappedPalette. ]. bits := anImage bits. trueBitsPerPixel < 8 ifTrue: [bits := self unpackBits: bits depthTo8From: bitsPerPixel with: anImage width height: anImage height pad: 32]. interlace := true. [self writeHeader. self writeBitData: bits. ] ensure: [self close]. ^anImage "| bits | anImage bitsPerPixel > 8 ifTrue: [^self imageNotStoredError]. width := anImage width. height := anImage height. bitsPerPixel := anImage bitsPerPixel. colorPalette := anImage palette. bits := anImage bits. bitsPerPixel < 8 ifTrue: [bits := self unpackBits: bits depthTo8From: bitsPerPixel with: anImage width height: anImage height pad: 32]. interlace := true. [self writeHeader. self writeBitData: bits. ] valueNowOrOnUnwindDo: [self close]. ^anImage"
width ^width
</methods> <methods> GIFEncoder.GUFEncoder <category>private</category>
checkCodeSize (freeCode > maxCode and: [codeSize < 12]) ifTrue: [codeSize := codeSize + 1. maxCode := (1 bitShift: codeSize) - 1]
setParameters: initCodeSize clearCode := 1 bitShift: initCodeSize. eoiCode := clearCode + 1. freeCode := clearCode + 2. codeSize := initCodeSize + 1. maxCode := (1 bitShift: codeSize) - 1
updatePixelPosition (xpos := xpos + 1) >= width ifFalse: [^self]. xpos := 0. interlace ifFalse: [ypos := ypos + 1. ^self]. pass = 0 ifTrue: [(ypos := ypos + 8) >= height ifTrue: [pass := pass + 1. ypos := 4]. ^self]. pass = 1 ifTrue: [(ypos := ypos + 8) >= height ifTrue: [pass := pass + 1. ypos := 2]. ^self]. pass = 2 ifTrue: [(ypos := ypos + 4) >= height ifTrue: [pass := pass + 1. ypos := 1]. ^self]. pass = 3 ifTrue: [ypos := ypos + 2. ^self]. ^self error: (#cantHappen << #WaveServerDialogs >> 'can''t happen')
</methods> <methods> GIFEncoder.GUFEncoder <category>private-packing</category>
fillBuffer | packSize | packSize := self next. bufStream := ReadStream on: (self next: packSize)
flushBuffer bufStream isEmpty ifTrue: [^self]. self nextPut: bufStream size. self nextPutAll: bufStream contents. bufStream := WriteStream on: (ByteArray new: 256)
nextByte bufStream atEnd ifTrue: [self atEnd ifTrue: [^nil]. self fillBuffer]. ^bufStream next
nextBytePut: aByte bufStream nextPut: aByte. bufStream size >= 254 ifTrue: [self flushBuffer]
peekByte bufStream atEnd ifTrue: [self atEnd ifTrue: [^nil]. self fillBuffer]. ^bufStream peek
</methods> <methods> GIFEncoder.GUFEncoder <category>private-bits access</category>
flushBits remainBitCount = 0 ifFalse: [self nextBytePut: bufByte. remainBitCount := 0]. self flushBuffer
nextBits | integer readBitCount shiftCount | integer := 0. remainBitCount = 0 ifTrue: [readBitCount := 8. shiftCount := 0] ifFalse: [readBitCount := remainBitCount. shiftCount := remainBitCount - 8]. [readBitCount < codeSize] whileTrue: [self peekByte isNil ifTrue: [^eoiCode]. integer := integer + (self nextByte bitShift: shiftCount). shiftCount := shiftCount + 8. readBitCount := readBitCount + 8]. self peekByte isNil ifTrue: [^eoiCode]. (remainBitCount := readBitCount - codeSize) = 0 ifTrue: [integer := integer + (self nextByte bitShift: shiftCount)] ifFalse: [integer := integer + (self peekByte bitShift: shiftCount)]. ^integer bitAnd: maxCode
nextBitsPut: anInteger | integer writeBitCount shiftCount | shiftCount := 0. remainBitCount = 0 ifTrue: [writeBitCount := 8. integer := anInteger] ifFalse: [writeBitCount := remainBitCount. integer := bufByte + (anInteger bitShift: 8 - remainBitCount)]. [writeBitCount < codeSize] whileTrue: [self nextBytePut: ((integer bitShift: shiftCount) bitAnd: 255). shiftCount := shiftCount - 8. writeBitCount := writeBitCount + 8]. (remainBitCount := writeBitCount - codeSize) = 0 ifTrue: [self nextBytePut: (integer bitShift: shiftCount)] ifFalse: [bufByte := integer bitShift: shiftCount]. ^anInteger
</methods> <methods> GIFEncoder.GUFEncoder <category>private-encoding</category>
flushCode self flushBits
readPixelFrom: bits | pixel | ypos >= height ifTrue: [^nil]. pixel := bits at: (ypos * rowByteSize + xpos + 1). self updatePixelPosition. ^pixel
writeBitData: bits "avoiding Lempel-Ziv Welch algorithm." | initCodeSize pixel | pass := 0. xpos := 0. ypos := 0. rowByteSize := width * 8 + 31 // 32 * 4. remainBitCount := 0. bufByte := 0. bufStream := WriteStream on: (ByteArray new: 256). initCodeSize := bitsPerPixel <= 1 ifTrue: [2] ifFalse: [bitsPerPixel]. self nextPut: initCodeSize. self setParameters: initCodeSize. self writeCode: clearCode. [(pixel := self readPixelFrom: bits) == nil] whileFalse:[ self writeCode: pixel. freeCode := freeCode + 1. freeCode > maxCode ifTrue: [ self writeCode: clearCode. self setParameters: initCodeSize. ]. ]. self writeCode: eoiCode. self flushCode. self nextPut: 0. "zero-length packet" self nextPut: Terminator
writeCode: aCode self nextBitsPut: aCode
writeCodeAndCheckCodeSize: aCode self writeCode: aCode. self checkCodeSize
writeHeader | byte array | self nextPutAll: 'GIF89a' asByteArray. self writeWord: width. "Screen Width" self writeWord: height. "Screen Height" byte := 16r80. "has color map" byte := byte bitOr: ((bitsPerPixel - 1) bitShift: 5). "color resolution" byte := byte bitOr: bitsPerPixel - 1. "bits per pixel" self nextPut: byte. self nextPut: 0. "background color." self nextPut: 0. "null (future expansion)" array := self rgbIntegerArrayFor: colorPalette. array do: [:rgb | self nextPut: ((rgb bitShift: -16) bitAnd: 255); nextPut: ((rgb bitShift: -8) bitAnd: 255); nextPut: (rgb bitAnd: 255)]. array size + 1 to: (1 bitShift: bitsPerPixel) do: [:i | self nextPut: 0; nextPut: 0; nextPut: 0]. transparentPixel notNil ifTrue: [self writeGraphicControlExtensionBlock]. self nextPut: ImageSeparator. self writeWord: 0. "Image Left" self writeWord: 0. "Image Top" self writeWord: width. "Image Width" self writeWord: height. "Image Height" byte := interlace ifTrue: [16r40] ifFalse: [0]. self nextPut: byte.
writeWord: aWord self nextPut: (aWord bitAnd: 255). self nextPut: ((aWord bitShift: -8) bitAnd: 255). ^aWord
</methods> <methods> GIFEncoder.GUFEncoder <category>private-decoding</category>
readBitData "Not yet supported" self error: (#GUFEncoderCantReadGIFs << #WaveServerDialogs >> 'GUFEncoder can''t read GIFs')
readCode ^self nextBits
readHeader | byte hasColorMap | ((self hasMagicNumber: 'GIF89a' asByteArray) or: [self hasMagicNumber: 'GIF87a' asByteArray]) ifFalse: [^self magicNumberError]. self readWord. "skip Screen Width" self readWord. "skip Screen Height" byte := self next. hasColorMap := (byte bitAnd: 16r80) ~= 0. bitsPerPixel := (byte bitAnd: 7) + 1. byte := self next. "skip background color." byte := self next. "skip gif87 0 or gif89a pixel aspect ratio." hasColorMap ifTrue: [| array | array := Array new: (1 bitShift: bitsPerPixel). 1 to: array size do: [:i | array at: i put: (self colorValueFrom: (self next bitShift: 16) + (self next bitShift: 8) + self next)]. colorPalette := MappedPalette withColors: array] ifFalse: [Transcript cr; show: 'GIF file does not have a colormap.'. colorPalette := MappedPalette monochromeDefault]. [byte := self next. byte == Extension] whileTrue: [self readExtension]. byte = ImageSeparator ifFalse: [^self error: 'corrupt GIF file (nosep)']. self readWord. "skip Image Left" self readWord. "skip Image Top" width := self readWord. height := self readWord. interlace := (self next bitAnd: 16r40) ~= 0. "I ignore the possible existence of a local color map."
readWord ^self next + (self next bitShift: 8)
writePixel: pixel to: bits bits at: (ypos * rowByteSize + xpos + 1) put: pixel. self updatePixelPosition
</methods> <methods> GIFEncoder.GUFEncoder <category>private-GIF89a</category>
readExtension | label | label := self next. label == GraphicControlLabel ifTrue: [self readGraphicControlExtensionBlock] ifFalse: [self skipExtensionBlock].
readGraphicControlExtensionBlock | isTransparent | self next. "skip size" isTransparent := ((self next bitAnd: 1) = 1). self next: 2. "skip delay" isTransparent ifTrue: [transparentPixel := self next] ifFalse: [self next]. self next "skip zero terminator"
skipExtensionBlock | sz | sz := self next. self next: sz. self next.
transparentPixel ^transparentPixel
transparentPixel: aPixelValueOrNil "Ideally, the GIF encoder would realize when an OpaqueImage were passed in, set all the pixels covered by the OpaqueImage's shape to a previously unused pixel value (expanding the palette if necessary), and set that pixel value to be transparent, all automatically. In the meantime, we set transparent pixels manually. :-( " transparentPixel := aPixelValueOrNil.
writeGraphicControlExtensionBlock self nextPut: Extension. self nextPut: GraphicControlLabel. self nextPut: 4. transparentPixel notNil ifTrue: [self nextPut: 1] ifFalse: [self nextPut: 0]. self nextPut: 0. "write null delay" self nextPut: 0. transparentPixel notNil ifTrue: [self nextPut: transparentPixel] ifFalse: [self nextPut: 0]. self nextPut: 0. "terminator"
</methods> <methods> GIFEncoder.GUFEncoder class <category>class initialization</category>
initialize "GUFEncoder initialize." ImageSeparator := $, asInteger. Extension := $! asInteger. Terminator := $; asInteger. GraphicControlLabel := 16rF9.
</methods> <methods> GIFEncoder.GIFEncoder <category>private-decoding</category>
readBitData "using modified Lempel-Ziv Welch algorithm." | bits outCodes outCount bitMask initCodeSize code curCode oldCode inCode finChar | pass := 0. xpos := 0. ypos := 0. rowByteSize := width * 8 + 7 // 8. remainBitCount := 0. bufByte := 0. bufStream := ReadStream on: ByteArray new. bits := ByteArray new: width * height. outCodes := ByteArray new: 1025. outCount := 0. bitMask := (1 bitShift: bitsPerPixel) - 1. prefixTable := Array new: 4096. suffixTable := Array new: 4096. initCodeSize := self next. self setParameters: initCodeSize. [(code := self readCode) = eoiCode] whileFalse: [code = clearCode ifTrue: [self setParameters: initCodeSize. curCode := oldCode := code := self readCode. finChar := curCode bitAnd: bitMask. self writePixel: finChar to: bits] ifFalse: [curCode := inCode := code. curCode >= freeCode ifTrue: [curCode := oldCode. outCodes at: (outCount := outCount + 1) put: finChar]. [curCode > bitMask] whileTrue: [outCount > 1024 ifTrue: [^self error: (#corruptGIFFileOutCount << #WaveServerDialogs >> 'corrupt GIF file (OutCount)')]. outCodes at: (outCount := outCount + 1) put: (suffixTable at: curCode + 1). curCode := prefixTable at: curCode + 1]. finChar := curCode bitAnd: bitMask. outCodes at: (outCount := outCount + 1) put: finChar. outCount to: 1 by: -1 do: [:i | self writePixel: (outCodes at: i) to: bits]. outCount := 0. prefixTable at: freeCode + 1 put: oldCode. suffixTable at: freeCode + 1 put: finChar. oldCode := inCode. freeCode := freeCode + 1. self checkCodeSize]]. prefixTable := suffixTable := nil. ^bits
</methods> <methods> GIFEncoder.GIFEncoder <category>private-encoding</category>
writeBitData: bits "using modified Lempel-Ziv Welch algorithm." | maxBits maxMaxCode tSize initCodeSize ent tShift fCode pixel | pass := 0. xpos := 0. ypos := 0. rowByteSize := width * 8 + 31 // 32 * 4. remainBitCount := 0. bufByte := 0. bufStream := WriteStream on: (ByteArray new: 256). maxBits := 12. maxMaxCode := 1 bitShift: maxBits. tSize := 5003. prefixTable := Array new: tSize. suffixTable := Array new: tSize. initCodeSize := bitsPerPixel <= 1 ifTrue: [2] ifFalse: [bitsPerPixel]. self nextPut: initCodeSize. self setParameters: initCodeSize. tShift := 0. fCode := tSize. [fCode < 65536] whileTrue: [tShift := tShift + 1. fCode := fCode * 2]. tShift := 8 - tShift. 1 to: tSize do: [:i | suffixTable at: i put: -1]. self writeCodeAndCheckCodeSize: clearCode. ent := self readPixelFrom: bits. [(pixel := self readPixelFrom: bits) == nil] whileFalse: [| index disp nomatch | fCode := (pixel bitShift: maxBits) + ent. index := ((pixel bitShift: tShift) bitXor: ent) + 1. (suffixTable at: index) = fCode ifTrue: [ent := prefixTable at: index] ifFalse: [nomatch := true. (suffixTable at: index) >= 0 ifTrue: [disp := tSize - index + 1. index = 1 ifTrue: [disp := 1]. "probe" [(index := index - disp) < 1 ifTrue: [index := index + tSize]. (suffixTable at: index) = fCode ifTrue: [ent := prefixTable at: index. nomatch := false. "continue whileFalse:"]. nomatch and: [(suffixTable at: index) > 0]] whileTrue: ["probe"]]. "nomatch" nomatch ifTrue: [self writeCodeAndCheckCodeSize: ent. ent := pixel. freeCode < maxMaxCode ifTrue: [prefixTable at: index put: freeCode. suffixTable at: index put: fCode. freeCode := freeCode + 1] ifFalse: [self writeCodeAndCheckCodeSize: clearCode. 1 to: tSize do: [:i | suffixTable at: i put: -1]. self setParameters: initCodeSize]]]]. prefixTable := suffixTable := nil. self writeCodeAndCheckCodeSize: ent. self writeCodeAndCheckCodeSize: eoiCode. self flushCode. self nextPut: 0. "zero-length packet" self nextPut: Terminator
</methods> <methods> GIFEncoder.GIFImage <category>converting</category>
asGIF ^self
asImage "Answer an Image that is like the receiver." | decoder | cachedVisual isNil ifTrue: [ decoder := (GIFImage preferredDecoderClass) on: (ReadStream on: gifBytes). cachedVisual := decoder nextImage ]. ^cachedVisual
asInternetMedia ^self
asRetainedMedium "Answer a retained medium (pixmap or mask) with my contents." ^self asImage asRetainedMedium
coercibleMediaType ^self mediaType
</methods> <methods> GIFEncoder.GIFImage <category>accessing</category>
bitsPerPixel " Answer the bitsPerPixel of the receiver. " bitsPerPixel isNil ifTrue: [self refreshProperties]. ^bitsPerPixel
height " Answer the height of the receiver. " height isNil ifTrue: [self refreshProperties]. ^height
width " Answer the width of the receiver. " width isNil ifTrue: [self refreshProperties]. ^width
</methods> <methods> GIFEncoder.GIFImage <category>private</category>
clearProperties height := width := palette := bitsPerPixel := nil.
gifBytes ^gifBytes
gifBytes: bytes gifBytes := bytes. self clearProperties.
refreshProperties | decoder | decoder := (GIFImage preferredDecoderClass) on: (ReadStream on: gifBytes). decoder readHeader. height := decoder height. width := decoder width. palette := decoder colorPalette. bitsPerPixel := decoder bitsPerPixel.
</methods> <methods> GIFEncoder.GIFImage <category>displaying</category>
displayOn: aGraphicsContext "Display the receiver on aGraphicsContext." ^self asImage displayOn: aGraphicsContext
displayOn: aGraphicsContext at: aPoint "Display the receiver on aGraphicsContext." ^self asImage displayOn: aGraphicsContext at: aPoint
</methods> <methods> GIFEncoder.GIFImage <category>internet media support</category>
mediaType ^'image/gif'
sendEntityOver: aStream aStream binary. aStream nextPutAll: gifBytes
sendHeadersOver: aStream "Only absolutely necessary header is Content-type" aStream nextPutAll: 'Content-Type: image/gif'; crlf. aStream nextPutAll: 'Content-Length: '; nextPutAll: gifBytes size printString; crlf.
sendOver: aStream self sendHeadersOver: aStream. aStream crlf. self sendEntityOver: aStream.
</methods> <methods> GIFEncoder.GIFImage class <category>instance creation</category>
from: bytes ^self new gifBytes: bytes.
fromSmalltalkBitmap: image ^image asGIF
</methods> <methods> GIFEncoder.GIFImage class <category>class initialization</category>
initialize "GIFImage initialize" GUFEncoder initialize. GIFEncoder initialize. PreferredEncoderClass := GIFEncoder. PreferredDecoderClass := GIFEncoder. TransparentOnePixelGIF := (self from: #[71 73 70 56 57 97 1 0 1 0 128 0 0 255 255 255 0 0 0 33 249 4 1 0 0 0 0 44 0 0 0 0 1 0 1 0 0 2 2 68 1 0 59]) refreshProperties. OnePixelTemplate := #[71 73 70 56 55 97 1 0 1 0 145 0 0 255 255 255 36 201 38 0 0 0 0 0 0 44 0 0 0 0 1 0 1 0 1 2 2 76 1 0 59].
uninitialize "GIFImage uninitialize" PreferredEncoderClass := GUFEncoder. PreferredDecoderClass := GIFEncoder. TransparentOnePixelGIF := nil. OnePixelTemplate := nil
</methods> <methods> GIFEncoder.GIFImage class <category>accessing</category>
preferredDecoderClass ^PreferredDecoderClass
preferredDecoderClass: aClass PreferredDecoderClass := aClass
preferredEncoderClass ^PreferredEncoderClass
preferredEncoderClass: aClass PreferredEncoderClass := aClass
preferredEncoderClassName ^self preferredEncoderClass name
preferredEncoderClassName: aSymbol | newClass | newClass := aSymbol asQualifiedReference valueOrDo: [self preferredEncoderClass]. self preferredEncoderClass: newClass.
</methods> <methods> GIFEncoder.GIFImage class <category>utility</category>
singlePixelOfColor: aColorValue | bytes | bytes := self singlePixelTemplate. bytes at: 17 put: (aColorValue red * 255) asInteger. bytes at: 18 put: (aColorValue green * 255) asInteger. bytes at: 19 put: (aColorValue blue * 255) asInteger. ^self from: bytes.
singlePixelTemplate ^OnePixelTemplate copy
singlePixelTransparent ^TransparentOnePixelGIF
</methods> <methods> Graphics.Image <category>converting</category>
asGIFNonTransparent | encoder image | image := (self bitsPerPixel < 8) ifTrue: [self] ifFalse: [self collapsedTo8BitsOrLess]. encoder := (GIFEncoder.GIFImage preferredEncoderClass) on: (WriteStream on: (ByteArray new: 1000)). "encoder transparentPixel: (image atPoint: 0@0)." encoder nextPutImage: image. ^GIFEncoder.GIFImage from: encoder contents.
asGIFWithTransparentPaint: aColorValue | encoder image | image := self bitsPerPixel <= 8 ifTrue: [self] ifFalse: [self collapsedTo8BitsOrLess]. encoder := GIFEncoder.GIFImage preferredEncoderClass on: (WriteStream on: (ByteArray new: 1000)). encoder transparentPixel: (image palette indexOfPaintNearest: aColorValue). encoder nextPutImage: image. ^GIFEncoder.GIFImage from: encoder contents
asGIFWithTransparentPixel: aPixelValue | encoder image | encoder := GIFEncoder.GIFImage preferredEncoderClass on: (WriteStream on: (ByteArray new: 1000)). self bitsPerPixel <= 8 ifTrue: [image := self. encoder transparentPixel: aPixelValue] ifFalse: [image := self collapsedTo8BitsOrLess. encoder transparentPixel: (image palette indexOfPaintNearest: (self palette at: aPixelValue))]. encoder nextPutImage: image. ^GIFEncoder.GIFImage from: encoder contents
asGIFWithTransparentPoint: aPoint | encoder image | image := self bitsPerPixel <= 8 ifTrue: [self] ifFalse: [self collapsedTo8BitsOrLess]. encoder := GIFEncoder.GIFImage preferredEncoderClass on: (WriteStream on: (ByteArray new: 1000)). encoder transparentPixel: (image atPoint: aPoint). encoder nextPutImage: image. ^GIFEncoder.GIFImage from: encoder contents
collapsedTo8BitsOrLess self bitsPerPixel <= 8 ifTrue: [^self]. (Screen default colorDepth = 8) ifTrue: [^self asRetainedMedium asImage]. ^self convertToPalette: (FixedPalette redShift: 4 redMask: 7 greenShift: 2 greenMask: 3 blueShift: 0 blueMask: 3).
</methods> <methods> Graphics.CoveragePalette <category>converting</category>
asMappedPalette "A CoveragePalette may not be meaningful as a MappedPalette; CoverageValues may not correspond to colors. For those cases where it makes sense, this method is provided. CoverageValues are converted to grays." self size = 2 ifTrue: [ ^MappedPalette whiteBlack ]. ^MappedPalette withColors: ( self collect: [ :cov | cov asGray ] )
</methods> <methods> Graphics.CoverageValue <category>converting</category>
asGray "It may not make sense fto convert coverages to colors -- but for those cases it might, this method is provided." self = Transparent ifTrue: [^ColorValue white]. self = Opaque ifTrue: [^ColorValue black]. ^ColorValue brightness: self coverage.
</methods> <initialize> GIFEncoder.BitmapImageEncoder </initialize> <initialize> GIFEncoder.GUFEncoder </initialize> <initialize> GIFEncoder.GIFImage </initialize> "Imported Classes:" self error: 'Attempting to file-in parcel imports. Choose terminate or close' <class> <name>Object</name> <environment>Core</environment> <super></super> <private>false</private> none <imports></imports> <category>Kernel-Objects</category> <attributes> <package>Kernel-Objects</package> </attributes> </class> <class> <name>CoverageValue</name> <environment>Graphics</environment> <super>Graphics.SimplePaint</super> <private>false</private> none coverage <imports></imports> <category>Graphics-Support</category> <attributes> <package>Graphics-Support</package> </attributes> </class> <class> <name>PixelArray</name> <environment>Graphics</environment> <super>Graphics.VisualComponent</super> <private>false</private> none <imports></imports> <category>Graphics-Images</category> <attributes> <package>Graphics-Images</package> </attributes> </class> <class> <name>Image</name> <environment>Graphics</environment> <super>Graphics.PixelArray</super> <private>false</private> none bits width height depth bitsPerPixel palette maxPixelValue rowByteSize <imports></imports> <category>Graphics-Images</category> <attributes> <package>Graphics-Images</package> </attributes> </class> <class> <name>CoveragePalette</name> <environment>Graphics</environment> <super>Graphics.Palette</super> <private>false</private> none maxPixelValue <imports></imports> <category>Graphics-Palettes</category> <attributes> <package>Graphics-Palettes</package> </attributes> </class>