[squeak-dev] The Trunk: Graphics-nice.492.mcz
Nicolas Cellier
nicolas.cellier.aka.nice at gmail.com
Sun Mar 6 13:58:47 UTC 2022
Sorry, it displayed the many diffs from Graphics-nice.442 rather than the
few from Graphics-mt.491...
That's only 3 new composition rules.
Le dim. 6 mars 2022 à 14:55, <commits at source.squeak.org> a écrit :
> Nicolas Cellier uploaded a new version of Graphics to project The Trunk:
> http://source.squeak.org/trunk/Graphics-nice.492.mcz
>
> ==================== Summary ====================
>
> Name: Graphics-nice.492
> Author: nice
> Time: 6 March 2022, 2:55:01.566363 pm
> UUID: 9b341ab2-f970-4afc-a140-d1c255a5ca41
> Ancestors: Graphics-nice.442, Graphics-mt.491
>
> Merge: Graphics-nice.442, Graphics-mt.491
>
> Note: the new BitBlt rules have been corrected in
> VMMaker.oscog-nice.3170/eem.3171, that is in VM generated from 2022
> February 24 and on.
>
> =============== Diff against Graphics-nice.442 ===============
>
> Item was added:
> + ----- Method: AbstractFont class>>defaultFallbackTextStyle (in category
> 'fallback') -----
> + defaultFallbackTextStyle
> + "Answer a text style that has fonts that support a lot of
> codepoints."
> +
> + ^ TextConstants at: #DefaultFallbackTextStyle ifAbsent: [TextStyle
> default]!
>
> Item was added:
> + ----- Method: AbstractFont class>>localeChanged (in category 'fallback')
> -----
> + localeChanged
> + self setupDefaultFallbackTextStyle.!
>
> Item was added:
> + ----- Method: AbstractFont class>>setupDefaultFallbackFontTo: (in
> category 'fallback') -----
> + setupDefaultFallbackFontTo: aFont
> +
> + self setupDefaultFallbackTextStyleTo: (TextStyle fontArray:
> {aFont}).!
>
> Item was added:
> + ----- Method: AbstractFont class>>setupDefaultFallbackTextStyle (in
> category 'fallback') -----
> + setupDefaultFallbackTextStyle
> +
> + | defaultStyle |
> + defaultStyle := self defaultFallbackTextStyle.
> + TextStyle knownTextStylesWithoutDefault do: [:styleName |
> + (TextStyle named: styleName) fontArray do: [:each |
> + each setupDefaultFallbackTextStyleTo:
> defaultStyle]].!
>
> Item was added:
> + ----- Method: AbstractFont class>>setupDefaultFallbackTextStyleTo: (in
> category 'fallback') -----
> + setupDefaultFallbackTextStyleTo: aTextStyle
> +
> + TextConstants at: #DefaultFallbackTextStyle put: aTextStyle.
> + !
>
> Item was added:
> + ----- Method: AbstractFont>>asNewTextStyle (in category 'converting')
> -----
> + asNewTextStyle
> + "Answer a new text style where the receiver is the default font.
> Try to lookup the an existing #textStyle so that TextFontChange can be used
> in views."
> +
> + | newTextStyle |
> + newTextStyle := self textStyleOrNil
> + ifNil: [TextStyle fontArray: {self}]
> + ifNotNil: [:style | style copy].
> + newTextStyle defaultFontIndex: (newTextStyle fontIndexOfPointSize:
> self pointSize).
> + ^ newTextStyle!
>
> Item was added:
> + ----- Method: AbstractFont>>asPointSize: (in category 'converting') -----
> + asPointSize: differentPointSize
> + "Convert the receiver into a different point size. Compared to
> #pointSize:, this operation does not modify the receiver but tries to
> lookup another font object or create one on-the-fly."
> +
> + self pointSize = differentPointSize ifTrue: [^ self].
> +
> + ^ self class
> + familyName: self familyName
> + pointSize: differentPointSize
> + emphasized: self emphasis!
>
> Item was added:
> + ----- Method: AbstractFont>>asRegular (in category 'converting') -----
> + asRegular
> + "Try to lookup the receiver with normal emphasis. If the receiver
> itself looks bold face, this might be okay. Rely on what is registered on
> the font family's text style. Not that this is different from #emphasized:
> with 0, which does nothing."
> +
> + self emphasis = 0 ifTrue: [^ self].
> + ^ self textStyleOrNil
> + ifNil: [self]
> + ifNotNil: [:style | style fontOfPointSize: self pointSize]!
>
> Item was added:
> + ----- Method: AbstractFont>>emphasis (in category 'accessing') -----
> + emphasis
> +
> + ^ 0!
>
> Item was added:
> + ----- Method: AbstractFont>>fallbackFont (in category 'accessing') -----
> + fallbackFont
> + "Answers the fallbackFont for the receiver. The fallback font must
> be some derivative of the receiver since it will not be asked to install
> itself properly on the target BitBlt so rendering a completely different
> font here is simply not possible. The default implementation uses a
> synthetic font that maps all characters to question marks."
> +
> + ^ FixedFaceFont new errorFont baseFont: self!
>
> Item was added:
> + ----- Method: AbstractFont>>fallbackFont: (in category
> 'initialize-release') -----
> + fallbackFont: aFont
> + "Reset fallback font. Ignore. See #fallbackFont."!
>
> Item was added:
> + ----- Method: AbstractFont>>hasFixedWidth (in category 'testing') -----
> + hasFixedWidth
> + "Answer whether the receiver is a
> monospaced/fixed-width/non-proportional font. See TextStyle class >>
> #defaultFixed."
> +
> + ^ (self widthOf: $.) = (self widthOf: $w)!
>
> Item was added:
> + ----- Method: AbstractFont>>isSynthetic (in category 'testing') -----
> + isSynthetic
> + ^ false!
>
> Item was added:
> + ----- Method: AbstractFont>>larger (in category 'converting') -----
> + larger
> + "Answer a font that is about 20% larger than the receiver but has
> the same font family and emphasis. Round to 0.5 points to not yield so many
> different font instances."
> +
> + ^ self asPointSize: ((self pointSize asFloat * 1.2) roundTo: 0.5)!
>
> Item was added:
> + ----- Method: AbstractFont>>lineGap (in category 'accessing') -----
> + lineGap
> +
> + ^ 2 "pre-rendered legacy fonts"!
>
> Item was added:
> + ----- Method: AbstractFont>>lineGapSlice (in category 'accessing') -----
> + lineGapSlice
> +
> + ^ 1 "pre-rendered legacy fonts"!
>
> Item was changed:
> ----- Method: AbstractFont>>lineGrid (in category 'accessing') -----
> lineGrid
> "Answer the relative space between lines"
>
> + ^ self height + self lineGap!
> - ^self subclassResponsibility!
>
> Item was added:
> + ----- Method: AbstractFont>>maxAscii (in category 'accessing') -----
> + maxAscii
> +
> + self flag: #deprecated.
> + ^ self maxCodePoint!
>
> Item was added:
> + ----- Method: AbstractFont>>maxCodePoint (in category 'accessing') -----
> + maxCodePoint
> + "Answer the largest code point that the receiver can translate
> into glyphs. Use the range from #minCodePoint to #maxCodePoint to configure
> a list of #fallbackFont's. Note that subclasses may insert 'holes' via
> #hasGlyphOf: test such as StrikeFont's internal xTable."
> +
> + ^ 16r10FFFF "Unicode uses 21-bit but 16r110000 to 16r1FFFFF are
> not valid code points. See https://www.unicode.org/versions/stats/."!
>
> Item was added:
> + ----- Method: AbstractFont>>minAscii (in category 'accessing') -----
> + minAscii
> +
> + self flag: #deprecated.
> + ^ self minCodePoint!
>
> Item was added:
> + ----- Method: AbstractFont>>minCodePoint (in category 'accessing') -----
> + minCodePoint
> + "Answer the smallest code point that the receiver can translate
> into glyphs. Use the range from #minCodePoint to #maxCodePoint to configure
> a list of #fallbackFont's. Note that subclasses may insert 'holes' via
> #hasGlyphOf: test such as StrikeFont's internal xTable."
> +
> + ^ 0!
>
> Item was changed:
> ----- Method: AbstractFont>>pixelSize (in category 'accessing') -----
> pixelSize
> "Make sure that we don't return a Fraction"
> + ^ (TextStyle pointsToPixels: self pointSize) rounded!
> - ^ TextStyle pointsToPixels: self pointSize!
>
> Item was added:
> + ----- Method: AbstractFont>>pixelsPerInch (in category 'accessing') -----
> + pixelsPerInch
> + "Answers the PPI reference for the pre-rendered receiver."
> +
> + ^ (self height * 72 / self pointSize asFloat) rounded!
>
> Item was changed:
> ----- Method: AbstractFont>>printShortDescriptionOn: (in category
> 'printing') -----
> printShortDescriptionOn: aStream
> + aStream nextPutAll: self familyName!
> - aStream space; nextPutAll: self familyName!
>
> Item was added:
> + ----- Method: AbstractFont>>reset (in category 'initialize-release')
> -----
> + reset
> + "Clear all caches."!
>
> Item was changed:
> ----- Method: AbstractFont>>sampleText (in category 'example') -----
> sampleText
>
> | text |
> + text := self isSymbolFont
> + ifTrue: [self symbolSample asText]
> - text := (self isSymbolFont or: [(self basicHasGlyphOf: $a) not])
> - ifTrue: [Text symbolSample]
> ifFalse: [Text textSample].
> text addAttribute: (TextFontReference toFont: self).
> ^ text!
>
> Item was added:
> + ----- Method: AbstractFont>>setupDefaultFallbackTextStyleTo: (in
> category 'initialize-release') -----
> + setupDefaultFallbackTextStyleTo: aTextStyle
> +
> + " | fonts f |
> + fonts := aTextStyle fontArray.
> + f := fonts first.
> + f familyName = self familyName ifTrue: [^ self].
> + 1 to: fonts size do: [:i |
> + self height > (fonts at: i) height ifTrue: [f := fonts at:
> i].
> + ]."
> +
> + self derivativeFonts do: [:each |
> + each setupDefaultFallbackTextStyleTo: aTextStyle].
> +
> + self flag: #todo. "mt: Figure out a way to lookup useful fallback
> fonts."
> +
> + self fallbackFont: nil.
> + self reset.!
>
> Item was added:
> + ----- Method: AbstractFont>>smaller (in category 'converting') -----
> + smaller
> + "Answer a font that is about 20% smaller than the receiver but has
> the same font family and emphasis. Round to 0.5 points to not yield so many
> different font instances."
> +
> + ^ self asPointSize: ((self pointSize asFloat * 0.8) roundTo: 0.5)!
>
> Item was added:
> + ----- Method: AbstractFont>>symbolSample (in category 'example') -----
> + symbolSample
> + "Variation of Text class >> #symbolSample, which uses the
> receiver's available code points. This is important for fonts such as
> Wingdings."
> +
> + ^ String streamContents: [:stream | | start lineLength character |
> + lineLength := 0.
> + (start := self minCodePoint max: 33) to: (self
> maxCodePoint min: start + 200) do: [:codePoint |
> + (self hasGlyphOf: (character := Character value:
> codePoint))
> + ifTrue: [stream nextPut: character.
> + ((lineLength := lineLength + 1) >
> 30) ifTrue: [
> + lineLength := 0.
> + stream cr]]]]!
>
> Item was changed:
> ----- Method: AbstractFont>>textStyle (in category 'accessing') -----
> textStyle
> + "Answer an instance of TextStyle that (most likely) includes the
> receiver. Note that if the receiver is used in more than one style, only
> answer the most prominent one."
> +
> + ^ self textStyleOrNil ifNil: [TextStyle fontArray: {self}]!
> - ^ TextStyle actualTextStyles detect:
> - [:aStyle | aStyle fontArray includes: self] ifNone: [
> TextStyle fontArray: { self } ]!
>
> Item was added:
> + ----- Method: AbstractFont>>textStyleOrNil (in category 'accessing')
> -----
> + textStyleOrNil
> + "Like #textStyle but avoid creating a new style for orphaned
> fonts."
> +
> + ^ TextStyle named: self textStyleName!
>
> Item was changed:
> ----- Method: BMPReadWriter class>>readAllFrom: (in category 'testing')
> -----
> readAllFrom: fd
> "MessageTally spyOn:[BMPReadWriter readAllFrom: FileDirectory
> default]"
> fd fileNames do:[:fName|
> (fName endsWith: '.bmp') ifTrue:[
> + [Form fromBinaryStream: (fd readOnlyFileNamed:
> fName)] ifError: [].
> - [Form fromBinaryStream: (fd readOnlyFileNamed:
> fName)] on: Error do:[:nix].
> ].
> ].
> fd directoryNames do:[:fdName|
> self readAllFrom: (fd directoryNamed: fdName)
> ].!
>
> Item was changed:
> ----- Method: BMPReadWriter>>nextPutImage: (in category 'writing') -----
> nextPutImage: aForm
> | bhSize rowBytes rgb data colorValues depth image ppw scanLineLen
> pixline |
> depth := aForm depth.
> depth := #(1 4 8 32 ) detect: [ :each | each >= depth].
> image := aForm asFormOfDepth: depth.
> image unhibernate.
> bhSize := 14. "# bytes in file header"
> biSize := 40. "info header size in bytes"
> biWidth := image width.
> biHeight := image height.
> biClrUsed := depth = 32 ifTrue: [0] ifFalse:[1 << depth]. "No.
> color table entries"
> bfOffBits := biSize + bhSize + (4*biClrUsed).
> rowBytes := ((depth min: 24) * biWidth + 31 // 32) * 4.
> biSizeImage := biHeight * rowBytes.
>
> "Write the file header"
> stream position: 0.
> stream nextLittleEndianNumber: 2 put: 19778. "bfType = BM"
> stream nextLittleEndianNumber: 4 put: bfOffBits + biSizeImage.
> "Entire file size in bytes"
> stream nextLittleEndianNumber: 4 put: 0. "bfReserved"
> stream nextLittleEndianNumber: 4 put: bfOffBits. "Offset of
> bitmap data from start of hdr (and file)"
>
> "Write the bitmap info header"
> stream position: bhSize.
> stream nextLittleEndianNumber: 4 put: biSize. "info header size
> in bytes"
> stream nextLittleEndianNumber: 4 put: image width. "biWidth"
> stream nextLittleEndianNumber: 4 put: image height. "biHeight"
> stream nextLittleEndianNumber: 2 put: 1. "biPlanes"
> stream nextLittleEndianNumber: 2 put: (depth min: 24).
> "biBitCount"
> stream nextLittleEndianNumber: 4 put: 0. "biCompression"
> stream nextLittleEndianNumber: 4 put: biSizeImage. "size of image
> section in bytes"
> stream nextLittleEndianNumber: 4 put: 2800. "biXPelsPerMeter"
> stream nextLittleEndianNumber: 4 put: 2800. "biYPelsPerMeter"
> stream nextLittleEndianNumber: 4 put: biClrUsed.
> stream nextLittleEndianNumber: 4 put: 0. "biClrImportant"
> biClrUsed > 0 ifTrue: [
> "write color map; this works for ColorForms, too"
> colorValues := image colormapIfNeededForDepth: 32.
> 1 to: biClrUsed do: [:i |
> rgb := colorValues at: i.
> 0 to: 24 by: 8 do: [:j | stream nextPut: (rgb >> j
> bitAnd: 16rFF)]]].
>
> depth < 32 ifTrue: [
> "depth = 1, 4 or 8."
> data := image bits asByteArray.
> ppw := 32 // depth.
> scanLineLen := biWidth + ppw - 1 // ppw * 4. "# of bytes
> in line"
> 1 to: biHeight do: [:i |
> stream next: scanLineLen putAll: data startingAt:
> (biHeight-i)*scanLineLen+1.
> ].
> ] ifFalse: [
> data := image bits.
> pixline := ByteArray new: (((biWidth * 3 + 3) // 4) * 4).
> 1 to: biHeight do:[:i |
> self store24BitBmpLine: pixline from: data
> startingAt: (biHeight-i)*biWidth+1 width: biWidth.
> stream nextPutAll: pixline.
> ].
> ].
> + stream position = (bfOffBits + biSizeImage) ifFalse: [self error:
> 'Write failure' translated].
> - stream position = (bfOffBits + biSizeImage) ifFalse: [self
> error:'Write failure'].
> stream close.!
>
> Item was changed:
> ----- Method:
> CharacterScanner>>primScanCharactersFrom:to:in:rightX:stopConditions:kern:
> (in category 'scanning') -----
> primScanCharactersFrom: startIndex to: stopIndex in: sourceString
> rightX: rightX stopConditions: stops kern: kernDelta
> + "Primitive. This is the inner loop of text display--but see
> #scanCharactersFrom:to:in:rightX: which would get the string,
> stopConditions and displaying from the instance. March through sourceString
> from startIndex to stopIndex. If any character is flagged with a non-nil
> entry in stops, then return the corresponding value. Determine width of
> each character from xTable, indexed by map. If destX would exceed rightX,
> then return stops at: 258. Advance destX by the width of the character. If
> stopIndex has been reached, then return stops at: 257.
> +
> + Optional. See Object documentation whatIsAPrimitive.
> +
> + NOTE THAT this primitive does only work for our legacy StrikeFont
> because #setActualFont: needs #xTable and #characterToGlyphMap, which are
> both not available for TTCFont this way and thus we end up calling
> #widthOf: manually on the font."
> +
> - "Primitive. This is the inner loop of text display--but see
> - scanCharactersFrom: to:rightX: which would get the string,
> - stopConditions and displaying from the instance. March through
> source
> - String from startIndex to stopIndex. If any character is flagged
> with a
> - non-nil entry in stops, then return the corresponding value.
> Determine
> - width of each character from xTable, indexed by map.
> - If dextX would exceed rightX, then return stops at: 258.
> - Advance destX by the width of the character. If stopIndex has been
> - reached, then return stops at: 257. Optional.
> - See Object documentation whatIsAPrimitive.
> - Historical note: this primitive has been unusable since about
> Squeak 2.8 when the shape of the CharracterScanner class changed. It is
> left here as a reminder that the actual primitive still needs supporting in
> the VM to keep old images such as Scratch1.4 alive - tpr"
> <primitive: 103>
> + ^self basicScanByteCharactersFrom: startIndex to: stopIndex in:
> sourceString rightX: rightX
> +
> + "Here are some sketchy benchmarks to illustrate the performance issue
> regarding the use of TrueType fonts:
> +
> + 1) TTCFont only; with primitive fail
> + '1,160 per second. 865 microseconds per run. 0.44 % GC time.'
> +
> + 2) TTCFont only; without primitive fail
> + '5,730 per second. 175 microseconds per run. 6.12 % GC time.'
> +
> + 3) StrikeFont only; using primitive 103
> + '29,700 per second. 33.6 microseconds per run. 1.11978 % GC time.'
> +
> + 4) StrikeFont only; without primitive 103
> + '13,900 per second. 71.7 microseconds per run. 1.17976 % GC time.'
> +
> + "!
> - ^self basicScanByteCharactersFrom: startIndex to: stopIndex in:
> sourceString rightX: rightX !
>
> Item was changed:
> ----- Method: Color>>negated (in category 'transformations') -----
> negated
> "Return an RGB inverted color"
> ^Color
> r: 1.0 - self red
> g: 1.0 - self green
> + b: 1.0 - self blue
> + alpha: self alpha!
> - b: 1.0 - self blue!
>
> Item was added:
> + ----- Method: Color>>veryMuchDarker (in category 'transformations') -----
> + veryMuchDarker
> +
> + ^ self alphaMixed: 0.25 with: Color black
> + !
>
> Item was changed:
> CharacterScanner subclass: #CompositionScanner
> + instanceVariableNames: 'spaceX spaceIndex lineHeight baseline
> lineGap lineGapSlice topMargin bottomMargin lineHeightAtSpace
> baselineAtSpace lastBreakIsNotASpace nextIndexAfterLineBreak'
> - instanceVariableNames: 'spaceX spaceIndex lineHeight baseline
> lineHeightAtSpace baselineAtSpace lastBreakIsNotASpace
> nextIndexAfterLineBreak'
> classVariableNames: ''
> poolDictionaries: ''
> category: 'Graphics-Text'!
>
> !CompositionScanner commentStamp: 'nice 10/6/2013 23:24' prior: 0!
> A CompositionScanner measures text and determines where line breaks.
> Given a rectangular zone on input, it is used to split text in
> horizontal lines, and produce information about those lines on output (at
> which index a line starts/stops, which vertical space does the line
> require, which horizontal space if left for adjusting inter-word spacing,
> etc...)
>
> Instance Variables
> baseline: <Number>
> baselineAtSpace: <Number>
> lastBreakIsNotASpace: <Boolean>
> lineHeight: <Number>
> lineHeightAtSpace: <Number>
> nextIndexAfterLineBreak: <Integer>
> spaceIndex: <Integer>
> spaceX: <Number>
>
> baseline
> - the distance between top of line and the base line (that is the
> bottom of latin characters abcdehiklmnorstuvwx in most fonts)
>
> baselineAtSpace
> - memorize the baseline at last encountered space or other
> breakable character.
> This is necessary because the CompositionScanner wants to break
> line at a breakable character.
> If a word layout overflows the right margin, the scanner has to
> roll back and restore the line state to last encountered breakable
> character.
>
> lastBreakIsNotASpace
> - indicates that the last breakable character was not a space.
> This is necessary because handling a line break at a space differs
> from non space.
> If line break occurs on space, the space won't be displayed in
> next line.
> If it's another breakable character, it has to be displayed on
> next line.
>
> lineHeight
> - the total line height from top to bottom, including inter-line
> spacing.
>
> lineHeightAtSpace
> - the line height at last encountered space or other breakable
> character.
> See baselineAtSpace for explanation.
>
> nextIndexAfterLineBreak
> - the index of character after the last line break that was
> encountered.
>
> spaceIndex
> - the index of last space or other breakable character that was
> encountered
>
> spaceX
> - the distance from left of composition zone to left of last
> encountered space or other breakable character
> See baselineAtSpace for explanation.
>
> Note: if a line breaks on a space, a linefeed or a carriage return, then
> the space, linefeed or carriage return is integrated in the line.
> If there is a carriage return - linefeed pair, the pair is integrated to
> the line as if it were a single line break for compatibility with legacy
> software.!
>
> Item was changed:
> ----- Method:
> CompositionScanner>>composeFrom:inRectangle:firstLine:leftSide:rightSide:
> (in category 'scanning') -----
> composeFrom: startIndex inRectangle: lineRectangle
> firstLine: firstLine leftSide: leftSide rightSide: rightSide
> "Answer an instance of TextLineInterval that represents the next
> line in the paragraph."
> + | runLength stopCondition lineSpacing |
> - | runLength stopCondition |
> "Set up margins"
> leftMargin := lineRectangle left.
> leftSide ifTrue: [leftMargin := leftMargin +
> (firstLine ifTrue:
> [textStyle firstIndent]
> ifFalse:
> [textStyle restIndent])].
> destX := spaceX := leftMargin.
> rightMargin := lineRectangle right.
> rightSide ifTrue: [rightMargin := rightMargin - textStyle
> rightIndent].
> lastIndex := startIndex. "scanning sets last index"
> destY := lineRectangle top.
> + lineHeight := baseline := 0. "Will be increased by setFont"
> + lineGap := lineGapSlice := -9999. "Will be increased by setFont;
> allow negative to show all effects of a custom #extraGap value. See
> TTFontDescription."
> + topMargin := bottomMargin := 0.
> - lineHeight := baseline := 0. "Will be increased by setFont"
> line := (TextLine start: lastIndex stop: 0 internalSpaces: 0
> paddingWidth: 0)
> rectangle: lineRectangle.
> self setStopConditions. "also sets font"
> runLength := text runLengthFor: startIndex.
> runStopIndex := (lastIndex := startIndex) + (runLength - 1).
> nextIndexAfterLineBreak := spaceCount := 0.
> lastBreakIsNotASpace := false.
> self handleIndentation.
> leftMargin := destX.
> line leftMargin: leftMargin.
>
> [stopCondition := self scanCharactersFrom: lastIndex to:
> runStopIndex
> in: text string rightX: rightMargin.
> "See setStopConditions for stopping conditions for composing."
> self perform: stopCondition] whileFalse.
>
> + lineHeight := lineHeight + lineGap.
> + baseline := baseline + lineGapSlice.
> +
> + "TODO: Allow special characters or text attributes to accumulate
> extra top or bottom margin."
> + (lineSpacing := textStyle lineSpacing) = 0.0
> + ifFalse: [bottomMargin := bottomMargin + (lineSpacing *
> lineHeight) truncated].
> +
> + line lineHeight: lineHeight baseline: baseline.
> + line topMargin: topMargin bottomMargin: bottomMargin.
> + ^ line!
> - ^ line
> - lineHeight: lineHeight + textStyle leading
> - baseline: baseline + textStyle leading!
>
> Item was changed:
> ----- Method: CompositionScanner>>computeDefaultLineHeight (in category
> 'scanning') -----
> computeDefaultLineHeight
> "Compute the default line height for a potentially empty text"
> rightMargin notNil
> ifTrue: [lastIndex := 1.
> self setFont.
> + ^ lineHeight + lineGap]
> - ^ lineHeight + textStyle leading]
> ifFalse: [^textStyle lineGrid]!
>
> Item was changed:
> ----- Method: CompositionScanner>>setActualFont: (in category 'text
> attributes') -----
> setActualFont: aFont
> "Keep track of max height and ascent for auto lineheight"
> | descent |
> super setActualFont: aFont.
> lineHeight == nil
> ifTrue: [descent := font descent.
> baseline := font ascent.
> + lineHeight := baseline + descent.
> + lineGap := aFont lineGap.
> + lineGapSlice := aFont lineGapSlice]
> - lineHeight := baseline + descent]
> ifFalse: [descent := lineHeight - baseline max: font
> descent.
> baseline := baseline max: font ascent.
> + lineHeight := lineHeight max: baseline +
> descent.
> + lineGap := lineGap max: aFont lineGap.
> + lineGapSlice := lineGapSlice max: aFont
> lineGapSlice]!
> - lineHeight := lineHeight max: baseline +
> descent]!
>
> Item was changed:
> ----- Method: Cursor class>>currentCursor: (in category 'current
> cursor') -----
> currentCursor: aCursor
> "Make the instance of cursor, aCursor, be the current cursor.
> Display it.
> Create an error if the argument is not a Cursor."
>
> (aCursor isKindOf: self)
> + ifTrue: [ | platformCursor |
> + CurrentCursor := aCursor. "unscaled"
> + platformCursor := aCursor enlargedBy:
> RealEstateAgent scaleFactor.
> + self useBiggerCursors
> + ifTrue: [platformCursor := platformCursor
> enlargedBy: 2].
> + platformCursor beCursor]
> - ifTrue: [CurrentCursor := aCursor.
> - self useBiggerCursors
> - ifTrue: [[^ aCursor asBigCursor
> beCursor]
> - on: Error do: ["fall
> through"]].
> - aCursor beCursor]
> ifFalse: [self error: 'The new cursor must be an instance
> of class Cursor']!
>
> Item was changed:
> ----- Method: Cursor class>>useBiggerCursors (in category 'preferences')
> -----
> useBiggerCursors
>
> <preference: 'Use bigger mouse cursors'
> + categoryList: #(mouse Accessibility)
> - category: 'mouse'
> description: 'If true, mouse cursors are scaled up'
> type: #Boolean>
> ^ UseBiggerCursors ifNil: [false]!
>
> Item was changed:
> ----- Method: Cursor class>>useBiggerCursors: (in category
> 'preferences') -----
> useBiggerCursors: aBool
>
> + UseBiggerCursors := aBool.
> + Cursor currentCursor: Cursor currentCursor.!
> - UseBiggerCursors := aBool!
>
> Item was changed:
> Form subclass: #DisplayScreen
> instanceVariableNames: 'clippingBox extraRegions'
> + classVariableNames: 'DeferringUpdates DisplayChangeSignature
> DisplayIsFullScreen PlatformScaleFactor RelativeScaleFactorEnabled'
> - classVariableNames: 'DeferringUpdates DisplayChangeSignature
> DisplayIsFullScreen'
> poolDictionaries: ''
> category: 'Graphics-Display Objects'!
>
> !DisplayScreen commentStamp: '<historical>' prior: 0!
> There is only one instance of me, Display. It is a global and is used to
> handle general user requests to deal with the whole display screen.
> Although I offer no protocol, my name provides a way to
> distinguish this special instance from all other Forms. This is useful, for
> example, in dealing with saving and restoring the system.
> To change the depth of your Display...
> Display newDepth: 16.
> Display newDepth: 8.
> Display newDepth: 1.
> Valid display depths are 1, 2, 4, 8, 16 and 32. It is suggested that
> you run with your monitors setting the same, for better speed and color
> fidelity. Note that this can add up to 4Mb for the Display form. Finally,
> note that newDepth: ends by executing a 'ControlManager restore' which
> currently terminates the active process, so nothing that follows in the
> doit will get executed.
>
> Depths 1, 2, 4 and 8 bits go through a color map to put color on the
> screen, but 16 and 32-bit color use the pixel values directly for RGB color
> (5 and 8 bits per, respectivlely). The color choice an be observed by
> executing Color fromUser in whatever depth you are using.
> !
>
> Item was added:
> + ----- Method: DisplayScreen class>>actualScreenScaleFactor (in category
> 'snapshots') -----
> + actualScreenScaleFactor
> + "<primitive: #primitiveScreenScaleFactor>"
> + "Once the primitive is ready, you can simply uncomment it. The
> system is prepared for changes from 'nil' to actual factors and back."
> +
> + ^ nil "unknown -- do not default to 1.0 here"!
>
> Item was added:
> + ----- Method: DisplayScreen class>>checkForNewScreenScaleFactor (in
> category 'display box access') -----
> + checkForNewScreenScaleFactor
> + "Check whether the platform's scale factor has changed and if so
> take appropriate actions"
> +
> + Display platformScaleFactor: DisplayScreen
> actualScreenScaleFactor.!
>
> Item was added:
> + ----- Method: DisplayScreen class>>relativeScaleFactor (in category
> 'preferences') -----
> + relativeScaleFactor
> + <preference: 'Scale Factor'
> + categoryList: #(Morphic Tools visuals Accessibility)
> + description: 'Set the size of fonts and tools according to
> the pixels-per-inch of your display. Can be used to zoom in even further.'
> + type: #String>
> +
> + ^ Display relativeUiScaleFactor!
>
> Item was added:
> + ----- Method: DisplayScreen class>>relativeScaleFactor: (in category
> 'preferences') -----
> + relativeScaleFactor: aFloatString
> +
> + Display relativeUiScaleFactor: (aFloatString ifNotNil: [:s | s
> asNumber] ifNil: [1.0]).!
>
> Item was added:
> + ----- Method: DisplayScreen class>>relativeScaleFactorEnabled (in
> category 'preferences') -----
> + relativeScaleFactorEnabled
> + <preference: 'Show Relative Scale Factor'
> + categoryList: #(Morphic Tools visuals)
> + description: 'When true, 100% means
> as-the-platform/monitor-demands, which is typical for macOS. When false,
> 100% means pixel-perfect, which is typical for Windows. Only works if
> #platformScaleFactorKnown.'
> + type: #Boolean>
> +
> + "Note that we set the default to 'false' because pixels are very
> prominent in the Morphic programming model. So it makes sense to communcate
> this kind of pixel scaling openly and thus not hide the
> #platformScaleFactor from the user."
> + ^ RelativeScaleFactorEnabled ifNil: [false].!
>
> Item was added:
> + ----- Method: DisplayScreen class>>relativeScaleFactorEnabled: (in
> category 'preferences') -----
> + relativeScaleFactorEnabled: aBoolean
> +
> + RelativeScaleFactorEnabled := Display platformScaleFactorKnown
> and: [aBoolean ifNil: [false]].!
>
> Item was added:
> + ----- Method: DisplayScreen>>currentScaleError (in category 'scale
> factor') -----
> + currentScaleError
> + "Documentation and debugging only. The scale error originates in
> rounding errors while rendering the standard TTCFont font into pixels or
> using a pre-rendered StrikeFont in the first place.
> +
> + Display currentScaleError
> + "
> +
> + ^ RealEstateAgent scaleFactor - self uiScaleFactor
> +
> + "
> + | errors current |
> + errors := OrderedDictionary new.
> + current := Display uiScaleFactor.
> + 1.0 to: 3.0 by: 0.25 do: [:s |
> + Display uiScaleFactor: s.
> + errors at: s put: Display currentScaleError].
> + Display uiScaleFactor: current.
> + errors explore.
> + "!
>
> Item was added:
> + ----- Method: DisplayScreen>>platformScaleFactor (in category 'scale
> factor') -----
> + platformScaleFactor
> + "Answers the platform's (and thus monitor's) current scale factor
> as last reported via VM primitive. See #checkForNewScreenScaleFactor."
> +
> + ^ PlatformScaleFactor ifNil: [1.0 "Primitive not ready."]!
>
> Item was added:
> + ----- Method: DisplayScreen>>platformScaleFactor: (in category 'scale
> factor') -----
> + platformScaleFactor: aFloatOrNil
> + "Report a new scale factor from the platform to the image. This
> can happen if you move Squeak's window between monitors with different
> pixels-per-inch. On some platforms, you can also set a scale factor
> independent of monitor PPI. Note that the user might have scaled the image
> regardless of the previous platform scale factor."
> +
> + | old new |
> + aFloatOrNil ifNil: [
> + "Ignore. Primitive not ready (anymore)."
> + PlatformScaleFactor := nil.
> + ^ self].
> + PlatformScaleFactor ifNil: [
> + "First time. Ignore. Assume that the user scaled manually
> until now."
> + PlatformScaleFactor := aFloatOrNil.
> + ^ self].
> +
> + (old := self platformScaleFactor) = (new := aFloatOrNil) ifTrue:
> [^ self].
> + PlatformScaleFactor := new.
> + self uiScaleFactor: self uiScaleFactor * (new/old).!
>
> Item was added:
> + ----- Method: DisplayScreen>>platformScaleFactorKnown (in category
> 'scale factor') -----
> + platformScaleFactorKnown
> + "Tools can help users understand whether Squeak will adjust the
> scale factor automatically or whether they have to scale manually. See
> class-side's #actualScreenScaleFactor and also #relativeUiScaleFactor."
> +
> + ^ PlatformScaleFactor notNil!
>
> Item was added:
> + ----- Method: DisplayScreen>>relativeUiScaleFactor (in category 'scale
> factor') -----
> + relativeUiScaleFactor
> + "Answers the scale factor as perceived by the user. Note that this
> concept might be platform-dependent. On Windows, for example, 100% means
> pixel-perfect while on macOS 100% means as-the-monitor-demands. So, Retina
> displays are effectively scaled even if the user sees 100% in a
> Choose-Your-Scale dialog."
> +
> + ^ self class relativeScaleFactorEnabled
> + ifTrue: [ "macOS" self uiScaleFactor / self
> platformScaleFactor ]
> + ifFalse: [ "Windows" self uiScaleFactor ]!
>
> Item was added:
> + ----- Method: DisplayScreen>>relativeUiScaleFactor: (in category 'scale
> factor') -----
> + relativeUiScaleFactor: aFloat
> +
> + ^ self class relativeScaleFactorEnabled
> + ifTrue: [ "macOS" self uiScaleFactor: aFloat * self
> platformScaleFactor ]
> + ifFalse: [ "Windows" self uiScaleFactor: aFloat ]!
>
> Item was added:
> + ----- Method: DisplayScreen>>uiScaleFactor (in category 'scale factor')
> -----
> + uiScaleFactor
> + "Answers the current scale factor used to configure all widgets,
> tools, or windows to be prepared for the current rendering system, i.e.,
> BitBlt. Note that 1.0 means 'pixel perfect'."
> +
> + ^ UserInterfaceTheme current isTTCBased
> + ifTrue: [TextStyle pixelsPerInch / 96.0 "Hide rounding
> errors in TTCFont >> #height."]
> + ifFalse: [RealEstateAgent scaleFactor roundTo: 0.25 "Force
> 25% steps. See, e.g., #doScale150."].!
>
> Item was added:
> + ----- Method: DisplayScreen>>uiScaleFactor: (in category 'scale factor')
> -----
> + uiScaleFactor: aFloat
> + "Sets the effective scale factor for the user interface, i.e., all
> widgets, tools, and windows. The user can override the CurrentScaleFactor
> recommended by the platform."
> +
> + | oldFactor newFactor |
> + (UserInterfaceTheme current canFakeScaleFactor: aFloat) ifTrue: [
> + self flag: #isTTCBased.
> + ^ UserInterfaceTheme current applyScaled: aFloat].
> +
> + aFloat = 0.75 ifTrue: [(Project uiManager confirm: ((('You are
> currently using <b>TrueType fonts</b>. Your requested scale factor of
> <b>{1}%</b> looks better using pre-rendered <b>pixel fonts</b>.\\Do you
> want to switch to pixel fonts now?' translated withCRs format: {(aFloat *
> 100) rounded}) withNoLineLongerThan: 60) copyReplaceAll: String cr with:
> '<br>') asTextFromHtml title: 'Blurry Fonts Detected' translated) == true
> + ifTrue: [UserInterfaceTheme cleanUpAndReset. ^ self
> uiScaleFactor: aFloat]].
> +
> + oldFactor := RealEstateAgent scaleFactor. "Use effective,
> pixel-based factor to account for rounding errors. See #isTTCBased and
> #uiScaleFactor."
> + newFactor := aFloat max: 0.75.
> + newFactor = oldFactor ifTrue: [^ self].
> +
> + TextStyle pixelsPerInch: 96.0 * aFloat.
> + newFactor := RealEstateAgent resetScaleFactor; scaleFactor.
> "Again, account for rounding errors."
> + Project current ifNotNil: [:p | p displayScaleChangedFrom:
> oldFactor to: newFactor].!
>
> Item was added:
> + ----- Method: DisplayText>>asString (in category 'converting') -----
> + asString
> + "See String >> #asDisplayText."
> +
> + ^ self string!
>
> Item was added:
> + ----- Method: DisplayText>>asText (in category 'converting') -----
> + asText
> + "See Text >> #asDisplayText."
> +
> + ^ self text!
>
> Item was changed:
> ----- Method: FixedFaceFont>>baseFont: (in category 'accessing') -----
> baseFont: aFont
> + baseFont := aFont.
> + self fixSubstitutionCharacter.!
> - baseFont := aFont!
>
> Item was changed:
> + ----- Method: FixedFaceFont>>characterFormAt: (in category 'character
> shapes') -----
> - ----- Method: FixedFaceFont>>characterFormAt: (in category 'accessing')
> -----
> characterFormAt: character
> ^ baseFont characterFormAt: substitutionCharacter!
>
> Item was added:
> + ----- Method: FixedFaceFont>>errorCharacter (in category 'accessing')
> -----
> + errorCharacter
> + ^$?!
>
> Item was changed:
> ----- Method: FixedFaceFont>>errorFont (in category
> 'initialize-release') -----
> errorFont
> displaySelector := #displayErrorOn:length:at:kern:baselineY:.
> + substitutionCharacter := self errorCharacter.
> + self fixSubstitutionCharacter.!
> - substitutionCharacter := $?.!
>
> Item was added:
> + ----- Method: FixedFaceFont>>fixSubstitutionCharacter (in category
> 'private') -----
> + fixSubstitutionCharacter
> +
> + substitutionCharacter ifNil: [^ self].
> + baseFont ifNil: [^ self].
> + (baseFont hasGlyphOf: substitutionCharacter) ifTrue: [^ self].
> +
> + ((baseFont minCodePoint max: 33) to: baseFont maxCodePoint)
> + detect: [:codePoint | baseFont hasGlyphOf: (Character
> value: codePoint)]
> + ifFound: [:codePoint | substitutionCharacter := Character
> value: codePoint]
> + ifNone: [
> + baseFont := TextStyle defaultFont.
> + substitutionCharacter := $?].
> + self
> + assert: [baseFont hasGlyphOf: substitutionCharacter]
> + description: 'Could not find a possible substitution
> character and font!!'.!
>
> Item was added:
> + ----- Method: FixedFaceFont>>fontPointSize: (in category 'accessing')
> -----
> + fontPointSize: aNumber
> + self baseFont: (StrikeFont familyName: baseFont familyName
> pointSize: aNumber) copy!
>
> Item was added:
> + ----- Method: FixedFaceFont>>formOf: (in category 'private') -----
> + formOf: aCharacter
> + "No need to check #hasGlyphOf:."
> +
> + ^ self characterFormAt: aCharacter!
>
> Item was changed:
> ----- Method: FixedFaceFont>>initialize (in category
> 'initialize-release') -----
> initialize
> +
> + baseFont := TextStyle defaultFont.
> + self passwordFont.!
> - "This used to be the default textstyle, but it needs to be a
> StrikeFont and not a TTCFont and sometimes the default textstyle is a
> TTCFont. So, we use a typical StrikeFont as the default fallback font."
> - baseFont := StrikeFont defaultSized: 12.
> - self passwordFont!
>
> Item was removed:
> - ----- Method: FixedFaceFont>>maxAscii (in category 'accessing') -----
> - maxAscii
> - ^ SmallInteger maxVal!
>
> Item was added:
> + ----- Method: FixedFaceFont>>maxCodePoint (in category 'accessing') -----
> + maxCodePoint
> + "Overwritten for robustness. The receiver MUST BE a reliable
> source of glyphs if all else fails. Font rendering must never stop."
> +
> + ^ SmallInteger maxVal!
>
> Item was added:
> + ----- Method: FixedFaceFont>>minSubstitutionCharacter (in category
> 'private') -----
> + minSubstitutionCharacter
> +
> + ^ Character value: (baseFont ifNil: [0] ifNotNil: [baseFont
> minCodePoint])!
>
> Item was changed:
> ----- Method: FixedFaceFont>>passwordFont (in category
> 'initialize-release') -----
> passwordFont
> displaySelector := #displayPasswordOn:length:at:kern:baselineY:.
> + substitutionCharacter := self passwordCharacter.
> + self fixSubstitutionCharacter.!
> - substitutionCharacter := $*!
>
> Item was changed:
> ----- Method: FontSet class>>acceptsLoggingOfCompilation (in category
> 'compiling') -----
> acceptsLoggingOfCompilation
> "Dont log sources for my subclasses, so as not to waste time
> and space storing printString versions of the string literals."
>
> + ^super acceptsLoggingOfCompilation
> + and:
> + [self == FontSet]!
> - ^ self == FontSet!
>
> Item was changed:
> DisplayMedium subclass: #Form
> instanceVariableNames: 'bits width height depth offset'
> + classVariableNames: 'CompressOnSnapshot'
> - classVariableNames: ''
> poolDictionaries: ''
> category: 'Graphics-Display Objects'!
>
> !Form commentStamp: 'cbc 5/5/2017 10:07' prior: 0!
> A rectangular array of pixels, used for holding images. All pictures,
> including character images are Forms. The depth of a Form is how many bits
> are used to specify the color at each pixel. The actual bits are held in a
> Bitmap, whose internal structure is different at each depth. Class Color
> allows you to deal with colors without knowing how they are actually
> encoded inside a Bitmap.
> The supported depths (in bits) are 1, 2, 4, 8, 16, and 32. The
> number of actual colors at these depths are: 2, 4, 16, 256, 32768, and 16
> million.
> Forms are indexed starting at 0 instead of 1; thus, the top-left
> pixel of a Form has coordinates 0 at 0.
> Forms are combined using BitBlt. See the comment in class
> BitBlt. Forms that repeat many times to fill a large destination are
> InfiniteForms.
>
> colorAt: x at y Returns the abstract Color at this
> location
> displayAt: x at y shows this form on the screen
> displayOn: aMedium at: x at y shows this form in a Window, a
> Form, or other DisplayMedium
> fillColor: aColor Set all the pixels to the color.
> edit launch an editor to change the bits of this form.
> pixelValueAt: x at y The encoded color. The encoding depends
> on the depth.
>
> Note: If you want to hook up other external forms/displayScreens, please
> look at the (successful) Graphics-External package in
> http://www.squeaksource.com/Balloon3D.!
>
> Item was added:
> + ----- Method: Form class>>compressOnSnapshot (in category 'preferences')
> -----
> + compressOnSnapshot
> + <preference: 'Always compress graphics data on snapshot'
> + categoryList: #(performance Graphics)
> + description: 'When enabled, graphics data such as all
> instances of Form are compressed during image snapshots -- even when the
> image will not quit after the snapshot. This includes cache clean-up such
> as the TrueType glyph cache. Disable to avoid render lags after snapshots
> at the cost of a bigger footprint of your .image file. Note that
> snapshot-and-quit always compresses graphics data.'
> + type: #Boolean>
> +
> + ^ CompressOnSnapshot ifNil: [true]!
>
> Item was added:
> + ----- Method: Form class>>compressOnSnapshot: (in category
> 'preferences') -----
> + compressOnSnapshot: aBoolean
> +
> + CompressOnSnapshot := aBoolean.!
>
> Item was removed:
> - ----- Method: Form class>>shutDown (in category 'shut down') -----
> - shutDown "Form shutDown"
> - "Compress all instances in the system. Will decompress on
> demand..."
> - Form allInstancesDo: [:f | f hibernate].
> - ColorForm allInstancesDo: [:f | f hibernate].!
>
> Item was added:
> + ----- Method: Form class>>shutDown: (in category 'shut down') -----
> + shutDown: quitting
> + "When quitting, compress all instances in the system. Will
> decompress on demand after start-up. Note that #compressOnShapshot can
> avoid hibernating forms during no-quit snapshotting to keep the system as
> responsive as possible directly after."
> +
> + "Form shutDown: true"
> + (quitting or: [self compressOnSnapshot]) ifTrue: [
> + Form allInstancesDo: [:f | f hibernate].
> + ColorForm allInstancesDo: [:f | f hibernate]].!
>
> Item was changed:
> ----- Method: Form>>bitsSize (in category 'accessing') -----
> bitsSize
> + | pixelsPerWord |
> + depth ifNil: [depth := 1].
> + pixelsPerWord := 32 // self depth.
> + ^width + pixelsPerWord - 1 // pixelsPerWord * height!
> - | pixPerWord |
> - depth == nil ifTrue: [depth := 1].
> - pixPerWord := 32 // self depth.
> - ^ width + pixPerWord - 1 // pixPerWord * height!
>
> Item was added:
> + ----- Method: Form>>isVirtualScreen (in category 'testing') -----
> + isVirtualScreen
> + "Am I a virtual display screen?"
> + ^false!
>
> Item was changed:
> ----- Method: Form>>setExtent:depth:bits: (in category 'private') -----
> setExtent: extent depth: bitsPerPixel bits: bitmap
> "Create a virtual bit map with the given extent and bitsPerPixel."
> + | bitsClass |
> + (width := extent x asInteger) < 0 ifTrue: [width := 0].
> + (height := extent y asInteger) < 0 ifTrue: [height := 0].
> -
> - width := extent x asInteger.
> - width < 0 ifTrue: [width := 0].
> - height := extent y asInteger.
> - height < 0 ifTrue: [height := 0].
> depth := bitsPerPixel.
> - depth := bitsPerPixel.
> (bits isNil
> + or: [(bitsClass := bits class) isBits
> + and: [self bitsSize * 4 "bytes per pixel" = (bitmap size *
> bitsClass elementSize)]]) ifFalse:
> + [^self error: 'Bad dimensions and/or bitmap kind'].
> - or:[(bitmap class isWords and: [self bitsSize = bitmap
> size])
> - or: [bitmap class isBytes and: [self bitsSize * 4 = bitmap
> size]]])
> - ifFalse:[^self error:'Bad dimensions'].
> bits := bitmap!
>
> Item was changed:
> StrikeFont subclass: #FormSetFont
> instanceVariableNames: 'tintable combinationRule'
> classVariableNames: ''
> poolDictionaries: ''
> category: 'Graphics-Fonts'!
>
> + !FormSetFont commentStamp: 'ct 2/14/2020 16:49' prior: 0!
> + FormSetFonts are designed to capture individual images as character
> forms for imbedding in normal text. While most often used to insert an
> isolated glyph in some text, the code is actually designed to support an
> entire user-defined font. The TextAttribute subclass TextFontReference is
> specifically designed for such in-line insertion of exceptional fonts in
> normal text.!
> - !FormSetFont commentStamp: '<historical>' prior: 0!
> - FormSetFonts are designed to capture individual images as character
> forms for imbedding in normal text. While most often used to insert an
> isolated glyph in some text, the code is actually desinged to support an
> entire user-defined font. The TextAttribute subclass TextFontReference is
> specifically designed for such in-line insertion of exceptional fonts in
> normal text.!
>
> Item was changed:
> ----- Method: GIFReadWriter class>>grabScreenAndSaveOnDisk (in category
> 'examples') -----
> grabScreenAndSaveOnDisk
> "GIFReadWriter grabScreenAndSaveOnDisk"
>
> | form fileName |
> form := Form fromUser.
> form bits size = 0 ifTrue: [^Beeper beep].
> fileName := FileDirectory default nextNameFor: 'Squeak' extension:
> 'gif'.
> + Project uiManager
> + informUser: ('Writing {1}' translated format: {fileName})
> + during: [GIFReadWriter putForm: form onFileNamed:
> fileName].!
> - UIManager default informUser: 'Writing ' , fileName
> - during: [GIFReadWriter putForm: form onFileNamed:
> fileName]!
>
> Item was changed:
> ----- Method: GIFReadWriter>>nextImage (in category 'accessing') -----
> nextImage
> "Read in the next GIF image from the stream."
>
> | f thisImageColorTable |
>
> localColorTable := nil.
> self readHeader.
> f := self readBody.
> self close.
> + f == nil ifTrue: [^ self error: 'corrupt GIF file' translated].
> - f == nil ifTrue: [^ self error: 'corrupt GIF file'].
>
> thisImageColorTable := localColorTable ifNil: [colorPalette].
> transparentIndex ifNotNil: [
> transparentIndex + 1 > thisImageColorTable size ifTrue: [
> thisImageColorTable := thisImageColorTable
> forceTo: transparentIndex + 1
> paddingWith: Color white
> ].
> thisImageColorTable at: transparentIndex + 1 put: Color
> transparent
> ].
> f colors: thisImageColorTable.
> ^ f
> !
>
> Item was changed:
> ----- Method: GIFReadWriter>>readBitData (in category
> 'private-decoding') -----
> readBitData
> "using modified Lempel-Ziv Welch algorithm."
>
> | outCodes outCount bitMask initCodeSize code curCode oldCode
> inCode finChar i bytes f c packedBits hasLocalColor localColorSize
> maxOutCodes |
>
> maxOutCodes := 4096.
> offset := self readWord at self readWord. "Image Left at Image Top"
> width := self readWord.
> height := self readWord.
>
> "---
> Local Color Table Flag 1 Bit
> Interlace Flag 1 Bit
> Sort Flag 1 Bit
> Reserved 2 Bits
> Size of Local Color Table 3 Bits
> ----"
> packedBits := self next.
> interlace := (packedBits bitAnd: 16r40) ~= 0.
> hasLocalColor := (packedBits bitAnd: 16r80) ~= 0.
> localColorSize := 1 bitShift: ((packedBits bitAnd: 16r7) + 1).
> hasLocalColor ifTrue: [localColorTable := self readColorTable:
> localColorSize].
>
> pass := 0.
> xpos := 0.
> ypos := 0.
> rowByteSize := ((width + 3) // 4) * 4.
> remainBitCount := 0.
> bufByte := 0.
> bufStream := ReadStream on: ByteArray new.
>
> outCodes := ByteArray new: maxOutCodes + 1.
> outCount := 0.
> bitMask := (1 bitShift: bitsPerPixel) - 1.
> prefixTable := Array new: 4096.
> suffixTable := Array new: 4096.
>
> initCodeSize := self next.
>
> self setParameters: initCodeSize.
> + bitsPerPixel > 8 ifTrue: [^self error: ('never heard of a GIF that
> deep (depth = {1})' translated format: {bitsPerPixel})].
> - bitsPerPixel > 8 ifTrue: [^self error: 'never heard of a GIF that
> deep'].
> bytes := ByteArray new: rowByteSize * height.
> [(code := self readCode) = eoiCode] whileFalse:
> [code = clearCode
> ifTrue:
> [self setParameters: initCodeSize.
> curCode := oldCode := code := self
> readCode.
> finChar := curCode bitAnd: bitMask.
> "Horrible hack to avoid running off the
> end of the bitmap. Seems to cure problem reading some gifs!!? tk 6/24/97
> 20:16"
> xpos = 0 ifTrue: [
> ypos < height ifTrue: [
> bytes at: (ypos *
> rowByteSize) + xpos + 1 put: finChar]]
> ifFalse: [bytes at: (ypos *
> rowByteSize) + xpos + 1 put: finChar].
> self updatePixelPosition]
> ifFalse:
> [curCode := inCode := code.
> curCode >= freeCode ifTrue:
> [curCode := oldCode.
> outCodes at: (outCount := outCount
> + 1) put: finChar].
> [curCode > bitMask] whileTrue:
> [outCount > maxOutCodes
> + ifTrue: [^self error:
> ('corrupt GIF file ({1})' translated format: {'OutCount'})].
> - ifTrue: [^self error:
> '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.
> i := outCount.
> [i > 0] whileTrue:
> ["self writePixel: (outCodes at:
> i) to: bits"
> bytes at: (ypos * rowByteSize) +
> xpos + 1 put: (outCodes at: i).
> self updatePixelPosition.
> i := i - 1].
> outCount := 0.
> prefixTable at: freeCode + 1 put: oldCode.
> suffixTable at: freeCode + 1 put: finChar.
> oldCode := inCode.
> freeCode := freeCode + 1.
> self checkCodeSize]].
> prefixTable := suffixTable := nil.
>
> f := ColorForm extent: width at height depth: 8.
> f bits copyFromByteArray: bytes.
> "Squeak can handle depths 1, 2, 4, and 8"
> bitsPerPixel > 4 ifTrue: [^ f].
> "reduce depth to save space"
> c := ColorForm extent: width at height
> depth: (bitsPerPixel = 3 ifTrue: [4] ifFalse:
> [bitsPerPixel]).
> f displayOn: c.
> + ^ c!
> - ^ c
> - !
>
> Item was changed:
> ----- Method: GIFReadWriter>>readHeader (in category 'private-decoding')
> -----
> readHeader
> | is89 byte hasColorMap |
> (self hasMagicNumber: 'GIF87a' asByteArray)
> ifTrue: [is89 := false]
> ifFalse: [(self hasMagicNumber: 'GIF89a' asByteArray)
> ifTrue: [is89 := true]
> + ifFalse: [^ self error: 'This does not appear to
> be a GIF file' translated]].
> - ifFalse: [^ self error: 'This does not appear to
> be a GIF file']].
> 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."
> self next ~= 0
> ifTrue: [is89
> + ifFalse: [^self error: ('corrupt GIF file ({1})'
> translated format: {'screen descriptor'})]].
> - ifFalse: [^self error: 'corrupt GIF file (screen
> descriptor)']].
> hasColorMap
> ifTrue:
> [colorPalette := self readColorTable: (1 bitShift:
> bitsPerPixel)]
> ifFalse:
> ["Transcript cr; show: 'GIF file does not have a
> color map.'."
> colorPalette := nil "Palette monochromeDefault"].!
>
> Item was changed:
> ----- Method: GIFReadWriter>>updatePixelPosition (in category 'private')
> -----
> 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].
>
> + ^pass caseError!
> - ^self error: 'can''t happen'!
>
> Item was changed:
> ----- Method: ImageReadWriter class>>formFromStream: (in category 'image
> reading/writing') -----
> formFromStream: aBinaryStream
> "Answer a ColorForm stored on the given stream. closes the stream"
> | reader readerClass form |
>
> readerClass := self withAllSubclasses
> detect: [:subclass |
> aBinaryStream reset.
> subclass understandsImageFormat: aBinaryStream]
> ifNone: [
> aBinaryStream close.
> + ^self error: 'image format not recognized'
> translated].
> - ^self error: 'image format not recognized'].
> aBinaryStream reset.
> reader := readerClass new on: aBinaryStream.
> Cursor read showWhile: [
> form := reader nextImage.
> reader close].
> ^ form
> !
>
> Item was changed:
> ----- Method:
> ImageReadWriter>>changePadOfBits:width:height:depth:from:to: (in category
> 'private') -----
> 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: ('Invalid pad: {1}' translated
> format: {oldPad})].
> - ifFalse: [^self error: 'Invalid pad: ', oldPad
> printString].
> (#(8 16 32) includes: newPad)
> + ifFalse: [^self error: ('Invalid pad: {1}' translated
> format: {newPad})].
> - ifFalse: [^self error: 'Invalid pad: ', newPad
> printString].
> srcRowByteSize := width * depth + oldPad - 1 // oldPad * (oldPad /
> 8).
> srcRowByteSize * height = bits size
> + ifFalse: [^self error: 'Incorrect bitmap array size.'
> translated].
> - ifFalse: [^self error: '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!
>
> Item was changed:
> ----- Method: ImageReadWriter>>unpackBits:depthTo8From:with:height:pad:
> (in category 'private') -----
> 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 bitIndex upBitIndex val |
> (#(1 2 4) includes: depth)
> + ifFalse: [^self error: 'depth must be 1, 2, or 4'
> translated].
> - ifFalse: [^self error: 'depth must be 1, 2, or 4'].
> (#(8 16 32) includes: pad)
> + ifFalse: [^self error: 'pad must be 8, 16, or 32'
> translated].
> - ifFalse: [^self error: '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 := 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
> !
>
> Item was changed:
> ----- Method: JPEGReadStream>>decodeValueFrom: (in category 'huffman
> trees') -----
> decodeValueFrom: table
> "Decode the next value in the receiver using the given huffman
> table."
> | bits bitsNeeded tableIndex value |
> bitsNeeded := (table at: 1) bitShift: -24. "Initial bits
> needed"
> tableIndex := 2.
> "First real table"
> [bits := self getBits: bitsNeeded. "Get bits"
> value := table at: (tableIndex + bits). "Lookup entry in
> table"
> (value bitAnd: 16r3F000000) = 0] "Check if
> it is a non-leaf node"
> whileFalse:["Fetch sub table"
> tableIndex := value bitAnd: 16rFFFF. "Table
> offset in low 16 bit"
> bitsNeeded := (value bitShift: -24) bitAnd: 255.
> "Additional bits in high 8 bit"
> + bitsNeeded > MaxBits ifTrue:[^self error: 'Invalid
> huffman table entry' translated]].
> - bitsNeeded > MaxBits ifTrue:[^self error:'Invalid
> huffman table entry']].
> ^value!
>
> Item was changed:
> ----- Method: JPEGReadStream>>getBits: (in category 'accessing') -----
> getBits: requestedBits
> | value |
> requestedBits > bitsInBuffer ifTrue:[
> self fillBuffer.
> requestedBits > bitsInBuffer ifTrue:[
> + self error: 'not enough bits available to decode'
> translated]].
> - self error: 'not enough bits available to
> decode']].
> value := bitBuffer bitShift: (requestedBits - bitsInBuffer).
> bitBuffer := bitBuffer bitAnd: (1 bitShift: (bitsInBuffer -
> requestedBits)) -1.
> bitsInBuffer := bitsInBuffer - requestedBits.
> ^ value!
>
> Item was changed:
> ----- Method: JPEGReadWriter>>nextImageDitheredToDepth: (in category
> 'public access') -----
> nextImageDitheredToDepth: depth
>
> | form xStep yStep x y bb |
> ditherMask := DitherMasks
> at: depth
> + ifAbsent: [self error: 'can only dither to display depths'
> translated].
> - ifAbsent: [self error: 'can only dither to display
> depths'].
> residuals := WordArray new: 3.
> sosSeen := false.
> self parseFirstMarker.
> [sosSeen] whileFalse: [self parseNextMarker].
> form := Form extent: (width @ height) depth: depth.
> bb := BitBlt toForm: form.
> bb sourceForm: mcuImageBuffer.
> bb colorMap: (mcuImageBuffer colormapIfNeededFor: form).
> bb sourceRect: mcuImageBuffer boundingBox.
> bb combinationRule: Form over.
> xStep := mcuWidth * DCTSize.
> yStep := mcuHeight * DCTSize.
> y := 0.
> 1 to: mcuRowsInScan do:
> [:row |
> x := 0.
> 1 to: mcusPerRow do:
> [:col |
> self decodeMCU.
> self idctMCU.
> self colorConvertMCU.
> bb destX: x; destY: y; copyBits.
> x := x + xStep].
> y := y + yStep].
> ^ form!
>
> Item was removed:
> - ----- Method: JPEGReadWriter>>notSupported: (in category 'error
> handling') -----
> - notSupported: aString
> -
> - self error: aString , ' is not currently supported'!
>
> Item was changed:
> ----- Method: JPEGReadWriter>>parseAPPn (in category 'marker parsing')
> -----
> parseAPPn
>
> | length buffer thumbnailLength markerStart |
> markerStart := self position.
> length := self nextWord.
> buffer := self next: 4.
> (buffer asString = 'JFIF') ifFalse: [
> "Skip APPs that we're not interested in"
> stream next: length-6.
> ^self].
> self next.
> majorVersion := self next.
> minorVersion := self next.
> densityUnit := self next.
> xDensity := self nextWord.
> yDensity := self nextWord.
> thumbnailLength := self next * self next * 3.
> length := length - (self position - markerStart).
> + length = thumbnailLength ifFalse: [self error: 'APP0 thumbnail
> length is incorrect.' translated].
> - length = thumbnailLength ifFalse: [self error: 'APP0 thumbnail
> length is incorrect.'].
> self next: length!
>
> Item was changed:
> ----- Method: JPEGReadWriter>>parseDecoderRestartInterval (in category
> 'marker parsing') -----
> parseDecoderRestartInterval
>
> | length |
> length := self nextWord.
> + length = 4 ifFalse: [self error: 'DRI length is incorrect.'
> translated].
> - length = 4 ifFalse: [self error: 'DRI length incorrect'].
> restartInterval := self nextWord.!
>
> Item was changed:
> ----- Method: JPEGReadWriter>>parseFirstMarker (in category 'marker
> parsing') -----
> parseFirstMarker
>
> | marker |
> + self next = 16rFF ifFalse: [self error: 'JFIF marker expected'
> translated].
> - self next = 16rFF ifFalse: [self error: 'JFIF marker expected'].
> marker := self next.
> marker = 16rD9
> ifTrue: [^self "halt: 'EOI encountered.'"].
> + marker = 16rD8 ifFalse: [self error: 'SOI marker expected'
> translated].
> - marker = 16rD8 ifFalse: [self error: 'SOI marker expected'].
> self parseStartOfInput.
> !
>
> Item was changed:
> ----- Method: JPEGReadWriter>>parseHuffmanTable (in category 'marker
> parsing') -----
> parseHuffmanTable
>
> | length markerStart index bits count huffVal isACTable hTable |
> markerStart := self position.
> length := self nextWord.
> [self position - markerStart >= length] whileFalse:
> [index := self next.
> isACTable := (index bitAnd: 16r10) ~= 0.
> index := (index bitAnd: 16r0F) + 1.
> index > HuffmanTableSize
> + ifTrue: [self error: ('image has more than {1}
> quantization tables' translated format: {HuffmanTableSize})].
> - ifTrue: [self error: 'image has more than ',
> HuffmanTableSize printString,
> - ' quantization tables'].
> bits := self next: 16.
> count := bits sum.
> (count > 256 or: [(count > (length - (self position -
> markerStart)))])
> + ifTrue: [self error: 'Huffman Table count is
> incorrect' translated].
> - ifTrue: [self error: 'Huffman Table count is
> incorrect'].
> huffVal := self next: count.
> hTable := stream buildLookupTable: huffVal counts: bits.
> isACTable
> ifTrue:
> [self hACTable at: index put: hTable]
> ifFalse:
> [self hDCTable at: index put: hTable]].!
>
> Item was changed:
> ----- Method: JPEGReadWriter>>parseNextMarker (in category 'marker
> parsing') -----
> parseNextMarker
> "Parse the next marker of the stream"
>
> | byte discardedBytes |
> discardedBytes := 0.
> [(byte := self next) = 16rFF] whileFalse: [discardedBytes :=
> discardedBytes + 1].
> [[(byte := self next) = 16rFF] whileTrue. byte = 16r00] whileTrue:
> [discardedBytes := discardedBytes + 2].
> discardedBytes > 0 ifTrue: [self "notifyWithLabel: 'warning:
> extraneous data discarded'"].
> self perform:
> (JFIFMarkerParser
> at: byte
> ifAbsent:
> [(self okToIgnoreMarker: byte)
> ifTrue: [#skipMarker]
> + ifFalse: [self error: ('marker {1}
> cannot be handled' translated format: {byte printStringHex})]])!
> - ifFalse: [self error: 'marker ',
> byte printStringHex , ' cannot be handled']])!
>
> Item was changed:
> ----- Method: JPEGReadWriter>>parseQuantizationTable (in category
> 'marker parsing') -----
> parseQuantizationTable
>
> | length markerStart n prec value table |
> markerStart := self position.
> length := self nextWord.
> [self position - markerStart >= length] whileFalse:
> [value := self next.
> n := (value bitAnd: 16r0F) + 1.
> prec := (value >> 4) > 0.
> n > QuantizationTableSize
> + ifTrue: [self error: ('image has more than {1}
> quantization tables' translated format: {QuantizationTableSize})].
> - ifTrue: [self error: 'image has more than ',
> - QuantizationTableSize printString,
> - ' quantization tables'].
> table := IntegerArray new: DCTSize2.
> 1 to: DCTSize2 do:
> [:i |
> value := (prec
> ifTrue: [self nextWord]
> ifFalse: [self next]).
> table at: (JPEGNaturalOrder at: i) put: value].
> self useFloatingPoint ifTrue: [self
> scaleQuantizationTable: table].
> self qTable at: n put: table]!
>
> Item was changed:
> ----- Method: JPEGReadWriter>>parseStartOfFile (in category 'marker
> parsing') -----
> parseStartOfFile
>
> | length markerStart value n |
> markerStart := self position.
> length := self nextWord.
> dataPrecision := self next.
> dataPrecision = 8
> + ifFalse: [self error: ('cannot handle {1}-bit components'
> translated format: {dataPrecision})].
> - ifFalse: [self error: 'cannot handle ', dataPrecision
> printString, '-bit components'].
> height := self nextWord.
> width := self nextWord.
> n := self next.
> + (height = 0) | (width = 0) | (n = 0) ifTrue: [self error: 'empty
> image' translated].
> - (height = 0) | (width = 0) | (n = 0) ifTrue: [self error: 'empty
> image'].
> (length - (self position - markerStart)) ~= (n * 3)
> + ifTrue: [self error: 'component length is incorrect'
> translated].
> - ifTrue: [self error: 'component length is incorrect'].
> components := Array new: n.
> 1 to: components size do:
> [:i |
> components
> at: i
> put:
> (JPEGColorComponent new
> id: self next;
> "heightInBlocks: (((value := self
> next) >> 4) bitAnd: 16r0F);
> widthInBlocks: (value bitAnd:
> 16r0F);"
> widthInBlocks: (((value := self
> next) >> 4) bitAnd: 16r0F);
> heightInBlocks: (value bitAnd:
> 16r0F);
>
> qTableIndex: self next + 1)]!
>
> Item was changed:
> ----- Method: JPEGReadWriter>>parseStartOfScan (in category 'marker
> parsing') -----
> parseStartOfScan
>
> | length n id value dcNum acNum comp |
> length := self nextWord.
> n := self next.
> + (length ~= (n*2 + 6)) | (n < 1) ifTrue: [self error: 'SOS length
> is incorrect' translated].
> - (length ~= (n*2 + 6)) | (n < 1) ifTrue: [self error: 'SOS length
> is incorrect'].
> currentComponents := Array new: n.
> 1 to: n do: [:i |
> id := self next.
> value := self next.
> dcNum := (value >> 4) bitAnd: 16r0F.
> acNum := value bitAnd: 16r0F.
> comp := components detect: [:c | c id = id].
> comp
> dcTableIndex: dcNum+1;
> acTableIndex: acNum+1.
> currentComponents at: i put: comp].
> ss := self next.
> se := self next.
> value := self next.
> ah := (value >> 4) bitAnd: 16r0F.
> al := value bitAnd: 16r0F.
> self initialSOSSetup.
> self perScanSetup.
> sosSeen := true!
>
> Item was changed:
> ----- Method: JPEGReadWriter>>perScanSetup (in category 'marker
> parsing') -----
> perScanSetup
>
> mcusPerRow := (width / (mcuWidth * DCTSize)) ceiling.
> mcuRowsInScan := (height / (mcuHeight * DCTSize)) ceiling.
> (currentComponents size = 3 or: [currentComponents size = 1])
> + ifFalse: [self error: 'JPEG color space not recognized'
> translated].
> - ifFalse: [self error: 'JPEG color space not recognized'].
> mcuMembership := OrderedCollection new.
> currentComponents withIndexDo:
> [:c :i |
> c priorDCValue: 0.
> mcuMembership addAll: ((1 to: c totalMcuBlocks) collect:
> [:b | i])].
> mcuMembership := mcuMembership asArray.
> mcuSampleBuffer := (1 to: mcuMembership size) collect: [:i |
> IntegerArray new: DCTSize2].
> currentComponents withIndexDo:
> [:c :i |
> c initializeSampleStreamBlocks:
> ((1 to: mcuMembership size)
> select: [:j | i = (mcuMembership
> at: j)]
> thenCollect: [:j | mcuSampleBuffer
> at: j])].
> mcuImageBuffer := Form
> extent: (mcuWidth @ mcuHeight) * DCTSize
> depth: 32.
> restartsToGo := restartInterval.!
>
> Item was changed:
> ----- Method: JPEGReadWriter2>>compress:quality: (in category 'public
> access') -----
> compress: aForm quality: quality
> "Encode the given Form and answer the compressed ByteArray.
> Quality goes from 0 (low) to 100 (high), where -1 means default.
> We can only compress:
> * 32-bit deep Forms
> * -32-bit deep Forms
> * 16-bit deep Forms
> * -16-bit deep Forms
> * GrayScale ColorForms (see #isGrayScale)"
> | sourceForm jpegCompressStruct jpegErrorMgr2Struct buffer
> byteCount |
>
> aForm unhibernate.
>
> sourceForm := self supports8BitGrayscaleJPEGs
> ifTrue: [
> (aForm depth = 32) | (aForm depth = 16) | (aForm
> isGrayScale)
> ifTrue: [aForm]
> ifFalse: [aForm asFormOfDepth: 32 ]]
> ifFalse: [
> (aForm nativeDepth > 0) & ((aForm depth = 32) |
> ((aForm depth = 16) & (aForm width even)))
> ifTrue: [aForm]
> ifFalse: [aForm asFormOfDepth: 32 ]].
>
> jpegCompressStruct := ByteArray new: self
> primJPEGCompressStructSize.
> jpegErrorMgr2Struct := ByteArray new: self
> primJPEGErrorMgr2StructSize.
> buffer := ByteArray new: sourceForm width * sourceForm height +
> 1024.
> byteCount := self primJPEGWriteImage: jpegCompressStruct
> onByteArray: buffer
> form: sourceForm
> quality: quality
> progressiveJPEG: false
> errorMgr: jpegErrorMgr2Struct.
> + byteCount = 0 ifTrue: [self error: 'buffer too small for
> compressed data' translated].
> - byteCount = 0 ifTrue: [self error: 'buffer too small for
> compressed data'].
> ^ buffer copyFrom: 1 to: byteCount
> !
>
> Item was changed:
> ----- Method: JPEGReadWriter2>>nextPutImage:quality:progressiveJPEG: (in
> category 'public access') -----
> nextPutImage: aForm quality: quality progressiveJPEG: progressiveFlag
> "Encode the given Form on my stream with the given settings.
> Quality goes from 0 (low) to 100 (high), where -1 means default. If
> progressiveFlag is true, encode as a progressive JPEG.
> We can compress:
> * 32-bit deep Forms
> * -32-bit deep Forms
> * 16-bit deep
> * -16-bit deep
> * GrayScale ColorForms (see #isGrayScale)"
>
> | sourceForm jpegCompressStruct jpegErrorMgr2Struct buffer
> byteCount |
>
> aForm unhibernate.
>
> sourceForm := self supports8BitGrayscaleJPEGs
> ifTrue: [
> (aForm depth = 32) | (aForm depth = 16) | (aForm
> isGrayScale)
> ifTrue: [aForm]
> ifFalse: [aForm asFormOfDepth: 32 ]]
> ifFalse: [
> (aForm nativeDepth > 0) & ((aForm depth = 32) |
> ((aForm depth = 16) & (aForm width even)))
> ifTrue: [aForm]
> ifFalse: [aForm asFormOfDepth: 32 ]].
>
> jpegCompressStruct := ByteArray new: self
> primJPEGCompressStructSize.
> jpegErrorMgr2Struct := ByteArray new: self
> primJPEGErrorMgr2StructSize.
> buffer := ByteArray new: sourceForm width * sourceForm height +
> 1024.
> "Try to write the image. Retry with a larger buffer if needed."
> [
> byteCount := self primJPEGWriteImage: jpegCompressStruct
> onByteArray: buffer
> form: sourceForm
> quality: quality
> progressiveJPEG: progressiveFlag
> errorMgr: jpegErrorMgr2Struct.
> byteCount = 0 and: [ buffer size < (sourceForm width *
> sourceForm height * 3 + 1024) ] ]
> whileTrue: [ buffer := ByteArray new: buffer size
> * 2 ].
> + byteCount = 0 ifTrue: [ self error: 'buffer too small for
> compressed data' translated ].
> - byteCount = 0 ifTrue: [ self error: 'buffer too small for
> compressed data' ].
> stream next: byteCount putAll: buffer startingAt: 1.
> self close.
> !
>
> Item was changed:
> ----- Method: JPEGReadWriter2>>uncompress:into:doDithering: (in category
> 'public access') -----
> uncompress: aByteArray into: aForm doDithering: ditherFlag
> "Uncompress an image from the given ByteArray into the given Form.
> Fails if the given Form has the wrong dimensions or depth.
> We can read RGB JPEGs into:
> * 32-bit Forms
> * -32-bit Forms
> * 16-bit Forms (with or without dithering!!)
> * -16-bit Forms (with or without dithering!!)
> We can read grayscale JPEGs into:
> * 32-bit Forms
> * -32-bit Forms
> * 16-bit Forms (with or without dithering!!)
> * -16-bit Forms (with or without dithering!!)
> * 8-bit grayScale ColorForms (see #isGrayScale)
> * -8-bit grayScale ColorForms (see #isGrayScale)"
>
> | jpegDecompressStruct jpegErrorMgr2Struct width height components
> |
>
> aForm unhibernate.
>
> jpegDecompressStruct := ByteArray new: self
> primJPEGDecompressStructSize.
> jpegErrorMgr2Struct := ByteArray new: self
> primJPEGErrorMgr2StructSize.
> self
> primJPEGReadHeader: jpegDecompressStruct
> fromByteArray: aByteArray
> errorMgr: jpegErrorMgr2Struct.
> width := self primImageWidth: jpegDecompressStruct.
> height := self primImageHeight: jpegDecompressStruct.
> components := self primImageNumComponents: jpegDecompressStruct.
>
> ((aForm width = width) & (aForm height = height)) ifFalse: [
> + ^ self error: 'form dimensions do not match' translated ].
> - ^ self error: 'form dimensions do not match' ].
> self supports8BitGrayscaleJPEGs
> ifTrue: [
> components = 3
> ifTrue: [
> aForm depth = 8
> + ifTrue: [ ^ self error:
> 'Cannot uncompress multi-channel JPEGs into 8-bit deep forms' translated ]].
> - ifTrue: [ ^ self error:
> 'Cannot uncompress multi-channel JPEGs into 8-bit deep forms' ]].
> components = 1
> ifTrue: [
> aForm depth = 8
> ifTrue: [
> aForm isGrayScale
> + ifFalse: [
> ^ self error: 'Cannot uncompress single-channel JPEGs into 8-bit deep forms
> that are not grayscale' translated ]]]]
> - ifFalse: [
> ^ self error: 'Cannot uncompress single-channel JPEGs into 8-bit deep forms
> that are not grayscale' ]]]]
>
> ifFalse: [
> aForm nativeDepth < 0
> + ifTrue: [ ^ self error: 'Current plugin
> version doesn''t support uncompressing JPEGs into little-endian forms'
> translated ]
> - ifTrue: [ ^ self error: 'Current plugin
> version doesn''t support uncompressing JPEGs into little-endian forms' ]
> ifFalse: [
> aForm depth = 16
> ifTrue: [
> width odd
> + ifTrue: [
> ^ self error: 'Current plugin version doesn''t support uncompressing JPEGs
> with an odd width into 16-bit deep forms' translated ]].
> - ifTrue: [
> ^ self error: 'Current plugin version doesn''t support uncompressing JPEGs
> with an odd width into 16-bit deep forms' ]].
> aForm depth = 8
> + ifTrue: [ ^ self error:
> 'Current plugin version doesn''t support uncompressing JPEGs into 8-bit
> deep forms' translated ]]].
> - ifTrue: [ ^ self error:
> 'Current plugin version doesn''t support uncompressing JPEGs into 8-bit
> deep forms' ]]].
>
> self primJPEGReadImage: jpegDecompressStruct
> fromByteArray: aByteArray
> onForm: aForm
> doDithering: ditherFlag
> + errorMgr: jpegErrorMgr2Struct.!
> - errorMgr: jpegErrorMgr2Struct.
> - !
>
> Item was changed:
> ----- Method: LayoutFrame class>>classVersion (in category 'accessing')
> -----
> classVersion
> + ^ 2 "fractions and offsets are never 'nil' anymore"
> - ^1 "changed treatment of bottomOffset and rightOffset"
> !
>
> Item was added:
> + ----- Method: LayoutFrame class>>withClassVersion: (in category 'objects
> from disk') -----
> + withClassVersion: aVersion
> +
> + aVersion <= self classVersion ifTrue: [^ self].
> + ^ super withClassVersion: aVersion!
>
> Item was changed:
> ----- Method: LayoutFrame>>convertToCurrentVersion:refStream: (in
> category 'objects from disk') -----
> convertToCurrentVersion: varDict refStream: smartRefStrm
> | className oldClassVersion |
>
> "JW 2/1/2001"
> "Since class version isn't passed in varDict, look it up through
> smartRefSrm."
> className := varDict at: #ClassName.
> oldClassVersion := (smartRefStrm structures at: className) first.
> + (oldClassVersion = 0) ifTrue: [ self negateBottomRightOffsets;
> fixup ].
> + (oldClassVersion = 1) ifTrue: [ self fixup ].
> - (oldClassVersion = 0) ifTrue: [ self negateBottomRightOffsets ].
> ^super convertToCurrentVersion: varDict refStream: smartRefStrm.
> !
>
> Item was changed:
> + ----- Method: LayoutFrame>>fixup (in category 'objects from disk') -----
> - ----- Method: LayoutFrame>>fixup (in category 'initialize-release') -----
> fixup
> "Set-up default value for un-initialized layout frames"
>
> "LayoutFrame allInstancesDo: [:e | e fixup]."
>
> leftFraction ifNil: [leftFraction := 0].
> leftOffset ifNil: [leftOffset := 0].
> topFraction ifNil: [topFraction := 0].
> topOffset ifNil: [topOffset := 0].
> rightFraction ifNil: [rightFraction := 0].
> rightOffset ifNil: [rightOffset := 0].
> bottomFraction ifNil: [bottomFraction := 0].
> bottomOffset ifNil: [bottomOffset := 0].!
>
> Item was changed:
> ----- Method: PCXReadWriter>>readPalette (in category
> 'private-decoding') -----
> readPalette
>
> | r g b array |
> + self next = 12 ifFalse: [self error: 'no Color Palette!!'
> translated].
> - self next = 12 ifFalse: [self error: 'no Color Palette!!'].
> array := Array new: (1 bitShift: bitsPerPixel).
> 1 to: array size do:
> [:i |
> r := self next. g := self next. b := self next.
> array at: i put: (Color r: r g: g b: b range: 255)].
> ^ array.
> !
>
> Item was changed:
> + ----- Method: PNGReadWriter class>>computeSwizzleMapForDepth: (in
> category 'class initialization') -----
> - ----- Method: PNGReadWriter class>>computeSwizzleMapForDepth: (in
> category 'as yet unclassified') -----
> computeSwizzleMapForDepth: depth
> "Answer a map that maps pixels in a word to their opposite
> location. Used for 'middle-endian' forms where the byte-order is different
> from the bit order (good joke, eh?)."
> | map swizzled |
> map := Bitmap new: 256.
> depth = 4 ifTrue:[
> 0 to: 255 do:[:pix|
> swizzled := 0.
> swizzled := swizzled bitOr: (((pix bitShift: 0)
> bitAnd: 15) bitShift: 4).
> swizzled := swizzled bitOr: (((pix bitShift: -4)
> bitAnd: 15) bitShift: 0).
> map at: pix+1 put: swizzled.
> ].
> ^ColorMap colors: map
> ].
>
> depth = 2 ifTrue:[
> 0 to: 255 do:[:pix|
> swizzled := 0.
> swizzled := swizzled bitOr: (((pix bitShift: 0)
> bitAnd: 3) bitShift: 6).
> swizzled := swizzled bitOr: (((pix bitShift: -2)
> bitAnd: 3) bitShift: 4).
> swizzled := swizzled bitOr: (((pix bitShift: -4)
> bitAnd: 3) bitShift: 2).
> swizzled := swizzled bitOr: (((pix bitShift: -6)
> bitAnd: 3) bitShift: 0).
> map at: pix+1 put: swizzled.
> ].
> ^ColorMap colors: map
> ].
>
> depth = 1 ifTrue:[
> 0 to: 255 do:[:pix|
> swizzled := 0.
> swizzled := swizzled bitOr: (((pix bitShift: 0)
> bitAnd: 1) bitShift: 7).
> swizzled := swizzled bitOr: (((pix bitShift: -1)
> bitAnd: 1) bitShift: 6).
> swizzled := swizzled bitOr: (((pix bitShift: -2)
> bitAnd: 1) bitShift: 5).
> swizzled := swizzled bitOr: (((pix bitShift: -3)
> bitAnd: 1) bitShift: 4).
> swizzled := swizzled bitOr: (((pix bitShift: -4)
> bitAnd: 1) bitShift: 3).
> swizzled := swizzled bitOr: (((pix bitShift: -5)
> bitAnd: 1) bitShift: 2).
> swizzled := swizzled bitOr: (((pix bitShift: -6)
> bitAnd: 1) bitShift: 1).
> swizzled := swizzled bitOr: (((pix bitShift: -7)
> bitAnd: 1) bitShift: 0).
> map at: pix+1 put: swizzled.
> ].
> ^ColorMap colors: map
> ].
> self error: 'Unrecognized depth'!
>
> Item was changed:
> + ----- Method: PNGReadWriter class>>debugging: (in category 'support')
> -----
> - ----- Method: PNGReadWriter class>>debugging: (in category 'as yet
> unclassified') -----
> debugging: aBoolean
>
> Debugging := aBoolean!
>
> Item was changed:
> + ----- Method: PNGReadWriter class>>initialize (in category 'class
> initialization') -----
> - ----- Method: PNGReadWriter class>>initialize (in category 'as yet
> unclassified') -----
> initialize
> "
> PNGReadWriter initialize
> "
>
> BPP := { #(1 2 4 8 16).
> #(0 0 0 0 0).
> #(0 0 0 24 48).
> #(1 2 4 8 0).
> #(0 0 0 16 32).
> #(0 0 0 0 0).
> #(0 0 0 32 64).
> #(0 0 0 0 0) }.
>
> BlockHeight := #(8 8 4 4 2 2 1).
> BlockWidth := #(8 4 4 2 2 1 1).
>
> StandardColors := Color indexedColors collect:[:aColor|
> Color
> r: (aColor red * 255) truncated / 255
> g: (aColor green * 255) truncated / 255
> b: (aColor blue * 255) truncated / 255.
> ].
>
> StandardSwizzleMaps := Array new: 4.
> #(1 2 4) do:[:i| StandardSwizzleMaps at: i put: (self
> computeSwizzleMapForDepth: i)].!
>
> Item was removed:
> - ----- Method: PNGReadWriter class>>insertMorph:named:into: (in category
> 'as yet unclassified') -----
> - insertMorph: aMorph named: aString into: aBook
> -
> - | newPage |
> -
> - aBook ifNil: [^self].
> - newPage := aBook insertPageLabel: aString morphs: {aMorph}.
> - newPage color: Color lightYellow.
> - newPage extent: (
> - newPage submorphs inject: 10 at 10 into: [ :ex :m |
> - m left: 10.
> - ex max: m width @ m bottom
> - ]
> - ) + (20 at 20).
> - !
>
> Item was changed:
> ----- Method: PNGReadWriter>>nextImage (in category 'accessing') -----
> nextImage
> bigEndian := Smalltalk isBigEndian.
> filtersSeen := Bag new.
> idatChunkStream := nil.
> transparentPixelValue := nil.
> unknownChunks := Set new.
> stream reset.
> stream binary.
> stream skip: 8.
> [stream atEnd] whileFalse: [self processNextChunk].
> "Set up our form"
> palette ifNotNil:
> ["Dump the palette if it's the same as our
> standard palette"
>
> palette = (StandardColors copyFrom: 1 to: palette
> size)
> ifTrue: [palette := nil]].
> (depth <= 8 and: [palette notNil])
> ifTrue:
> [form := ColorForm extent: width @ height depth:
> depth.
> form colors: palette]
> ifFalse: [form := Form extent: width @ height depth:
> depth].
> backColor ifNotNil: [form fillColor: backColor].
> idatChunkStream
> + ifNil: [ self error: 'image data is missing' translated ]
> - ifNil: [ self error: 'image data is missing' ]
> ifNotNil: [ self processIDATChunk ].
> unknownChunks isEmpty
> ifFalse:
> ["Transcript show: ' ',unknownChunks
> asSortedCollection asArray printString."
>
> ].
> self debugging
> ifTrue:
> [Transcript
> cr;
> show: 'form = ' , form printString.
> Transcript
> cr;
> show: 'colorType = ' , colorType
> printString.
> Transcript
> cr;
> show: 'interlaceMethod = ' ,
> interlaceMethod printString.
> Transcript
> cr;
> show: 'filters = ' , filtersSeen
> sortedCounts asArray printString].
> ^form!
>
> Item was changed:
> ----- Method: PNGReadWriter>>processInterlaced (in category 'chunks')
> -----
> processInterlaced
> | z startingCol colIncrement rowIncrement startingRow |
> startingCol := #(0 4 0 2 0 1 0 ).
> colIncrement := #(8 8 4 4 2 2 1 ).
> rowIncrement := #(8 8 8 4 4 2 2 ).
> startingRow := #(0 0 4 0 2 0 1 ).
> z := ZLibReadStream
> on: idatChunkStream originalContents
> from: 1
> to: idatChunkStream position.
> 1 to: 7 do: [:pass |
> | cx sc bytesPerPass |
> (self doPass: pass)
> ifTrue:
> [cx := colIncrement at: pass.
> sc := startingCol at: pass.
> bytesPerPass := width - sc + cx - 1 // cx
> * bitsPerPixel + 7 // 8.
> prevScanline := ByteArray new:
> bytesPerPass.
> thisScanline := ByteArray new:
> bytesPerScanline.
> (startingRow at: pass)
> to: height - 1
> by: (rowIncrement at: pass)
> do: [:y |
> | filter temp |
> filter := z next.
> filtersSeen add: filter.
> (filter isNil or: [(filter
> between: 0 and: 4) not])
> ifTrue: [^ self].
> thisScanline := z next:
> bytesPerPass into: thisScanline startingAt: 1.
> self filterScanline:
> filter count: bytesPerPass.
> self copyPixels: y at: sc
> by: cx.
> temp := prevScanline.
> prevScanline :=
> thisScanline.
> thisScanline := temp.
> ]
> ]
> ].
> + z atEnd ifFalse:[self error: 'Unexpected data' translated].!
> - z atEnd ifFalse:[self error:'Unexpected data'].!
>
> Item was changed:
> ----- Method: PNGReadWriter>>processNextChunk (in category 'chunks')
> -----
> processNextChunk
>
> | length chunkType crc chunkCrc |
>
> length := self nextLong.
>
> chunkType := (self next: 4) asString.
> (chunk isNil or: [ chunk size ~= length ])
> ifTrue: [ chunk := self next: length ]
> ifFalse: [ stream next: length into: chunk startingAt: 1 ].
> chunkCrc := self nextLong bitXor: 16rFFFFFFFF.
> crc := self updateCrc: 16rFFFFFFFF from: 1 to: 4 in: chunkType.
> crc := self updateCrc: crc from: 1 to: length in: chunk.
> crc = chunkCrc ifFalse:[
> + self error: ('PNGReadWriter crc error in chunk {1}'
> translated format: {chunkType}).
> - self error: 'PNGReadWriter crc error in chunk ', chunkType.
> ].
>
> chunkType = 'IEND' ifTrue: [stream setToEnd. ^self "*should*
> be the last chunk"].
> chunkType = 'sBIT' ifTrue: [^self processSBITChunk "could indicate
> unusual sample depth in original"].
> chunkType = 'gAMA' ifTrue: [^self "indicates gamma
> correction value"].
> chunkType = 'bKGD' ifTrue: [^self processBackgroundChunk].
> chunkType = 'pHYs' ifTrue: [^self processPhysicalPixelChunk].
> chunkType = 'tRNS' ifTrue: [^self processTransparencyChunk].
>
> chunkType = 'IHDR' ifTrue: [^self processIHDRChunk].
> chunkType = 'PLTE' ifTrue: [^self processPLTEChunk].
> chunkType = 'IDAT' ifTrue: [
> "---since the compressed data can span multiple
> chunks, stitch them all together first. later,
> if memory is an issue, we need to figure out how
> to do this on the fly---"
> idatChunkStream
> ifNil: [ idatChunkStream := WriteStream with:
> chunk copy ]
> ifNotNil: [ idatChunkStream nextPutAll: chunk ].
> ^self
> ].
> unknownChunks add: chunkType.
> !
>
> Item was changed:
> ----- Method: PNGReadWriter>>processPLTEChunk (in category 'chunks')
> -----
> processPLTEChunk
>
> | colorCount i |
>
> + colorCount := chunk size // 3.
> + self flag: #todo. "validate colorCount against depth"
> - colorCount := chunk size // 3. "TODO - validate colorCount against
> depth"
> palette := Array new: colorCount.
> 0 to: colorCount-1 do: [ :index |
> i := index * 3 + 1.
> palette at: index+1 put:
> (Color r: (chunk at: i)/255.0 g: (chunk at:
> i+1)/255.0 b: (chunk at: i+2)/255.0)
> ].!
>
> Item was changed:
> ----- Method: PNGReadWriter>>writeSBITChunkOn: (in category 'writing')
> -----
> writeSBITChunkOn: aStream
> "Write the IDAT chunk"
> aStream nextPutAll: 'sBIT' asByteArray.
> + form depth = 16 ifFalse: [self notYetImplemented].
> - form depth = 16 ifFalse:[self error: 'Unimplemented feature'].
> aStream nextPut: 5.
> aStream nextPut: 5.
> aStream nextPut: 5.
> aStream nextPut: 1.!
>
> Item was changed:
> ----- Method: PNMReadWriter>>nextImage (in category 'reading') -----
> nextImage
> "read one image"
> | data p |
> first ifNil:[
> first := false.
> data := stream contentsOfEntireFile.
> stream := (RWBinaryOrTextStream with: data) reset.
> ]
> ifNotNil:[
> type < 4 ifTrue:[
> + self error: 'Plain PBM, PGM or PPM have only one
> image' translated
> - self error:'Plain PBM, PGM or PPM have only one
> image'
> ].
> ].
> stream ascii.
> p := stream next.
> type := (stream next) asInteger - 48.
> (p = $P and:[type > 0 and:[type < 8]]) ifFalse:[
> + self error: 'Not a PNM file' translated
> - self error:'Not a PNM file'
> ].
> type = 7 ifTrue:[
> self readHeaderPAM
> ]
> ifFalse: [
> self readHeader
> ].
> type caseOf: {
> [1] -> [^self readPlainBW].
> [2] -> [^self readPlainGray].
> [3] -> [^self readPlainRGB].
> [4] -> [^self readBWreverse: false].
> [5] -> [^self readGray].
> [6] -> [^self readRGB].
> [7] -> [ "PAM"
> (tupleType asUppercase) caseOf: {
> ['BLACKANDWHITE']
> -> [^self readBWreverse: true].
> ['GRAYSCALE']
> -> [^self readGray].
> ['RGB']
> -> [^self readRGB].
> + ['RGB_ALPHA']
> -> [^self notYetImplemented].
> + ['GRAYSCALE_ALPHA'] ->
> [^self notYetImplemented].
> - ['RGB_ALPHA']
> -> [^self error:'Not implemented'].
> - ['GRAYSCALE_ALPHA'] ->
> [^self error:'Not implemented'].
> } otherwise: [^self readData].
> ]
> }!
>
> Item was changed:
> ----- Method: PNMReadWriter>>readGray (in category 'reading') -----
> readGray
> "gray form, return ColorForm with gray ramp"
> | form poker |
> + maxValue > 255 ifTrue:[self error: ('Gray value > {1} bits not
> supported in Squeak' translated format: {8})].
> - maxValue > 255 ifTrue:[self error:'Gray value > 8 bits not
> supported in Squeak'].
> stream binary.
> form := ColorForm extent: cols at rows depth: depth.
> form colors: nil.
> poker := BitBlt bitPokerToForm: form.
> 0 to: rows-1 do: [:y |
> 0 to: cols-1 do: [:x |
> |val|
> val := stream next.
> poker pixelAt: x at y put: val.
> ]
> ].
> "a better way is using a gamma corrected palette"
> form colors: ((0 to: 255) collect:[:c|
> c > maxValue
> ifTrue:[Color white]
> ifFalse:[Color gray: (c/maxValue) asFloat]]).
> form colors at: 1 put: (Color black).
> ^form
> !
>
> Item was changed:
> ----- Method: PNMReadWriter>>readHeader (in category 'reading') -----
> readHeader
> "read header for pbm, pgm or ppm"
> | tokens aux d c |
> tokens := OrderedCollection new.
> aux := self getTokenPbm: tokens.
> cols := aux at: 1. tokens := aux at: 2.
> aux := self getTokenPbm: tokens.
> rows := aux at: 1. tokens := aux at: 2.
>
> (type = 1 or:[type = 4]) ifTrue:[
> maxValue := 1
> ]
> ifFalse: [
> aux := self getTokenPbm: tokens.
> maxValue := aux at: 1. tokens := aux at: 2.
> ].
> d := {1 . 2 . 4 . 8 . 16 . 32}.
> c := {2 . 4 . 16 . 256 . 32768 . 16777216}.
> (type = 3 or:[type = 6]) ifTrue: [
> maxValue >= 65536 ifTrue:[
> + self error: ('Pixmap > {1} bits not supported in
> PPM' translated format: {48})
> - self error:'Pixmap > 48 bits not supported in PPM'
> ].
> maxValue >= 256 ifTrue:[
> + self error: ('Pixmap > {1} bits are not supported
> in Squeak' translated format: {32})
> - self error:'Pixmap > 32 bits are not supported in
> Squeak'
> ].
> maxValue < 32 ifTrue:[depth := 16] ifFalse:[depth := 32].
> ]
> ifFalse: [
> depth := nil.
> 1 to: c size do:[:i| ((c at: i) > maxValue and:[depth =
> nil]) ifTrue:[depth:=d at: i]].
> ].
> Transcript cr; show: 'PBM file class ', type asString, ' size ',
> cols asString, ' x ',
> rows asString, ' maxValue =', maxValue asString, '
> depth=', depth asString.
> !
>
> Item was changed:
> ----- Method: PNMReadWriter>>readPlainBW (in category 'reading') -----
> readPlainBW
> "plain BW"
> | val form poker |
> form := Form extent: cols at rows depth: depth.
> poker := BitBlt bitPokerToForm: form.
> 0 to: rows-1 do: [:y |
> 0 to: cols-1 do: [:x |
> [val := stream next. (val = $0 or:[val = $1])]
> whileFalse:[
> + val ifNil: [self error: 'End of file
> reading PBM' translated].
> - val ifNil:[self error:'End of file reading
> PBM'].
> ].
> poker pixelAt: x at y put: (val asInteger).
> ]
> ].
> ^form
> !
>
> Item was changed:
> ----- Method: PNMReadWriter>>readPlainRGB (in category 'reading') -----
> readPlainRGB
> "RGB form, use 32 bits"
> | val form poker tokens aux |
> + maxValue > 255 ifTrue:[self error: ('RGB value > {1} bits not
> supported in Squeak' translated format: {32})].
> - maxValue > 255 ifTrue:[self error:'RGB value > 32 bits not
> supported in Squeak'].
> form := Form extent: cols at rows depth: 32.
> poker := BitBlt bitPokerToForm: form.
> tokens := OrderedCollection new.
> 0 to: rows-1 do: [:y |
> 0 to: cols-1 do: [:x | | r g b|
> aux := self getTokenPbm: tokens. r := aux at: 1.
> tokens := aux at: 2.
> aux := self getTokenPbm: tokens. g := aux at: 1.
> tokens := aux at: 2.
> aux := self getTokenPbm: tokens. b := aux at: 1.
> tokens := aux at: 2.
> val := self r: r g: g b: b for: depth.
> poker pixelAt: x at y put: val.
> ]
> ].
> ^form
> !
>
> Item was changed:
> ----- Method: PNMReadWriter>>readRGB (in category 'reading') -----
> readRGB
> "RGB form, use 16/32 bits"
> | val form poker sample shift |
> + maxValue > 255 ifTrue:[self error: ('RGB value > {1} bits not
> supported in Squeak' translated format: {32})].
> - maxValue > 255 ifTrue:[self error:'RGB value > 32 bits not
> supported in Squeak'].
> stream binary.
> form := Form extent: cols at rows depth: depth.
> poker := BitBlt bitPokerToForm: form.
> depth = 32 ifTrue:[shift := 8] ifFalse:[shift := 5].
> 0 to: rows-1 do: [:y |
> 0 to: cols-1 do: [:x |
> val := 16rFF. "no transparency"
> 1 to: 3 do: [:i |
> sample := stream next.
> val := val << shift + sample.
> ].
> poker pixelAt: x at y put: val.
> ]
> ].
> ^form
> !
>
> Item was added:
> + ----- Method: Point>>exactCenter: (in category 'converting to
> rectangle') -----
> + exactCenter: aPoint
> + "Answer a Rectangle whose extent is the receiver and whose center
> is exactly aPoint. This is one of the infix ways of expressing the creation
> of a rectangle."
> +
> + ^ Rectangle exactCenter: aPoint extent: self!
>
> Item was added:
> + ----- Method: Rectangle>>pointAtFraction: (in category 'rectangle
> functions') -----
> + pointAtFraction: relativePoint
> +
> + | result |
> + result := self origin + (self extent * relativePoint).
> + ^ self isIntegerRectangle
> + ifTrue: [result rounded]
> + ifFalse: [result]!
>
> Item was added:
> + ----- Method: Rectangle>>randomPoint (in category 'random') -----
> + randomPoint
> +
> + ^ self randomPoint: ThreadSafeRandom value!
>
> Item was added:
> + ----- Method: Rectangle>>randomPoint: (in category 'random') -----
> + randomPoint: aGenerator
> + "Answers a random point that lies within the receiver."
> +
> + ^ self pointAtFraction: aGenerator next @ aGenerator next!
>
> Item was changed:
> AbstractFont subclass: #StrikeFont
> + instanceVariableNames: 'characterToGlyphMap xTable glyphs name
> type minAscii maxAscii maxWidth strikeLength ascent descent xOffset raster
> subscript superscript emphasis derivativeFonts pointSize fallbackFont
> charIndexCompatibilitySlot lineGap lineGapSlice'
> - instanceVariableNames: 'characterToGlyphMap xTable glyphs name
> type minAscii maxAscii maxWidth strikeLength ascent descent xOffset raster
> subscript superscript emphasis derivativeFonts pointSize fallbackFont
> charIndexCompatibilitySlot'
> classVariableNames: 'DefaultStringScanner'
> poolDictionaries: 'TextConstants'
> category: 'Graphics-Fonts'!
>
> !StrikeFont commentStamp: 'fbs 11/28/2013 08:50' prior: 0!
> I represent a compact encoding of a set of Forms corresponding to
> characters in the ASCII character set. All the forms are placed side by
> side in a large form whose height is the font height, and whose width is
> the sum of all the character widths. The xTable variable gives the left-x
> coordinates of the subforms corresponding to the glyphs. Characters are
> mapped to glyphs by using the characterToGyphMap.
>
> Subclasses can have non-trivial mapping rules as well as different
> representations for glyphs sizes (e.g., not using an xTable). If so, these
> classes should return nil when queried for xTable and/or the
> characterToGlyphMap. This will cause the CharacterScanner primitive to fail
> and query the font for the width of a character (so that a more
> programatical approach can be implemented).
>
> For display, fonts need to implement two messages:
> #installOn: aDisplayContext foregroundColor: foregroundColor
> backgroundColor: backgroundColor
> This method installs the receiver (a font) on the given DisplayContext
> (which may be an instance of BitBlt or Canvas (or any of its subclasses).
> The font should take the appropriate action to initialize the display
> context so that further display operations can be optimized.
> #displayString: aString on: aDisplayContext from: startIndex to:
> stopIndex at: aPoint kern: kernDelta
> This method is called for each subsequent run of characters in aString
> which is to be displayed with the (previously installed) settings.
> !
>
> Item was removed:
> - ----- Method: StrikeFont class>>cleanUp (in category 'class
> initialization') -----
> - cleanUp
> - "Flush synthesized strike fonts"
> -
> - self allInstancesDo:[:sf| sf reset].!
>
> Item was added:
> + ----- Method: StrikeFont class>>cleanUp: (in category 'class
> initialization') -----
> + cleanUp: aggressive
> +
> + aggressive ifTrue: [self allInstancesDo: [:sf | sf reset]].!
>
> Item was changed:
> ----- Method: StrikeFont class>>createDejaVu: (in category 'font
> creation') -----
> createDejaVu: pointSize
> "Warning: Uses the methods in 'dejaVu font data' category, that
> will be removed soon (or are already removed) to save space."
>
> + | base bold oblique boldOblique point actualPointSize |
> - | base bold oblique boldOblique point |
> point := pointSize asString.
> + actualPointSize := self fixDejaVuPointSIze: pointSize.
> base := (StrikeFont new
> buildFromForm: (self perform: ('dejaVuSansBook', point,
> 'Form') asSymbol)
> data: (self perform: ('dejaVuSansBook', point, 'Data')
> asSymbol)
> name: 'Bitmap DejaVu Sans ', point)
> + pointSize: actualPointSize.
> - pointSize: pointSize.
> bold := (StrikeFont new
> buildFromForm: (self perform: ('dejaVuSansBold', point,
> 'Form') asSymbol)
> data: (self perform: ('dejaVuSansBold', point, 'Data')
> asSymbol)
> name: 'Bitmap DejaVu Sans ', point, 'B')
> emphasis: 1;
> + pointSize: actualPointSize.
> - pointSize: pointSize.
> oblique := (StrikeFont new
> buildFromForm: (self perform: ('dejaVuSansOblique', point,
> 'Form') asSymbol)
> data: (self perform: ('dejaVuSansOblique', point, 'Data')
> asSymbol)
> name: 'Bitmap DejaVu Sans ', point, 'I')
> emphasis: 2;
> + pointSize: actualPointSize.
> - pointSize: pointSize.
> boldOblique := (StrikeFont new
> buildFromForm: (self perform: ('dejaVuSansBoldOblique',
> point, 'Form') asSymbol)
> data: (self perform: ('dejaVuSansBoldOblique', point,
> 'Data') asSymbol)
> name: 'Bitmap DejaVu Sans ', point, 'BI')
> emphasis: 3;
> + pointSize: actualPointSize.
> - pointSize: pointSize.
>
> base derivativeFont: bold at: 1.
> base derivativeFont: oblique at: 2.
> base derivativeFont: boldOblique at: 3.
>
> ^base!
>
> Item was changed:
> ----- Method: StrikeFont class>>createDejaVuDark: (in category 'font
> creation') -----
> createDejaVuDark: pointSize
>
> + | base bold oblique boldOblique point actualPointSize |
> - | base bold oblique boldOblique point |
> point := pointSize asString.
> + actualPointSize := self fixDejaVuPointSIze: pointSize.
> base := (StrikeFont new
> buildFromForm: (self perform: ('dejaVuSansBookDark',
> point, 'Form') asSymbol)
> data: (self perform: ('dejaVuSansBookDark', point, 'Data')
> asSymbol)
> name: 'Darkmap DejaVu Sans', point)
> + pointSize: actualPointSize.
> - pointSize: pointSize.
> bold := (StrikeFont new
> buildFromForm: (self perform: ('dejaVuSansBoldDark',
> point, 'Form') asSymbol)
> data: (self perform: ('dejaVuSansBoldDark', point, 'Data')
> asSymbol)
> name: 'Darkmap DejaVu Sans', point, 'B')
> emphasis: 1;
> + pointSize: actualPointSize.
> - pointSize: pointSize.
> oblique := (StrikeFont new
> buildFromForm: (self perform: ('dejaVuSansObliqueDark',
> point, 'Form') asSymbol)
> data: (self perform: ('dejaVuSansObliqueDark', point,
> 'Data') asSymbol)
> name: 'Darkmap DejaVu Sans', point, 'I')
> emphasis: 2;
> + pointSize: actualPointSize.
> - pointSize: pointSize.
> boldOblique := (StrikeFont new
> buildFromForm: (self perform:
> ('dejaVuSansBoldObliqueDark', point, 'Form') asSymbol)
> data: (self perform: ('dejaVuSansBoldObliqueDark', point,
> 'Data') asSymbol)
> name: 'Darkmap DejaVu Sans', point, 'BI')
> emphasis: 3;
> + pointSize: actualPointSize.
> - pointSize: pointSize.
>
> base derivativeFont: bold at: 1.
> base derivativeFont: oblique at: 2.
> base derivativeFont: boldOblique at: 3.
>
> ^base!
>
> Item was removed:
> - ----- Method: StrikeFont class>>defaultSized: (in category 'accessing')
> -----
> - defaultSized: aNumber
> - | fonts f |
> - "This used to be the default textstyle, but it needs to be a
> StrikeFont and not a TTCFont and sometimes the default textstyle is a
> TTCFont. So, we use a typical StrikeFont as the default fallback font."
> - fonts := (TextConstants at: #Accuny ifAbsent:[TextStyle default])
> fontArray.
> - f := fonts first.
> - 1 to: fonts size do: [:i |
> - aNumber > (fonts at: i) height ifTrue: [f := fonts at: i].
> - ].
> - ^f
> - !
>
> Item was added:
> + ----- Method: StrikeFont class>>fixDejaVuPointSIze: (in category 'font
> creation') -----
> + fixDejaVuPointSIze: pointSize
> + "Maps the given pointSize to 96 ppi."
> +
> + ^ (Dictionary newFrom: {
> + 7 -> 7.5.
> + 9 -> 10.5.
> + 12 -> 14.5.
> + 14 -> 16.5.
> + 17 -> 19.5.
> + 20 -> 23.5}) at: pointSize
> + !
>
> Item was changed:
> ----- Method: StrikeFont class>>generateDejaVuMethods: (in category
> 'font creation') -----
> generateDejaVuMethods: directory
> "StrikeFont generateDejaVuMethods: 'DejaVu'."
> | dir formTemplate dataTemplate methodCategory |
> methodCategory := #'dejaVu font data'.
> formTemplate := '{1}
> <generated>
> " Form data for {2}. Generated with StrikeFont
> generateDejaVuMethods: ''{3}''"
> ^Form fromBinaryStream: (
> ''{4}''
> ) base64Decoded asByteArray readStream
> '.
> dataTemplate := '{1}
> <generated>
> " Font meta data for {2}. Generated with StrikeFont
> generateDejaVuMethods: ''{3}''"
> ^ #({4})
> '.
> dir := FileDirectory default / directory.
> #('*.txt' 'Data' '*.png' 'Form') pairsDo:
> [:match :suffix | (dir fileNamesMatching: match)
> do:
> [:local | | selector source stringContent|
> " .txt and .png have both length 4"
> selector := (local allButLast: 4)
> asLegalSelector, suffix.
> stringContent := dir
> readOnlyFileNamed: local do:
> [:stream | suffix = 'Data'
> ifTrue: [stream
> contentsOfEntireFile]
> ifFalse: [(stream
> binary; contentsOfEntireFile) base64Encoded]].
> source := (suffix = 'Data' ifTrue:
> [dataTemplate] ifFalse: [formTemplate])
> format: {selector . (local
> allButLast: 4) . directory . stringContent }.
> self class compile: source
> classified: methodCategory]
> + displayingProgress: [:local | 'Generating {1}'
> translated format: {local}]].!
> - displayingProgress: [:local | 'Generating ',
> local]].!
>
> Item was removed:
> - ----- Method: StrikeFont class>>localeChanged (in category 'font
> creation') -----
> - localeChanged
> - self setupDefaultFallbackFont!
>
> Item was added:
> + ----- Method: StrikeFont class>>passwordFont (in category 'instance
> creation') -----
> + passwordFont
> +
> + ^ self passwordFontPointSize: TextStyle defaultFont pointSize!
>
> Item was added:
> + ----- Method: StrikeFont class>>passwordFontPointSize: (in category
> 'instance creation') -----
> + passwordFontPointSize: pointSize
> +
> + ^ FixedFaceFont new passwordFont fontPointSize: pointSize!
>
> Item was changed:
> + ----- Method: StrikeFont>>basicHasGlyphOf: (in category 'private') -----
> - ----- Method: StrikeFont>>basicHasGlyphOf: (in category 'multibyte
> character methods') -----
> basicHasGlyphOf: aCharacter
>
> ^ self hasGlyphForCode: (self codeForCharacter: aCharacter)
> !
>
> Item was added:
> + ----- Method: StrikeFont>>closeHtmlOn: (in category 'html') -----
> + closeHtmlOn: aStream
> +
> + aStream nextPutAll: '</font>'.!
>
> Item was changed:
> ----- Method: StrikeFont>>fallbackFont (in category 'accessing') -----
> fallbackFont
> + "Overwritten to add a cache."
> +
> + ^ fallbackFont ifNil: [fallbackFont := super fallbackFont]!
> - "Answers the fallbackFont for the receiver. The fallback font must
> be some derivative of the receiver since it will not be asked to install
> itself properly on the target BitBlt so rendering a completely different
> font here is simply not possible. The default implementation uses a
> synthetic font that maps all characters to question marks."
> - ^ fallbackFont
> - ifNil: [fallbackFont := FixedFaceFont new errorFont
> baseFont: self]!
>
> Item was added:
> + ----- Method: StrikeFont>>formOf: (in category 'private') -----
> + formOf: aCharacter
> + "Like #characterFormAt: but checks for #hasGlyphOf: and supports
> #fallbackFont."
> +
> + (self hasGlyphOf: aCharacter)
> + ifFalse: [^ self fallbackFont formOf: aCharacter].
> +
> + ^ self characterFormAt: aCharacter!
>
> Item was changed:
> + ----- Method: StrikeFont>>glyphOf: (in category 'private') -----
> - ----- Method: StrikeFont>>glyphOf: (in category 'accessing') -----
> glyphOf: aCharacter
> - "Answer the width of the argument as a character in the receiver."
>
> + self flag: #deprecated.
> + ^ self formOf: aCharacter!
> - | code |
> - (self hasGlyphOf: aCharacter) ifFalse: [
> - fallbackFont ifNotNil: [
> - ^ fallbackFont glyphOf: aCharacter.
> - ].
> - ^ (Form extent: 1 at self height) fillColor: Color white
> - ].
> - code := self codeForCharacter: aCharacter.
> - ^ glyphs copy: (((xTable at: code + 1)@0) corner: (xTable at: code
> +2)@self height).
> - !
>
> Item was changed:
> + ----- Method: StrikeFont>>hasGlyphForCode: (in category 'private') -----
> - ----- Method: StrikeFont>>hasGlyphForCode: (in category 'multibyte
> character methods') -----
> hasGlyphForCode: aCharacterCode
> + "Note that missing glyphs are encoded as -1 in the xTable but to
> speed up the #widthOf: check, the next offset must be adjacent and thus be
> duplicated. For example: #(-1 -1 0 24 -1 -1 -1 24 48 -1 ...). Since
> aCharacterCode is 0-based, that codes offset is at +1 while its width needs
> to consult +2, too. See #widthOf:."
>
> + (aCharacterCode between: self minAscii and: self maxAscii)
> + ifFalse: [^ false].
> + (xTable at: aCharacterCode + 1) >= 0
> + ifFalse: [^ false].
> + (xTable at: aCharacterCode + 2) >= 0
> + ifFalse: [^ false].
> + ^ true!
> - ((aCharacterCode between: self minAscii and: self maxAscii) not)
> ifTrue: [
> - ^ false.
> - ].
> - (xTable at: aCharacterCode + 1) < 0 ifTrue: [
> - ^ false.
> - ].
> - ^ true.
> - !
>
> Item was added:
> + ----- Method: StrikeFont>>lineGap (in category 'accessing') -----
> + lineGap
> + "Historical. The #lineGap has been 2 pixels for all fonts in the
> system for a very long time. Since the #referenceHeight is 14 pixels, use
> that to compute a #lineGap relative to the receivers #pointSize/#pixelSize.
> Also see TTCFont >> #lineGap."
> +
> + ^ lineGap ifNil: [lineGap := ((self height asFloat / self class
> referenceHeight) * 2 "pixels") rounded]!
>
> Item was added:
> + ----- Method: StrikeFont>>lineGapSlice (in category 'accessing') -----
> + lineGapSlice
> + "Cached portion of the receiver's #lineGap, which can be used to
> center one-liners in text fields. Also see TTCFont >> #lineGapSlice."
> +
> + ^ lineGapSlice ifNil: [lineGapSlice := (self lineGap asFloat / 2)
> rounded]!
>
> Item was removed:
> - ----- Method: StrikeFont>>lineGrid (in category 'accessing') -----
> - lineGrid
> - ^ ascent + descent!
>
> Item was removed:
> - ----- Method: StrikeFont>>maxAscii (in category 'accessing') -----
> - maxAscii
> - "Answer the integer that is the last Ascii character value of the
> receiver."
> -
> - ^maxAscii!
>
> Item was added:
> + ----- Method: StrikeFont>>maxCodePoint (in category 'accessing') -----
> + maxCodePoint
> + "Overwritten to configure ranges of glyphs per pre-rendered font."
> +
> + ^maxAscii!
>
> Item was removed:
> - ----- Method: StrikeFont>>minAscii (in category 'accessing') -----
> - minAscii
> - "Answer the integer that is the first Ascii character value of the
> receiver."
> -
> - ^minAscii!
>
> Item was added:
> + ----- Method: StrikeFont>>minCodePoint (in category 'accessing') -----
> + minCodePoint
> + "Overwritten to configure ranges of glyphs per pre-rendered font."
> +
> + ^minAscii!
>
> Item was changed:
> ----- Method: StrikeFont>>newFromStrike: (in category 'file in/out')
> -----
> newFromStrike: fileName
> "Build an instance from the strike font file name. The '.strike'
> extension
> is optional."
>
> | strike startName raster16 |
> name := fileName copyUpTo: $.. "assumes extension (if any) is
> '.strike'"
> strike := FileStream readOnlyFileNamed: name, '.strike.'.
> strike binary.
>
> "strip off direcory name if any"
> startName := name size.
> [startName > 0 and: [((name at: startName) ~= $>) & ((name at:
> startName) ~= $])]]
> whileTrue: [startName := startName - 1].
> name := name copyFrom: startName+1 to: name size.
>
> type := strike nextWord.
> "type is ignored now -- simplest
>
> assumed. Kept here to make
>
> writing and consistency more
>
> straightforward."
> minAscii := strike nextWord.
> maxAscii := strike nextWord.
> maxWidth := strike nextWord.
> strikeLength := strike nextWord.
> ascent := strike nextWord.
> descent := strike nextWord.
> "xOffset :=" strike nextWord.
>
> raster16 := strike nextWord.
>
> superscript := ascent - descent // 3.
> subscript := descent - ascent // 3.
> emphasis := 0.
> glyphs := Form extent: (raster16 * 16) @
> (self height)
> offset: 0 at 0.
> glyphs bits fromByteStream: strike.
>
> xTable := (Array new: maxAscii + 3) atAllPut: 0.
> (minAscii + 1 to: maxAscii + 3) do:
> [:index | xTable at: index put: strike nextWord].
>
> "Set up space character"
> ((xTable at: (Space asciiValue + 2)) = 0 or:
> [(xTable at: (Space asciiValue + 2)) = (xTable at:
> (Space asciiValue + 1))])
> ifTrue: [(Space asciiValue + 2) to: xTable size do:
> + [:index | xTable at: index put:
> ((xTable at: index) + 4 "DefaultSpace")]].
> - [:index | xTable at: index put:
> ((xTable at: index) + DefaultSpace)]].
> strike close.
> characterToGlyphMap := nil.!
>
> Item was added:
> + ----- Method: StrikeFont>>openHtmlOn: (in category 'html') -----
> + openHtmlOn: aStream
> +
> + aStream
> + nextPutAll: '<font face="';
> + nextPutAll: self familyName;
> + nextPutAll: '" size="';
> + nextPutAll: self pointSize asString;
> + nextPutAll: '">'.!
>
> Item was added:
> + ----- Method: StrikeFont>>pixelSize (in category 'accessing') -----
> + pixelSize
> + "Overwritten because the receiver is pre-rendered using a fixed
> pixels-per-inch (PPI) value, usually 96 PPI. Note that you can change the
> #pointSize to match 96 PPI (see TextStyle class >> #pixelsPerInch) as
> follows:
> +
> + self pointSize: ((72 * self pixelSize / 96) roundTo: 0.5).
> + self derivativeFonts do: [:d | d pointSize: font pointSize].
> +
> + Note that a line gap (similar to a TTCFont's #lineGap) for
> pre-rendered fonts is managed via TextStyle's #lineGrid.
> +
> + Also see StrikeFont class >> #referenceHeight."
> +
> + ^ self height!
>
> Item was changed:
> ----- Method: StrikeFont>>postCopy (in category 'copying') -----
> postCopy
> + "The receiver is a just created shallow copy. This method gives it
> the final touch."
> +
> + glyphs := glyphs copy.
> + xTable := xTable copy.
> + characterToGlyphMap := characterToGlyphMap copy.
> + derivativeFonts := derivativeFonts copy.
> +
> + self reset. " takes care of the derivative fonts "!
> - " the receiver is a just created shallow copy. This method gives it the
> final touch. "
> -
> - glyphs := glyphs copy.
> - xTable := xTable copy.
> - characterToGlyphMap := characterToGlyphMap copy.
> -
> - self reset. " takes care of the derivative fonts "!
>
> Item was changed:
> ----- Method: StrikeFont>>printOn: (in category 'file in/out') -----
> printOn: aStream
> +
> super printOn: aStream.
> +
> + aStream nextPut: $(.
> + self printShortDescriptionOn: aStream.
> + aStream nextPut: $).!
> - aStream
> - nextPut: $(;
> - nextPutAll: self name;
> - space;
> - print: self height;
> - nextPut: $)!
>
> Item was changed:
> ----- Method: StrikeFont>>printShortDescriptionOn: (in category
> 'printing') -----
> printShortDescriptionOn: aStream
> +
> + aStream
> + nextPutAll: self familyName;
> + space; print: self pointSize; nextPutAll: 'pt';
> + space; print: self pixelsPerInch; nextPutAll: 'ppi';
> + space; print: self height; nextPutAll: 'px';
> + space; nextPutAll: self emphasisString.!
> - aStream space; nextPutAll: self name!
>
> Item was changed:
> ----- Method: StrikeFont>>readFromStrike2Stream: (in category 'file
> in/out') -----
> readFromStrike2Stream: file
> "Build an instance from the supplied binary stream on data in
> strike2 format"
> type := file nextInt32. type = 2 ifFalse: [file close. self
> error: 'not strike2 format'].
> minAscii := file nextInt32.
> maxAscii := file nextInt32.
> maxWidth := file nextInt32.
> ascent := file nextInt32.
> descent := file nextInt32.
> pointSize := file nextInt32.
> superscript := ascent - descent // 3.
> subscript := descent - ascent // 3.
> emphasis := file nextInt32.
> xTable := (Array new: maxAscii + 3) atAllPut: 0.
> (minAscii + 1 to: maxAscii + 3) do:
> [:index | xTable at: index put: file nextInt32].
> glyphs := Form new readFrom: file.
>
> "Set up space character"
> ((xTable at: (Space asciiValue + 2)) = 0 or:
> [(xTable at: (Space asciiValue + 2)) = (xTable at:
> (Space asciiValue + 1))])
> ifTrue: [(Space asciiValue + 2) to: xTable size do:
> + [:index | xTable at: index put:
> ((xTable at: index) + 4 "DefaultSpace")]].
> - [:index | xTable at: index put:
> ((xTable at: index) + DefaultSpace)]].
> characterToGlyphMap := nil.!
>
> Item was changed:
> ----- Method: StrikeFont>>reset (in category 'emphasis') -----
> reset
> "Reset the cache of derivative emphasized fonts"
>
> + lineGap := lineGapSlice := nil.
> +
> fallbackFont class = FixedFaceFont
> ifTrue: [fallbackFont := nil].
>
> derivativeFonts notNil ifTrue: [
> derivativeFonts withIndexDo: [ :f :i |
> (f notNil and: [f isSynthetic]) ifTrue:
> [derivativeFonts at: i put: nil]]].
> "
> derivativeFonts := Array new: 32.
> #('B' 'I' 'BI') doWithIndex:
> [:tag :index |
> (style := TextStyle named: self familyName) ifNotNil:
> [(font := style fontArray
> detect: [:each | each name = (self name ,
> tag)]
> ifNone: [nil]) ifNotNil: [derivativeFonts
> at: index put: font]]]
> "!
>
> Item was removed:
> - ----- Method: StrikeFont>>setupDefaultFallbackFont (in category
> 'emphasis') -----
> - setupDefaultFallbackFont
> - "This used to be the default textstyle, but it needs to be a
> StrikeFont and not a TTCFont and sometimes the default textstyle is a
> TTCFont. So, we use a typical StrikeFont as the default fallback font."
> - self fallbackFont: (StrikeFont defaultSized: self height).
> - self reset.
> -
> - !
>
> Item was changed:
> ----- Method: StrikeFont>>textStyle (in category 'accessing') -----
> textStyle
> + "Overwritten to not create a new style for orphaned fonts."
> +
> + ^ self textStyleOrNil!
> - ^ TextStyle actualTextStyles detect:
> - [:aStyle | aStyle fontArray includes: self] ifNone: [nil]!
>
> Item was changed:
> ----- Method: TextComposer>>addNullLineWithIndex:andRectangle: (in
> category 'private') -----
> addNullLineWithIndex: index andRectangle: r
> + "TextEditor has emphasisHere, which encodes the emphasis of future
> input. We don't have that info here. Therefore we just use the height of
> the last text line if there is any."
> +
> -
> lines addLast: (
> (
> TextLine
> start: index
> stop: index - 1
> internalSpaces: 0
> paddingWidth: 0
> )
> rectangle: r;
> + lineHeight: (lines
> + ifEmpty: [defaultLineHeight]
> + ifNotEmpty: [lines last lineHeight])
> + baseline: (lines
> + ifEmpty: [theTextStyle baseline]
> + ifNotEmpty: [lines last baseline])
> - lineHeight: defaultLineHeight baseline:
> theTextStyle baseline
> )
> !
>
> Item was changed:
> ----- Method: TextComposer>>composeAllRectangles: (in category
> 'private') -----
> composeAllRectangles: rectangles
>
> | charIndexBeforeLine numberOfLinesBefore reasonForStopping |
>
> + actualHeight := 0.
> - actualHeight := defaultLineHeight.
> charIndexBeforeLine := currCharIndex.
> numberOfLinesBefore := lines size.
> reasonForStopping := self composeEachRectangleIn: rectangles.
>
> currentY := currentY + actualHeight.
> currentY > theContainer bottom ifTrue: [
> "Oops -- the line is really too high to fit -- back out"
> currCharIndex := charIndexBeforeLine.
> lines size - numberOfLinesBefore timesRepeat: [lines
> removeLast].
> ^self
> ].
>
> "It's OK -- the line still fits."
> maxRightX := maxRightX max: scanner rightX.
> 1 to: rectangles size - 1 do: [ :i | |lineIndex|
> "Adjust heights across rectangles if necessary"
> lineIndex:=lines size - rectangles size + i.
> (lines size between: 1 and: lineIndex) ifTrue:
> [(lines at: lineIndex)
> lineHeight: lines last lineHeight
> baseline: lines last baseline]
> ].
> isFirstLine := false.
> reasonForStopping == #columnBreak ifTrue: [^nil].
> currCharIndex > theText size ifTrue: [
> ^nil "we are finished composing"
> ].
> !
>
> Item was changed:
> ----- Method: TextComposer>>composeEachRectangleIn: (in category
> 'private') -----
> composeEachRectangleIn: rectangles
>
> | myLine lastChar |
>
> 1 to: rectangles size do: [:i |
> currCharIndex <= theText size ifFalse: [^false].
> myLine := scanner
> composeFrom: currCharIndex
> inRectangle: (rectangles at: i)
>
> firstLine: isFirstLine
> leftSide: i=1
> rightSide: i=rectangles size.
> lines addLast: myLine.
> + myLine moveByTopMargin.
> + actualHeight := actualHeight max: myLine
> lineHeightWithMargins. "includes font changes and text style's
> #lineSpacing"
> - actualHeight := actualHeight max: myLine lineHeight.
> "includes font changes"
> currCharIndex := myLine last + 1.
> lastChar := theText at: myLine last.
> (CharacterSet crlf includes: lastChar) ifTrue: [^#cr].
> wantsColumnBreaks ifTrue: [
> lastChar = Character characterForColumnBreak
> ifTrue: [^#columnBreak].
> ].
> ].
> ^false!
>
> Item was changed:
> Object subclass: #TextLine
> + instanceVariableNames: 'left right top bottom firstIndex lastIndex
> internalSpaces paddingWidth baseline leftMargin topMargin bottomMargin'
> - instanceVariableNames: 'left right top bottom firstIndex lastIndex
> internalSpaces paddingWidth baseline leftMargin'
> classVariableNames: ''
> poolDictionaries: 'TextConstants'
> category: 'Graphics-Text'!
>
> !TextLine commentStamp: '<historical>' prior: 0!
> A TextLine embodies the layout of a line of composed text.
> left right top bottom The full line rectangle
> firstIndex lastIndex Starting and stopping indices in
> the full text
> internalSpaces Number of spaces to share paddingWidth
> paddingWidth Number of pixels of extra space in full
> line
> baseline Distance of baseline below
> the top of the line
> leftMargin Left margin due to paragraph
> indentation
> TextLine's rather verbose message protocol is required for compatibility
> with the old CharacterScanners.!
>
> Item was added:
> + ----- Method: TextLine>>bottomMargin (in category 'accessing') -----
> + bottomMargin
> +
> + ^ bottomMargin!
>
> Item was added:
> + ----- Method: TextLine>>lineHeightWithMargins (in category 'accessing')
> -----
> + lineHeightWithMargins
> +
> + ^ bottom - top + topMargin + bottomMargin!
>
> Item was added:
> + ----- Method: TextLine>>moveByTopMargin (in category 'updating') -----
> + moveByTopMargin
> +
> + top := top + topMargin.
> + bottom := bottom + topMargin.
> + !
>
> Item was added:
> + ----- Method: TextLine>>topMargin (in category 'accessing') -----
> + topMargin
> +
> + ^ topMargin!
>
> Item was added:
> + ----- Method: TextLine>>topMargin:bottomMargin: (in category 'private')
> -----
> + topMargin: tm bottomMargin: bm
> + topMargin := tm.
> + bottomMargin := bm.!
>
> Item was changed:
> Object subclass: #TextStyle
> + instanceVariableNames: 'fontArray alignment firstIndent restIndent
> rightIndent tabsArray marginTabsArray defaultFontIndex lineSpacing'
> + classVariableNames: 'NumSpacesPerTab'
> - instanceVariableNames: 'fontArray fontFamilySize lineGrid baseline
> alignment firstIndent restIndent rightIndent tabsArray marginTabsArray
> leading defaultFontIndex'
> - classVariableNames: ''
> poolDictionaries: 'TextConstants'
> category: 'Graphics-Text'!
>
> + !TextStyle commentStamp: 'mt 2/21/2022 10:55' prior: 0!
> + A text style comprises the formatting information for composing and
> displaying a unit (usually a paragraph) of text. It provides a #defaultFont
> to use, but text attributes can change that per character (see
> CompositionScanner and DisplayScanner). It also has a default #alignment
> that attributes can override. Those defaults make it possible to compose
> and display (unformatted) strings in paragraphs without having to style
> them first as texts (i.e., string+attributes).
> - !TextStyle commentStamp: '<historical>' prior: 0!
> - A textStyle comprises the formatting information for composing and
> displaying a unit (usually a paragraph) of text. Typically one makes a
> copy of a master textStyle (such as TextStyle default), and then that copy
> may get altered in the process of editing. Bad things can happen if you do
> not copy first.
>
> + NOTE THAT for each use you *must* make a copy of a font's master text
> style (e.g., "TextStyle default copy") or create a fresh one with at least
> a single font (see TextStyle class >> #fontArray: and AbstractFont >>
> #asNewTextStyle). That specific instance is typically altered in the
> process of editing: change default font size, change default alignment, ...
> and you wouldn't want to change that properties for other applications by
> accident.
> - Each of my instances consists of...
> - fontArray An array of StrikeFonts
> - fontFamilySize unused
> - lineGrid An integer; default line spacing
> for paragraphs
> - baseline An integer; default baseline (dist
> from line top to bottom of an 'a')
> - alignment An integer; text alignment, see TextStyle
> alignment:
> - firstIndent An integer; indent of first line in pixels
> - restIndent An integer; indent of remaining lines in
> pixels
> - rightIndent An integer; indent of right margin rel to
> section
> - tabsArray An array of integers giving tab offsets in
> pixels
> - marginTabsArray An array of margin tabs
> - leading An integer giving default vertical line
> separation
>
> + A text style also drives the interpretation of Character tab. Both
> tabsArray and marginTabsArray are initialized for the #defaultFont(Index:).
> When you change a style's default font size, those "tab positions" will be
> recomputed for fast access during composition. See the preference
> #numSpacesPerTab(:).
> +
> + While each text style looks like it could handle an arbitrary array of
> fonts, it is *best practice* to only store fonts of the same font family. A
> font's master style thus collects all known point sizes at a single place
> (i.e. "TextStyle named: aFamilyName"). Copies will share that array. The
> attribute TextFontChange makes it possible to switch to any index in that
> array, but this is not portable and hence discouraged. TextFontReference
> adds an explicit reference to font, which is also not good. (February 2022:
> We plan to add TextFont(Point)Size and TextFontFamily as a portable way to
> change the font per character.).
> +
> + There are some legacy information, which should no longer be used:
> + - #baseline: ... used to prescribe baseline info but is now
> completely derived from #defaultFont
> + - #lineGrid: ... same as #baseLine:
> + - #leading(:) ... is replaced by #lineSpacing(:) and denotes the
> extra spacing relative to the respective line's height in the composition
> +
> + The #lineSpacing is noticeable in a paragraph's text selection. Line
> spacing < 0.0 will appear as overlaps between (translucent) selection
> rectangles. Lince spacing > 0.0 will appear as gaps between selection
> rectangles.
> +
> + Here are some example styles to explore:
> + - TextStyle default
> + - TextStyle defaultFixes!
> - For a concrete example, look at TextStyle default copy inspect!
>
> Item was changed:
> ----- Method: TextStyle class>>actualTextStyles (in category
> 'TextConstants access') -----
> actualTextStyles
> | aDict |
> "TextStyle actualTextStyles"
>
> "Answer dictionary whose keys are the names of styles in the
> system and whose values are the actual styles"
>
> aDict := TextConstants select: [:thang | thang isKindOf: self ].
> + self defaultFamilyNames do: [ :sym | aDict removeKey: sym
> ifAbsent: [] ].
> - self defaultFamilyNames do: [ :sym | aDict removeKey: sym ].
> ^ aDict!
>
> Item was changed:
> ----- Method: TextStyle class>>defaultFamilyNames (in category
> 'TextConstants access') -----
> defaultFamilyNames
> + ^#(DefaultTextStyle DefaultFixedTextStyle DefaultMultiStyle
> DefaultFallbackTextStyle)!
> - ^#(DefaultTextStyle DefaultFixedTextStyle DefaultMultiStyle)!
>
> Item was added:
> + ----- Method: TextStyle class>>defaultTT (in category 'constants') -----
> + defaultTT
> + "Answer the default TrueType text style."
> +
> + ^ self default isTTCStyle
> + ifTrue: [self default]
> + ifFalse: [self named: #BitstreamVeraSans]!
>
> Item was added:
> + ----- Method: TextStyle class>>defaultTTFont (in category 'constants')
> -----
> + defaultTTFont
> +
> + ^ self defaultTT defaultFont!
>
> Item was changed:
> ----- Method: TextStyle class>>fontSizeSummaryContents (in category
> 'utilities') -----
> fontSizeSummaryContents
>
> + ^ Text streamContents: [:aStream |
> + | knownStyles knownTTCStyles knownLegacyStyles
> defaultStyles printBlock |
> + knownStyles := self knownTextStylesWithoutDefault
> sorted.
> + defaultStyles := self defaultFamilyNames sorted.
> +
> + aStream nextPutAll: ('This page lists all known
> text styles and for each style''s font the available point sizes. Most text
> fields offer the {1} where you can choose a different font or point size.
> Note that you can use any new point size for TrueType fonts. This is,
> however, not possible for our pre-rendred legacy fonts. If you need more
> fonts, use the {2} to import TrueType fonts from your current platform.
> Click {3} to browse all styles by example.\\'
> + translated withCRs asText format: {
> + 'FontChooserTool' asText
> + addAttribute:
> (PluggableTextAttribute evalBlock: [FontChooserTool open]); yourself.
> + 'FontImporterTool' asText
> + addAttribute:
> (PluggableTextAttribute evalBlock: [FontImporterTool open]); yourself.
> + 'here' asText
> + addAttribute:
> (PluggableTextAttribute evalBlock: [TextStyle browseAllStyles])}).
> +
> + defaultStyles do: [:styleName |
> + | style prefix |
> + style := self named: styleName.
> + prefix := (style isNil or: [(self named:
> style defaultFamilyName) == style]) ifTrue: [''] ifFalse: [' !! '].
> + aStream
> + nextPutAll: (((styleName padded:
> #left to: 24 with: Character space), ': ', prefix) asText addAttribute:
> (TextFontReference toFont: TextStyle defaultFixedFont); yourself);
> + nextPutAll: (style ifNil: ['-']
> ifNotNil: [(style defaultFamilyName asText addAttribute: (TextFontReference
> toFont: style defaultFont); addAttribute: (PluggableTextAttribute
> evalBlock: [style explore]); yourself)]);
> + cr].
> +
> + printBlock := [:styleName |
> + | style defaultFont
> preferredPointSize exampleFont |
> + style := self named: styleName.
> + preferredPointSize := TextStyle
> defaultFont pointSize. "system's current default"
> + defaultFont := style defaultFont.
> "style's current default"
> + exampleFont := defaultFont
> isSymbolFont
> + ifTrue: [TextStyle
> defaultFont "famiyl name should be legible"]
> + ifFalse: [defaultFont
> asPointSize: preferredPointSize].
> + aStream
> + nextPutAll: (styleName
> asText addAttribute: (TextFontReference toFont: exampleFont)).
> + styleName ~= style
> defaultFamilyName ifTrue: ["style alias"
> + aStream nextPutAll: ' (',
> style defaultFamilyName, ')'].
> + aStream
> + nextPutAll: ((Text new,
> + ((style isTTCStyle
> ifFalse: [''] ifTrue: [' TrueType', (defaultFont isRemoteFont ifFalse: ['']
> ifTrue: [' (remote)']), (defaultFont isSymbolFont ifFalse: [''] ifTrue: ['
> (symbols)'])])
> + asText
> addAttribute: (TextColor color: ((self userInterfaceTheme get:
> #balloonTextColor for: #PluggableTextMorphPlus) ifNil: [Color gray]));
> yourself),
> + (style isTTCStyle
> ifFalse: [''] ifTrue: [ | eg |
> + ' ... '
> asText,
> + ((' ',
> (defaultFont extraGlyphScale * 100) rounded asString, '%') asText
> addAttribute: (PluggableTextAttribute evalBlock: [style chooseExtraScale]);
> yourself),
> + ((' ',
> ((eg := defaultFont extraLineGap) >= 0 ifTrue: ['+', eg asString] ifFalse:
> [eg asString])) asText addAttribute: (PluggableTextAttribute evalBlock:
> [style chooseExtraGap]); yourself) ]),
> + ' ... ',
> + ('explore'
> translated asText addAttribute: (PluggableTextAttribute evalBlock: [style
> explore]); yourself),
> + ' ',
> + ('browse'
> translated asText addAttribute: (PluggableTextAttribute evalBlock:
> [defaultFont browseAllGlyphs; browseAllGlyphsByCategory]); yourself),
> +
> + ' ')
> addAttribute: (TextFontReference toFont: Preferences standardButtonFont);
> yourself);
> + cr.
> + aStream nextPutAll: (((self
> fontPointSizesFor: styleName) inject: ' ' asText into: [:text :pointSize
> |
> + pointSize =
> defaultFont pointSize
> + ifFalse:
> [text, ((pointSize asFloat printShowingDecimalPlaces: 1) padded: #left to:
> 5 with: Character space)]
> + ifTrue:
> [text, (((pointSize asFloat printShowingDecimalPlaces: 1) padded: #left to:
> 5 with: Character space) asText addAttribute: TextEmphasis bold;
> yourself)]]) addAttribute: (TextFontReference toFont: TextStyle
> defaultFixedFont); yourself).
> + aStream cr; cr].
> +
> + knownTTCStyles := knownStyles select: [:ea | (self
> named: ea) isTTCStyle].
> + knownLegacyStyles := knownStyles reject: [:ea |
> (self named: ea) isTTCStyle].
> +
> + aStream cr.
> + knownTTCStyles do: printBlock.
> + aStream nextPutAll: ('The following pre-rendered
> legacy fonts are still available. Note that you can only choose from the
> point sizes that are listed here. Each point size has a pixel size for 96
> PPI. The system scales currently for {1} PPI.' translated format:
> {TextStyle pixelsPerInch}) ; cr; cr.
> + knownLegacyStyles do: printBlock.
> + ].!
> - ^ Text streamContents:
> - [:aStream |
> - self knownTextStyles do: [:aStyleName |
> - aStream nextPutAll:
> - (aStyleName asText addAttribute:
> (TextFontReference toFont: (TextStyle named: aStyleName) defaultFont)), '
> ',
> - (self fontPointSizesFor:
> aStyleName) asArray storeString.
> - aStream cr]].!
>
> Item was removed:
> - ----- Method: TextStyle class>>new (in category 'instance creation')
> -----
> - new
> - ^ super new leading: 2!
>
> Item was added:
> + ----- Method: TextStyle class>>numSpacesPerTab (in category
> 'preferences') -----
> + numSpacesPerTab
> + <preference: 'Tab width (i.e., number of spaces)'
> + categoryList: #(tools visuals Accessibility)
> + description: 'Amount of spaces to be used when calculating
> the width of a tab character for a specific font face and point size.'
> + type: #Number>
> + ^ NumSpacesPerTab ifNil: [6]!
>
> Item was added:
> + ----- Method: TextStyle class>>numSpacesPerTab: (in category
> 'preferences') -----
> + numSpacesPerTab: anInteger
> +
> + anInteger = NumSpacesPerTab ifTrue: [^ self].
> + NumSpacesPerTab := anInteger ifNotNil: [anInteger truncated max:
> 1].
> + TextStyle allInstancesDo: [:ea | ea initializeTabsArray].
> +
> + "Avoid dependency to Morphic project..."
> + (self environment classNamed: #TextMorph)
> + ifNotNil: [:tmClass | tmClass allSubInstancesDo: [:tm |
> + tm releaseParagraph; changed]].!
>
> Item was changed:
> ----- Method: TextStyle class>>pixelsPerInch: (in category 'utilities')
> -----
> pixelsPerInch: aNumber
> "Set the nominal number of pixels per inch to aNumber."
> +
> + self pixelsPerInch = aNumber ifTrue: [^ self].
> + TextConstants at: #pixelsPerInch put: aNumber.
> - TextConstants at: #pixelsPerInch put: aNumber asFloat.
> AbstractFont allSubInstancesDo: [ :font | font
> pixelsPerInchChanged ].
> + TextStyle allInstancesDo: [ :style | style pixelsPerInchChanged ].!
> - Display restore.!
>
> Item was added:
> + ----- Method: TextStyle class>>referenceHeight (in category 'utilities')
> -----
> + referenceHeight
> + "See commentary in RealEstateAgent class >> #scaleFactor."
> +
> + ^ self default isTTCStyle
> + ifTrue: [self pointsToPixels: TTCFont referencePointSize]
> + ifFalse: [self defaultFont height]!
>
> Item was changed:
> + ----- Method: TextStyle>>baseline (in category 'accessing - default
> font') -----
> - ----- Method: TextStyle>>baseline (in category 'accessing') -----
> baseline
> "Answer the distance from the top of the line to the bottom of
> most of the
> characters (by convention, bottom of the letter 'A')."
>
> + ^ self defaultFont ascent!
> - ^baseline!
>
> Item was changed:
> + ----- Method: TextStyle>>baseline: (in category 'accessing - default
> font') -----
> - ----- Method: TextStyle>>baseline: (in category 'accessing') -----
> baseline: anInteger
> - "Set the distance from the top of the line to the bottom of most
> of the
> - characters."
>
> + self flag: #deprecated. "Either change #defaultFont in this style
> or use custom fonts via text attributes."!
> - baseline := anInteger!
>
> Item was changed:
> ----- Method: TextStyle>>consistOnlyOf: (in category 'fonts and font
> indexes') -----
> consistOnlyOf: aFont
> +
> + self deprecated.
> + ^ self newFontArray: {aFont}!
> - fontArray := Array with: aFont.
> - defaultFontIndex := 1!
>
> Item was added:
> + ----- Method: TextStyle>>defaultFamilyName (in category 'accessing -
> default font') -----
> + defaultFamilyName
> + ^ self defaultFont familyName!
>
> Item was changed:
> + ----- Method: TextStyle>>defaultFont (in category 'accessing - default
> font') -----
> - ----- Method: TextStyle>>defaultFont (in category 'accessing') -----
> defaultFont
> ^ fontArray at: self defaultFontIndex!
>
> Item was changed:
> + ----- Method: TextStyle>>defaultFontIndex (in category 'accessing -
> default font') -----
> - ----- Method: TextStyle>>defaultFontIndex (in category 'default font')
> -----
> defaultFontIndex
> ^ defaultFontIndex ifNil: [defaultFontIndex := 1]!
>
> Item was changed:
> + ----- Method: TextStyle>>defaultFontIndex: (in category 'accessing -
> default font') -----
> - ----- Method: TextStyle>>defaultFontIndex: (in category 'default font')
> -----
> defaultFontIndex: anIndex
> +
> + defaultFontIndex := anIndex.
> + self initializeTabsArray.!
> - defaultFontIndex := anIndex!
>
> Item was changed:
> ----- Method: TextStyle>>gridForFont:withLead: (in category 'private')
> -----
> gridForFont: fontIndex withLead: leadInteger
> "Force whole style to suit one of its fonts. Assumes only one font
> referred
> to by runs."
> +
> + self flag: #deprecated.
> + self defaultFontIndex: fontIndex.!
> - | font |
> - font := self fontAt: fontIndex.
> - self lineGrid: font height + leadInteger.
> - self baseline: font ascent.
> - self leading: leadInteger!
>
> Item was added:
> + ----- Method: TextStyle>>initializeTabsArray (in category
> 'initialize-release') -----
> + initializeTabsArray
> +
> + | fontToUse numSpacesPerTab tabWidth maxWidth |
> + self flag: #discuss. "mt: Add cache per font and pointSize? Maybe
> it is not worth it..."
> +
> + numSpacesPerTab := self class numSpacesPerTab.
> + fontToUse := self defaultFont.
> + maxWidth := Display width max: 3840.
> + tabWidth := ((fontToUse widthOf: Character space) max: 1 "For tiny
> point sizes...") * numSpacesPerTab.
> +
> + "Note that using Interval via #to:by: and #asArray would be about
> 4x slower."
> + tabsArray := Array new: maxWidth // tabWidth.
> + 1 to: tabsArray size do: [:i | tabsArray at: i put: tabWidth * i].
> +
> + marginTabsArray := Array new: (maxWidth // tabWidth) // 2.
> + 1 to: marginTabsArray size do: [:i | | offset |
> + marginTabsArray at: i put: (Array with: (offset :=
> tabWidth * i) with: offset)].!
>
> Item was changed:
> + ----- Method: TextStyle>>leading (in category 'accessing - default
> font') -----
> - ----- Method: TextStyle>>leading (in category 'accessing') -----
> leading
> "Leading (from typographers historical use of extra lead (type
> metal))
> is the extra spacing above and beyond that needed just to
> accomodate
> the various font heights in the set."
> +
> + self flag: #deprecated. "mt: Fonts provide their #lineGap (and
> #lineGapSlice) in the CompositionScanner to accommodate various font
> heights in a set. Use #lineSpacing to define a factor that moves the lines
> further apart."
> + ^ 0!
> - ^ leading!
>
> Item was changed:
> + ----- Method: TextStyle>>leading: (in category 'accessing - default
> font') -----
> - ----- Method: TextStyle>>leading: (in category 'accessing') -----
> leading: yDelta
>
> + self flag: #deprecated. "See commentary in #leading."!
> - leading := yDelta!
>
> Item was changed:
> + ----- Method: TextStyle>>lineGrid (in category 'accessing - default
> font') -----
> - ----- Method: TextStyle>>lineGrid (in category 'accessing') -----
> lineGrid
> "Answer the relative space between lines of a paragraph in the
> style of
> the receiver."
>
> + ^ self defaultFont lineGrid!
> - ^lineGrid!
>
> Item was changed:
> + ----- Method: TextStyle>>lineGrid: (in category 'accessing - default
> font') -----
> - ----- Method: TextStyle>>lineGrid: (in category 'accessing') -----
> lineGrid: anInteger
> - "Set the relative space between lines of a paragraph in the style
> of the
> - receiver to be the argument, anInteger."
>
> + self flag: #deprecated. "Either change #defaultFont in this style
> or use custom fonts via text attributes."!
> - lineGrid := anInteger!
>
> Item was added:
> + ----- Method: TextStyle>>lineSpacing (in category 'accessing') -----
> + lineSpacing
> + "Answer the factor that is used to compute extra spacing between
> text lines. The default is 0.0, which means that the CompositionScanner
> will just rely on the various font metrics in a line. There will be 0% of
> extra spacing. Use the current line height as blank space with a factor of
> 1.0 and so on (i.e. the common misnomer 'double line space')."
> +
> + ^ lineSpacing ifNil: [0.0]!
>
> Item was added:
> + ----- Method: TextStyle>>lineSpacing: (in category 'accessing') -----
> + lineSpacing: aFactor
> +
> + lineSpacing := aFactor.!
>
> Item was changed:
> ----- Method: TextStyle>>newFontArray: (in category 'private') -----
> newFontArray: anArray
> "Currently there is no supporting protocol for changing these
> arrays. If an editor wishes to implement margin setting, then a copy of the
> default should be stored with these instance variables.
> , Make size depend on first font."
>
> fontArray := anArray.
> + self defaultFontIndex: 1.
> - lineGrid := (fontArray at: 1) height + leading. "For whole family"
> - baseline := (fontArray at: 1) ascent + leading.
> alignment := 0.
> firstIndent := 0.
> restIndent := 0.
> rightIndent := 0.
> - tabsArray := DefaultTabsArray.
> - marginTabsArray := DefaultMarginTabsArray
> "
> TextStyle allInstancesDo: [:ts | ts newFontArray: TextStyle default
> fontArray].
> "!
>
> Item was added:
> + ----- Method: TextStyle>>pixelsPerInchChanged (in category
> 'notifications') -----
> + pixelsPerInchChanged
> +
> + self reset.!
>
> Item was changed:
> ----- Method: TextStyle>>printOn: (in category 'printing') -----
> printOn: aStream
>
> super printOn: aStream.
> + aStream nextPut: $(.
> + aStream print: fontArray size.
> + aStream nextPut: $).
> + aStream space.
> + self defaultFont printShortDescriptionOn: aStream.
> - self defaultFont printShortDescriptionOn: aStream
> !
>
> Item was added:
> + ----- Method: TextStyle>>reset (in category 'initialize-release') -----
> + reset
> + "Reset values cached from the receiver's default font."
> +
> + self defaultFontIndex: self defaultFontIndex.!
>
> Item was changed:
> ----- Method: TextStyle>>tabWidth (in category 'tabs and margins') -----
> tabWidth
> "Answer the width of a tab."
>
> + ^ tabsArray at: 1 ifAbsent: [24]!
> - ^DefaultTab!
>
> Item was changed:
> + ----- Method: TranslucentColor>>isOpaque (in category 'testing') -----
> - ----- Method: TranslucentColor>>isOpaque (in category 'queries') -----
> isOpaque
> ^alpha = 255!
>
> Item was changed:
> + ----- Method: TranslucentColor>>isTranslucent (in category 'testing')
> -----
> - ----- Method: TranslucentColor>>isTranslucent (in category 'queries')
> -----
> isTranslucent
> ^ alpha < 255!
>
> Item was changed:
> + ----- Method: TranslucentColor>>isTranslucentColor (in category
> 'testing') -----
> - ----- Method: TranslucentColor>>isTranslucentColor (in category
> 'queries') -----
> isTranslucentColor
> "This means: self isTranslucent, but isTransparent not"
> ^ alpha > 0!
>
> Item was changed:
> + ----- Method: TranslucentColor>>isTransparent (in category 'testing')
> -----
> - ----- Method: TranslucentColor>>isTransparent (in category 'queries')
> -----
> isTransparent
> ^ alpha = 0!
>
> Item was added:
> + ----- Method: TranslucentColor>>printOn: (in category 'printing') -----
> + printOn: aStream
> + | name |
> + self isTransparent ifTrue: [^ aStream nextPutAll: 'Color
> transparent'].
> + (name := self asNontranslucentColor name) ifNil: [^self storeOn:
> aStream].
> + aStream
> + nextPutAll: '(Color ';
> + nextPutAll: name;
> + nextPutAll: ' alpha: ';
> + print: self alpha maxDecimalPlaces: 3;
> + nextPut: $)
> + !
>
> Item was changed:
> ----- Method: TranslucentColor>>storeOn: (in category 'printing') -----
> storeOn: aStream
>
> + self isTransparent ifTrue: [^ aStream nextPutAll: 'Color
> transparent'].
> - self isTransparent ifTrue: [^ aStream nextPutAll: '(Color
> transparent)'].
> super storeOn: aStream.
> aStream
> skip: -1; "get rid of trailing )"
> nextPutAll: ' alpha: ';
> print: self alpha maxDecimalPlaces: 3;
> nextPutAll: ')'.
> !
>
> Item was changed:
> + (PackageInfo named: 'Graphics') postscript: '"Compute the correct tab
> widths per style using its current default font."
> + TextStyle allInstancesDo: [:ea | ea initializeTabsArray].'!
> - (PackageInfo named: 'Graphics') postscript: 'Smalltalk
> removeFromStartUpList: DisplayScreen. "see Project class >> #startUp"
> - Smalltalk removeFromShutDownList: DisplayScreen. "see Project class >>
> #shutDown"'!
>
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20220306/1fd09f09/attachment-0001.html>
More information about the Squeak-dev
mailing list
|