CairoGraphicsContext.st
changeset 40 28dfc583beb5
parent 39 8af34937e1ec
child 41 17bc740cbc2a
--- a/CairoGraphicsContext.st	Tue Feb 16 07:46:52 2016 +0000
+++ b/CairoGraphicsContext.st	Wed Feb 17 06:43:31 2016 +0000
@@ -3,9 +3,9 @@
 "{ NameSpace: Smalltalk }"
 
 DeviceGraphicsContext subclass:#CairoGraphicsContext
-	instanceVariableNames:'cr crId'
-	classVariableNames:'Lobby'
-	poolDictionaries:'Cairo::FontSlant Cairo::FontWeight Cairo::Format'
+	instanceVariableNames:'cr'
+	classVariableNames:'Lobby SkippedSlotIndexes'
+	poolDictionaries:'Cairo::FontSlant Cairo::FontWeight Cairo::Format Cairo::Status'
 	category:'Cairo-Compatibility'
 !
 
@@ -19,7 +19,13 @@
 
     Lobby := Registry new.
 
-    "Modified: / 09-01-2015 / 15:08:47 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    SkippedSlotIndexes := {
+        DeviceGraphicsContext instVarIndexFor: #font.
+        DeviceGraphicsContext instVarIndexFor: #deviceFont.
+        DeviceGraphicsContext instVarIndexFor: #lineWidth.
+    }
+
+    "Modified: / 17-02-2016 / 21:43:54 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
 !CairoGraphicsContext class methodsFor:'instance creation'!
@@ -27,19 +33,21 @@
 onDeviceGraphicsContext: dGC
     | cGC |
 
-    cGC := self onDevice: dGC device.  
-    1 to: DeviceGraphicsContext instSize do:[:i | 
-        cGC instVarAt: i put: (dGC instVarAt: i).
+    cGC := self basicNew.
+    1 to: DeviceGraphicsContext instSize do:[:i |
+        (SkippedSlotIndexes includes:i) ifFalse:[ 
+            cGC instVarAt: i put: (dGC instVarAt: i).
+        ].
     ].
     dGC gcId notNil ifTrue:[ 
         cGC createCR.
     ].
     cGC lineWidth: dGC lineWidth.
-    cGC font: dGC font.
+    cGC basicFont: dGC basicFont.
     ^ cGC
 
     "Created: / 15-02-2016 / 21:20:00 / Jan Vrany <jan.vrany@fit.cvut.cz>"
-    "Modified: / 16-02-2016 / 10:44:24 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 17-02-2016 / 22:46:02 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
 !CairoGraphicsContext class methodsFor:'accessing'!
@@ -122,6 +130,22 @@
 
 !CairoGraphicsContext methodsFor:'accessing'!
 
+basicFont:aFont
+    (aFont ~~ font) ifTrue:[     
+        super basicFont: aFont.
+        font notNil ifTrue:[ 
+            font := CairoScaledFont fromFontDescription: font onDevice: device.
+            cr notNil ifTrue:[ 
+                cr font: font scaledFont.
+                cr fontSize: (Screen current verticalPixelPerInch / 72) * font size.
+            ].
+        ].
+    ].
+
+    "Created: / 16-02-2016 / 15:37:44 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 18-02-2016 / 00:38:31 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
 clippingBounds:aRectangleOrNil
     "set the clipping rectangle for drawing (in logical coordinates);
      a nil argument turn off clipping (i.e. whole view is drawable)"    
@@ -131,25 +155,6 @@
     "Created: / 15-02-2016 / 21:38:05 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
-font:aFont
-    | family slant psize weight |
-
-    super font: aFont.
-
-    family := font family.
-    slant := SymbolicFontSlantToCairoFontSlantMap at: (font style ? 'roman'). 
-    weight := SymbolicFontFaceToCairoFontWeightMap at: (font face ? 'regular').
-    psize := font pixelSize.
-    psize isNil ifTrue:[ 
-        psize := (self device verticalPixelPerInch / 72) * font size.
-    ].
-
-    cr font: family slant: slant weight: weight.
-    cr fontSize: psize .
-
-    "Modified: / 16-02-2016 / 10:55:51 / Jan Vrany <jan.vrany@fit.cvut.cz>"
-!
-
 function:aFunctionSymbol
     "set the drawing function"
 
@@ -256,11 +261,12 @@
 displayRectangleX:x y:y width:w height:h
     "draw a rectangle
      - this could be recoded to draw using displayLine"
+    (w > 0 and:[h > 0]) ifTrue:[
+        cr rectangleX: x y: y width: w height: h.
+        cr stroke.
+    ]
 
-    cr rectangleX: x y: y width: w height: h.
-    cr stroke.
-
-    "Modified: / 13-02-2016 / 20:04:31 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 18-02-2016 / 22:21:56 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
 displayString:aStringArg from:index1Arg to:index2Arg x:x y:y opaque:opaqueArg maxWidth:maxWidth
@@ -271,19 +277,63 @@
      the case where paint and/or bgPaint are dithered colors.
      maxWidth is the maximum width of the string in pixels or nil if unknown."    
 
+    | opaque string |
+    "
+     if backgroundPaint color is nil, we assume
+     this is a non-opaque draw
+    "
+    opaque := opaqueArg ? false.
+    bgPaint isNil ifTrue:[
+        opaque := false.
+    ].
+
+    gcId isNil ifTrue:[
+        self initGC
+    ]. 
+
+    (aStringArg isString not or:[aStringArg isText]) ifTrue:[
+        "
+         hook for non-strings (i.e. attributed text)
+         that 'thing' should know how to display itself ...
+        "
+        aStringArg displayOn:self x:x y:y from:index1Arg to:index2Arg opaque:opaque.
+        ^ self
+    ].   
+
+    (index1Arg == 1 and:[ index2Arg == aStringArg size ]) ifTrue:[ 
+        string := aStringArg
+    ] ifFalse:[ 
+        string := aStringArg copyFrom: index1Arg to: index2Arg
+    ].
+
     cr save.
     [ 
+        | extents |
+
+        extents := cr textExtents: string.
+        cr rectangleX: x + extents xBearing ceiling y: y - font ascent ceiling  width: extents xAdvance ceiling height: font height ceiling.
+        cr lineWidth: 0.
+
+        opaque ifTrue:[ 
+            cr source: bgPaint.
+            cr fill.
+        ] ifFalse:[
+            "/ I'm not sure why this is required but if not done,
+            "/ selected text is not rendered correcrtly...
+            cr stroke.
+        ].
+
+        self assert: cr status == CAIRO_STATUS_SUCCESS.
+        cr source: paint.
         cr moveToX: x y: y.
-        (index1Arg == 1 and:[ index2Arg == aStringArg size ]) ifTrue:[ 
-            cr showText: aStringArg
-        ] ifFalse:[ 
-            cr showText: (aStringArg copyFrom: index1Arg to: index2Arg).
-        ].
+        cr showText: string.
+        self assert: cr status == CAIRO_STATUS_SUCCESS.
     ] ensure:[ 
         cr restore.
     ]
 
     "Created: / 16-02-2016 / 10:51:24 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 18-02-2016 / 21:12:00 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
 !CairoGraphicsContext methodsFor:'basic filling'!
@@ -341,18 +391,70 @@
 fillRectangleX:x y:y width:w height:h
     "fill a rectangle with current paint color"
 
-    cr rectangleX: x y: y width: w height: h. 
-    cr strokeAndPreserve.
-    cr fill.
+    (w > 0 and:[h > 0]) ifTrue:[
+        cr lineWidth: 0.      
+        cr rectangleX: x y: y width: w height: h. 
+        cr fill.
+        cr lineWidth: (lineWidth == 0 ifTrue:[1] ifFalse:[lineWidth])  
+    ].
 
 "/    cr save.
 "/    cr rectangleX: x y: y width: w height: h. 
 "/    cr sourceR: 1 G: 0 B: 0.
-"/    cr lineWidth: 3.  
+"/    cr lineWidth: 1.  
 "/    cr stroke.
 "/    cr restore.
 
-    "Modified: / 16-02-2016 / 07:58:46 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 18-02-2016 / 22:20:48 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+! !
+
+!CairoGraphicsContext methodsFor:'bit blitting'!
+
+copyBitsFrom:aByteArray bitsPerPixel:bpp depth:depth padding:pad width:srcW height:srcH x:srcX y:srcY toX:dstX y:dstY
+    "copy bits from a smalltalk byteArray.
+     The bits found there are supposed to be in the devices native format (i.e.
+     translated to allocated color indices on pseudoColor devices and padded as required.
+     The byteOrder is MSB and will be converted as appropriate by the underlying devices
+     method to whatever the device needs."
+
+    cr notNil ifTrue:[ 
+        cr surface flush
+    ].
+    ^ super copyBitsFrom:aByteArray bitsPerPixel:bpp depth:depth padding:pad width:srcW height:srcH x:srcX y:srcY toX:dstX y:dstY
+
+    "Created: / 18-02-2016 / 20:16:24 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+copyFrom:aDrawable x:srcX y:srcY toX:dstX y:dstY width:w height:h async:async
+    "copy from aDrawable into the receiver;
+     the source may be the receiver as well - in this case its a scroll.
+     All coordinates are in device coordinates.
+     If the receiver is a view AND async is true, the call returns immediately
+     - otherwise, it returns when the scroll operation is finished.
+     (not all devices care for this).
+     If the receiver is a pixmap, the call always returns immediately."
+
+    cr notNil ifTrue:[ 
+        cr surface flush
+    ].
+    ^ super copyFrom:aDrawable x:srcX y:srcY toX:dstX y:dstY width:w height:h async:async
+
+    "Created: / 18-02-2016 / 20:17:26 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+copyPlaneFrom:aDrawable x:srcX y:srcY toX:dstX y:dstY width:w height:h
+    "copy one plane from aDrawable into the receiver. 0's are drawn in
+     background, while 1's are drawn with foreground color.
+     The depth of aDrawable must (should) be 1.
+     The drawable must have been allocated on the same device.
+     All coordinates are in device coordinates."
+
+     cr notNil ifTrue:[ 
+        cr surface flush
+    ].
+    ^ super copyPlaneFrom:aDrawable x:srcX y:srcY toX:dstX y:dstY width:w height:h
+
+    "Created: / 18-02-2016 / 20:17:58 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
 !CairoGraphicsContext methodsFor:'drawing'!
@@ -375,15 +477,36 @@
     [
         image bitsARGB32Into: data stride: stride fg: self paint bg:  self backgroundPaint. 
         image_surface := Cairo::CPrimitives cairo_image_surface_create_for_data: data _: CAIRO_FORMAT_ARGB32 _: width _: height _: stride.
-        Cairo::CPrimitives cairo_set_source_surface: crId _: image_surface _: x asFloat _: y asFloat.
-        Cairo::CPrimitives cairo_paint: crId.
+        Cairo::CPrimitives cairo_set_source_surface: cr _: image_surface _: x asFloat _: y asFloat.
+        Cairo::CPrimitives cairo_paint: cr.
     ] ensure:[ 
         data finalize.
         image_surface release.
     ].
 
     "Created: / 31-12-2014 / 12:08:08 / Jan Vrany <jan.vrany@fit.cvut.cz>"
-    "Modified: / 15-02-2016 / 21:32:16 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 17-02-2016 / 20:58:00 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+displayLineFrom:p0 to:p1
+    "draw a line (with current paint-color); apply transformation if nonNil"
+
+    ^ self displayLineFromX: p0 x y: p0 y toX: p1 x y: p1 y
+
+    "Created: / 18-02-2016 / 20:27:56 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+displayLineFromX:xStart y:yStart toX:xEnd y:yEnd brush:aForm
+    "draw a line using a brush.
+     Here, a slow fallback is used, drawing into a 
+     temporary bitmap first, which is then displayed"
+
+    cr notNil ifTrue:[ 
+        cr surface flush
+    ].
+    ^ super displayLineFromX:xStart y:yStart toX:xEnd y:yEnd brush:aForm
+
+    "Created: / 18-02-2016 / 20:28:58 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
 displayRoundRectangleX:x y:y width:w height:h wCorner:wCorn hCorner:hCorn
@@ -485,9 +608,12 @@
     "Physically create a Cairo graphics context"
 
     cr := self cairo.
+    font notNil ifTrue:[ 
+        cr font: font scaledFont.
+    ].
 
     "Created: / 12-02-2016 / 16:59:03 / Jan Vrany <jan.vrany@fit.cvut.cz>"
-    "Modified: / 13-02-2016 / 19:56:36 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 17-02-2016 / 22:42:47 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
 createGC