diff --git a/src/Alexandrie-Cairo-Tests/AeCairoContextTest.class.st b/src/Alexandrie-Cairo-Tests/AeCairoContextTest.class.st index 31b0b6b..19f57cc 100644 --- a/src/Alexandrie-Cairo-Tests/AeCairoContextTest.class.st +++ b/src/Alexandrie-Cairo-Tests/AeCairoContextTest.class.st @@ -213,21 +213,6 @@ AeCairoContextTest >> testGlyphExtentsOf [ self assert: aTextExtents advance closeTo: 34@0 ] -{ #category : #tests } -AeCairoContextTest >> testLineWidth [ - - surface := AeCairoImageSurface extent: 1@1. - context := surface newContext. - - "Assert on default value" - self assert: context lineWidth equals: 2.0. - - context lineWidth: 7. - - "Assert on new value" - self assert: context lineWidth equals: 7.0 -] - { #category : #tests } AeCairoContextTest >> testOperator [ @@ -264,7 +249,7 @@ AeCairoContextTest >> testPathExtents [ scaleByX: 0.5 y: 2; moveTo: 5 @ 10; lineTo: 90 @ 50; - fillPreserve. + fillPreservingPath. self assert: context pathExtentsInUserSpace @@ -354,12 +339,12 @@ AeCairoContextTest >> testStrokeExtents [ equals: (Rectangle origin: 0 asPoint extent: 0 asPoint). context - translateBy: -1 @ -2; + translateByX: -1 y: -2; scaleByX: 0.5 y: 2; - moveTo: 5 @ 10; - lineTo: 90 @ 50; - lineWidth: 10; - strokePreserve. + moveToX: 5 y: 10; + lineToX: 90 y: 50; + strokeSize: 10; + strokePreservingPath. self assert: context strokeExtentsInUserSpace @@ -378,3 +363,18 @@ AeCairoContextTest >> testStrokeExtents [ equals: (Rectangle origin: -1 @ -2 extent: 0 asPoint). ] + +{ #category : #tests } +AeCairoContextTest >> testStrokeSize [ + + surface := AeCairoImageSurface extent: 1@1. + context := surface newContext. + + "Assert on default value" + self assert: context strokeSize equals: 2.0. + + context strokeSize: 7. + + "Assert on new value" + self assert: context strokeSize equals: 7.0 +] diff --git a/src/Alexandrie-Cairo-Tests/AeCairoExamplesRenderTest.class.st b/src/Alexandrie-Cairo-Tests/AeCairoExamplesRenderTest.class.st index a2a9202..6094506 100644 --- a/src/Alexandrie-Cairo-Tests/AeCairoExamplesRenderTest.class.st +++ b/src/Alexandrie-Cairo-Tests/AeCairoExamplesRenderTest.class.st @@ -288,24 +288,24 @@ AeCairoExamplesRenderTest >> surfaceWithDottedCircle [ "stroke with black squared dots" aContext sourceColor: Color black; - lineCap: AeCairoCapStyle square; - lineWidth: lineWidth; - strokePreserve. + useLineCapSquare; + strokeSize: lineWidth; + strokePreservingPath. self assert: aContext dashes equals: dashByteArray. self assert: aContext hasCurrentPoint. "stroke with green circle dots" aContext sourceColor: Color green; - lineWidth: lineWidth/1.5; - lineCap: AeCairoCapStyle round; - strokePreserve. + strokeSize: lineWidth/1.5; + useLineCapRound; + strokePreservingPath. self assert: aContext dashes equals: dashByteArray. "stroke red without dashes" aContext sourceColor: Color red; - lineWidth: 1; + strokeSize: 1; dashDisable; stroke. self assert: aContext dashes equals: #(). @@ -600,7 +600,7 @@ AeCairoExamplesRenderTest >> surfaceWithRoundedRectangle [ bl: aRectangle height / 2. aContext - lineWidth: 10; + strokeSize: 10; sourceColor: (Color blue alpha: 0.5); stroke. @@ -772,7 +772,7 @@ AeCairoExamplesRenderTest >> surfaceWithStraightEdgesCurve [ aContext sourceColor: Color white; - lineWidth: lineWidth; + strokeSize: lineWidth; cubicCurveFrom: startPoint controlPoint1: startPoint + (0 @ 35.4) controlPoint2: startPoint + (39.4 @ 64) @@ -880,7 +880,7 @@ AeCairoExamplesRenderTest >> surfaceWithTextAsPath [ aContext sourceColor: Color blue; dash: (AeFFIDoubleArray newFrom: { 7. 1 }) offset: 3; - lineWidth: 1.5; + strokeSize: 1.5; stroke. ^ aSurface @@ -960,10 +960,10 @@ AeCairoExamplesRenderTest >> surfaceWithToyAPISelectFontSlantWeight [ fontSize: fontSize; translateByX: 2 y: fontSize; appendTextPath: string; - lineWidth: 3; + strokeSize: 3; sourceColor: Color white; - useLineJoinRound; - strokePreserve; + useStrokeJoinRound; + strokePreservingPath; sourceColor: Color purple; fill. diff --git a/src/Alexandrie-Cairo-Tests/AeCairoMigratedRenderTest.class.st b/src/Alexandrie-Cairo-Tests/AeCairoMigratedRenderTest.class.st index 092bae0..ae05468 100644 --- a/src/Alexandrie-Cairo-Tests/AeCairoMigratedRenderTest.class.st +++ b/src/Alexandrie-Cairo-Tests/AeCairoMigratedRenderTest.class.st @@ -113,7 +113,7 @@ AeCairoMigratedRenderTest >> surfaceWithFillAndStroke [ aContext rectangle: (pad asPoint extent: size asPoint); sourceColor: Color blue; - fillPreserve; + fillPreservingPath; sourceColor: Color red; stroke. @@ -123,7 +123,7 @@ AeCairoMigratedRenderTest >> surfaceWithFillAndStroke [ circleCenterX: pad + (size/2) y: pad + (size/2) radius: size/2; - fillPreserve; + fillPreservingPath; sourceColor: Color blue; stroke. @@ -153,7 +153,7 @@ AeCairoMigratedRenderTest >> surfaceWithFillAndStrokeAlpha [ pathBlock value. aContext source: fillPattern; - fillPreserve; + fillPreservingPath; source: strokePattern; stroke ] alpha: 0.5 ]. @@ -209,13 +209,13 @@ AeCairoMigratedRenderTest >> surfaceWithFillAndStrokeAlphaAdd [ pathBlock value. aContext source: fillPattern; - fillPreserve. + fillPreservingPath. "Use DEST_OUT to subtract stroke from fill." aContext sourceColor: Color black; operator: AeCairoOperator destOut; - strokePreserve. + strokePreservingPath. "Then use ADD to draw the stroke without a seam." aContext @@ -494,7 +494,7 @@ AeCairoMigratedRenderTest >> surfaceWithGroupClip [ aContext rectangle: (25@25 extent: surfaceSize); - clipPreserve; + clipPreservingPath; pushGroup; sourceColor: Color blue; fill; @@ -504,7 +504,7 @@ AeCairoMigratedRenderTest >> surfaceWithGroupClip [ aContext resetClip; - clipPreserve; + clipPreservingPath; sourceColor: (Color red alpha: 0.5); paint. @@ -584,7 +584,7 @@ AeCairoMigratedRenderTest >> surfaceWithInvertedClip [ radius: 40 startAngle: 0 endAngle: Float twoPi; - clipPreserve; + clipPreservingPath; paint. aContext diff --git a/src/Alexandrie-Cairo-Tests/AeCairoRecordingSurfaceRenderTest.class.st b/src/Alexandrie-Cairo-Tests/AeCairoRecordingSurfaceRenderTest.class.st index 40fe503..169bdc9 100644 --- a/src/Alexandrie-Cairo-Tests/AeCairoRecordingSurfaceRenderTest.class.st +++ b/src/Alexandrie-Cairo-Tests/AeCairoRecordingSurfaceRenderTest.class.st @@ -43,7 +43,7 @@ AeCairoRecordingSurfaceRenderTest >> surfaceWithBounds [ aRecordingSurface newContext sourceColor: (Color cyan alpha: 0.8); rectangle: aRectangle; - lineWidth: 10; + strokeSize: 10; stroke. "Replay the recorded surface on a target surface" @@ -85,7 +85,7 @@ AeCairoRecordingSurfaceRenderTest >> surfaceWithNoBounds [ aRecordingSurface newContext sourceColor: (Color cyan alpha: 0.8); rectangle: aRectangle; - lineWidth: 10; + strokeSize: 10; stroke. "Replay the recorded surface on a target surface" diff --git a/src/Alexandrie-Cairo-Tests/AeCairoRecordingSurfaceTest.class.st b/src/Alexandrie-Cairo-Tests/AeCairoRecordingSurfaceTest.class.st index f946943..7829c36 100644 --- a/src/Alexandrie-Cairo-Tests/AeCairoRecordingSurfaceTest.class.st +++ b/src/Alexandrie-Cairo-Tests/AeCairoRecordingSurfaceTest.class.st @@ -18,7 +18,7 @@ AeCairoRecordingSurfaceTest >> testInkExtentsInBoundedSurface [ aRecordingSurface newContext sourceColor: Color cyan; rectangle: aRectangle; - lineWidth: 10; + strokeSize: 10; stroke. self @@ -40,7 +40,7 @@ AeCairoRecordingSurfaceTest >> testInkExtentsInUnboundedSurface [ aRecordingSurface newContext sourceColor: Color cyan; rectangle: aRectangle; - lineWidth: 10; + strokeSize: 10; stroke. self diff --git a/src/Alexandrie-Cairo/AeCairoContext.class.st b/src/Alexandrie-Cairo/AeCairoContext.class.st index 7f0a5c8..369522c 100644 --- a/src/Alexandrie-Cairo/AeCairoContext.class.st +++ b/src/Alexandrie-Cairo/AeCairoContext.class.st @@ -401,13 +401,29 @@ AeCairoContext >> clipPreserve [ self ffiCall: #( void cairo_clip_preserve ( self ) ) ] +{ #category : #'API - clipping' } +AeCairoContext >> clipPreservingPath [ + "Establishes a new clip region by intersecting the current clip region with the current path as it would be filled according to the current fill rule. + + Preserve the path within the cairo context. + + The current clip region affects all drawing operations by effectively masking out any changes to the surface that are outside the current clip region. + + This operation can only make the clip region smaller, never larger. But the current clip is part of the graphics state, so a temporary restriction of the clip region can be achieved by calling `cairo_clip_preserve()` within a `cairo_save()`/`cairo_restore()` pair. + The only other means of increasing the size of the clip region is `cairo_reset_clip()`. + + See: https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-clip-preserve" + + self clipPreserve +] + { #category : #'API - clipping' } AeCairoContext >> clipPreservingPath: aBoolean [ "Establishes a new clip region by intersecting the current clip region with the current path as it would be filled according to the current fill rule. The argument indicates whether the path must be preserved within the cairo context." aBoolean - ifTrue: [ self clipPreserve ] + ifTrue: [ self clipPreservingPath ] ifFalse: [ self clip ] ] @@ -627,13 +643,21 @@ AeCairoContext >> fillPreserve [ self ffiCall: #( void cairo_fill_preserve ( self ) ) ] +{ #category : #'API - painting' } +AeCairoContext >> fillPreservingPath [ + "Fill the current path according to the current fill rule, source, and other settings. + Preserve the path within the cairo context." + + self fillPreserve +] + { #category : #'API - painting' } AeCairoContext >> fillPreservingPath: aBoolean [ "Fill the current path according to the current fill rule. The argument indicates whether the path must be preserved within the cairo context." aBoolean - ifTrue: [ self fillPreserve ] + ifTrue: [ self fillPreservingPath ] ifFalse: [ self fill ] ] @@ -1024,7 +1048,8 @@ AeCairoContext >> lineWidth [ { #category : #'API - painting settings' } AeCairoContext >> lineWidth: aNumber [ - "Sets the current line width within the cairo context. The line width value specifies the diameter of a pen that is circular in user space, (though device-space pen may be an ellipse in general due to scaling/shear/rotation of the CTM). + "Sets the current line width to be used in next stroke operations. + The line width value specifies the diameter of a pen that is circular in user space, (though device-space pen may be an ellipse in general due to scaling/shear/rotation of the CTM). See: https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-line-width" @@ -1839,6 +1864,14 @@ AeCairoContext >> stroke [ self ffiCall: #( void cairo_stroke ( self ) ) ] +{ #category : #'API - painting settings' } +AeCairoContext >> strokeCap: aCairoCapStyle [ + "Sets the current line cap style within the cairo context. + Default line cap style is butt." + + self lineCap: aCairoCapStyle +] + { #category : #'API - query' } AeCairoContext >> strokeExtentsInDeviceSpace [ @@ -1881,6 +1914,28 @@ AeCairoContext >> strokeExtentsInUserSpace [ (rightPointer doubleAt: 1) @ (bottomPointer doubleAt: 1) ] +{ #category : #'API - painting settings' } +AeCairoContext >> strokeJoin: aCairoJoinStyle [ + "Sets the current line join style within the cairo context. + + The default line join style is miter." + + self lineJoin: aCairoJoinStyle +] + +{ #category : #'API - painting settings' } +AeCairoContext >> strokeJoinMiterLimit: aNumber [ + "Set the current miter limit, to be used in next stroke operation if join is miter. + + The default miter limit value is 10.0, which will convert joins with interior angles less than 11 degrees to bevels instead of miters. For reference, a miter limit of 2.0 makes the miter cutoff at 60 degrees, and a miter limit of 1.414 makes the cutoff at 90 degrees. + + A miter limit for a desired angle can be computed as: miter limit = 1/sin(angle/2). + + See: https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-miter-limit" + + self miterLimit: aNumber +] + { #category : #'API - painting' } AeCairoContext >> strokePreserve [ "Stroke the current path according to the current line width, line join, line cap, and dash settings. @@ -1891,16 +1946,38 @@ AeCairoContext >> strokePreserve [ self ffiCall: #( void cairo_stroke_preserve ( self )) ] +{ #category : #'API - painting' } +AeCairoContext >> strokePreservingPath [ + "Stroke the current path according to the current line width, line join, line cap, and dash settings. + Preserve the path within the cairo context." + + self strokePreserve +] + { #category : #'API - painting' } AeCairoContext >> strokePreservingPath: aBoolean [ "Stroke the current path according to the current line width, line join, line cap, and dash settings. The argument indicates whether the path must be preserved within the cairo context." aBoolean - ifTrue: [ self strokePreserve ] + ifTrue: [ self strokePreservingPath ] ifFalse: [ self stroke ] ] +{ #category : #'API - painting settings' } +AeCairoContext >> strokeSize [ + "Gets the current stroke size (line width) to be used in next stroke operations." + + ^ self lineWidth +] + +{ #category : #'API - painting settings' } +AeCairoContext >> strokeSize: aNumber [ + "Sets the current stroke size (line width) to be used in next stoke operations." + + self lineWidth: aNumber +] + { #category : #accessing } AeCairoContext >> surface [ @@ -2029,35 +2106,107 @@ AeCairoContext >> useFillRuleWinding [ { #category : #'API - painting settings' } AeCairoContext >> useLineCapButt [ - self lineCap: AeCairoCapStyle butt + self + deprecated: 'send stroke* version instead' + transformWith: + '`@receiver useLineCapButt' -> + '`@receiver useStrokeCapButt'. + + self useStrokeCapButt ] { #category : #'API - painting settings' } AeCairoContext >> useLineCapRound [ - self lineCap: AeCairoCapStyle round + self + deprecated: 'send stroke* version instead' + transformWith: + '`@receiver useLineCapRound' -> + '`@receiver useStrokeCapRound'. + + self useStrokeCapRound ] { #category : #'API - painting settings' } AeCairoContext >> useLineCapSquare [ - self lineCap: AeCairoCapStyle square + self + deprecated: 'send stroke* version instead' + transformWith: + '`@receiver useLineCapSquare' -> + '`@receiver useStrokeCapSquare'. + + self useStrokeCapSquare ] { #category : #'API - painting settings' } AeCairoContext >> useLineJoinBevel [ - self lineJoin: AeCairoJoinStyle bevel + self + deprecated: 'send stroke* version instead' + transformWith: + '`@receiver useLineJoinBevel' -> + '`@receiver useStrokeJoinBevel'. + + self useStrokeJoinBevel ] { #category : #'API - painting settings' } AeCairoContext >> useLineJoinMiter [ - self lineJoin: AeCairoJoinStyle miter + self + deprecated: 'send stroke* version instead' + transformWith: + '`@receiver useLineJoinMiter' -> + '`@receiver useStrokeJoinMiter'. + + self useStrokeJoinMiter ] { #category : #'API - painting settings' } AeCairoContext >> useLineJoinRound [ - self lineJoin: AeCairoJoinStyle round + self + deprecated: 'send stroke* version instead' + transformWith: + '`@receiver useLineJoinRound' -> + '`@receiver useStrokeJoinRound'. + + self useStrokeJoinRound +] + +{ #category : #'API - painting settings' } +AeCairoContext >> useStrokeCapButt [ + + self strokeCap: AeCairoCapStyle butt +] + +{ #category : #'API - painting settings' } +AeCairoContext >> useStrokeCapRound [ + + self strokeCap: AeCairoCapStyle round +] + +{ #category : #'API - painting settings' } +AeCairoContext >> useStrokeCapSquare [ + + self strokeCap: AeCairoCapStyle square +] + +{ #category : #'API - painting settings' } +AeCairoContext >> useStrokeJoinBevel [ + + self strokeJoin: AeCairoJoinStyle bevel +] + +{ #category : #'API - painting settings' } +AeCairoContext >> useStrokeJoinMiter [ + + self strokeJoin: AeCairoJoinStyle miter +] + +{ #category : #'API - painting settings' } +AeCairoContext >> useStrokeJoinRound [ + + self strokeJoin: AeCairoJoinStyle round ] diff --git a/src/Alexandrie-Canvas/AeCanvas.class.st b/src/Alexandrie-Canvas/AeCanvas.class.st index 0374b09..27a4463 100644 --- a/src/Alexandrie-Canvas/AeCanvas.class.st +++ b/src/Alexandrie-Canvas/AeCanvas.class.st @@ -162,12 +162,12 @@ AeCanvas >> drawFigureWithBorderCenterAndBackground [ "Need to restore clipping area to draw the border afterwards" self restoreContextAfter: [ self fillViaClipPreservingPath ] ] - ifFalse: [ cairoContext fillPreserve ]. + ifFalse: [ cairoContext fillPreservingPath ]. "Draw border, and clip afterwards if required" self prepareStrokeWithSingleWidth. clipAfterwards - ifTrue: [ cairoContext strokePreserve; clip ] + ifTrue: [ cairoContext strokePreservingPath; clip ] ifFalse: [ cairoContext stroke ] ] @@ -179,7 +179,7 @@ AeCanvas >> drawFigureWithBorderCenterAndNoBackground [ "Draw border and clip (if required)" self prepareStrokeWithSingleWidth. clipAfterwards - ifTrue: [ cairoContext strokePreserve; clip ] + ifTrue: [ cairoContext strokePreservingPath; clip ] ifFalse: [ cairoContext stroke ] ] @@ -206,7 +206,7 @@ AeCanvas >> drawFigureWithBorderInsideAndNoBackground [ self restoreContextOnlyIfClipAfterwards: [ "Draw border" self prepareStrokeWithDoubledWidth. - cairoContext clipPreserve; stroke ] + cairoContext clipPreservingPath; stroke ] ] { #category : #private } @@ -222,7 +222,7 @@ AeCanvas >> drawFigureWithBorderOutsideAndNoBackground [ cairoContext paintGroupWith: [ self prepareStrokeWithDoubledWidth. cairoContext - strokePreserve; + strokePreservingPath; setOperatorClear; fillPreservingPath: clipAfterwards ]. @@ -238,7 +238,7 @@ AeCanvas >> drawFigureWithBorderOutsideAndOpaqueBackground [ "Draw border" self prepareStrokeWithDoubledWidth. - cairoContext strokePreserve. + cairoContext strokePreservingPath. "Draw background. As it is opaque, it overlaps the inner half of the double-sized border, emulating @@ -263,9 +263,9 @@ AeCanvas >> drawFigureWithBorderOutsideAndTranslucentBackground [ cairoContext paintGroupWith: [ self prepareStrokeWithDoubledWidth. cairoContext - strokePreserve; + strokePreservingPath; setOperatorClear; - fillPreserve ]. + fillPreservingPath ]. "Draw background" backgroundSourceBlock value. @@ -384,7 +384,7 @@ AeCanvas >> fillViaClip [ { #category : #private } AeCanvas >> fillViaClipPreservingPath [ - cairoContext clipPreserve. + cairoContext clipPreservingPath. backgroundAlpha < 1.0 ifTrue: [ cairoContext paintWithAlpha: backgroundAlpha ] ifFalse: [ cairoContext paint ] @@ -612,7 +612,7 @@ AeCanvas >> prepareStrokeWithDoubledWidth [ borderBlock value. - cairoContext lineWidth: borderWidth * 2.0. + cairoContext strokeSize: borderWidth * 2.0. ] @@ -621,7 +621,7 @@ AeCanvas >> prepareStrokeWithSingleWidth [ borderBlock value. - cairoContext lineWidth: borderWidth. + cairoContext strokeSize: borderWidth. ] @@ -767,19 +767,19 @@ AeCanvas >> setCapButt [ See: https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-line-cap" - cairoContext lineCap: AeCairoCapStyle butt + cairoContext strokeCap: AeCairoCapStyle butt ] { #category : #'API - border' } AeCanvas >> setCapRound [ - cairoContext lineCap: AeCairoCapStyle round + cairoContext strokeCap: AeCairoCapStyle round ] { #category : #'API - border' } AeCanvas >> setCapSquare [ - cairoContext lineCap: AeCairoCapStyle square + cairoContext strokeCap: AeCairoCapStyle square ] { #category : #'API - border' } @@ -822,7 +822,7 @@ AeCanvas >> setIdentityMatrix [ { #category : #'API - border' } AeCanvas >> setJoinBevel [ - cairoContext lineJoin: AeCairoJoinStyle bevel + cairoContext strokeJoin: AeCairoJoinStyle bevel ] { #category : #'API - border' } @@ -830,18 +830,18 @@ AeCanvas >> setJoinMiter: limit [ "Hint: there may be NO need to set miter join as it's the default in Cairo" "https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-line-join" - cairoContext lineJoin: AeCairoJoinStyle miter. + cairoContext strokeJoin: AeCairoJoinStyle miter. "We might NOT need to set the miter limit if it's 10.0, the default" "https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-miter-limit" - cairoContext miterLimit: limit + cairoContext strokeJoinMiterLimit: limit ] { #category : #'API - border' } AeCanvas >> setJoinRound [ - cairoContext lineJoin: AeCairoJoinStyle round + cairoContext strokeJoin: AeCairoJoinStyle round ] { #category : #initialization }