Hi all. A small holiday present for anyone interested: https://github.com/ErikOnBike/CodeParadise now runs on P10 (and P11 with some care)! If you are using it on P8, please unload the CP-ClientEnvironment and reload it using the “pharo8” branch:
Metacello new
repository: 'github://ErikOnBike/CP-ClientEnvironment:pharo8';
baseline: 'CodeParadise';
load.
P11 seems to have some issues sometimes with restarting the image. Have not found the exact cause. Will look into this.
The recent Pharo VM Release v9.0.21 for Windows, Mac and Linux was great for our Bloc project since we had some troubles with the graphic libraries dependencies. We take advantage in two aspects:
Simpler install instructions. Getting the project installed is now a simple script, as any other. Before, we had additional steps according to the OS platform (on Mac, via brew or macport; on Windows, by downloading dlls in a zip).
CI running again. The GitHub workflows have been broken some time. The reason was the difficulty to get the VM and update it with updated graphic libraries in the script. Happily, with the new VM release we have restored jobs in all three platforms. After fixing CI, some hidden bugs did pop up, such as an error when scanning fonts on Windows (fixed). Still not everything green, but it’s already good that CI is back!
Outskirts inside and outside
Some geometries now provide an explicit Cairo path to draw the border inside or outside, for two main reasons:
Rendering speed. A recent Bloc Update showed that rendering elements with border inside or outside was significantly slower than centered borders. Now, they are almost as fast as centered outskirts.
Dashed borders. There was a visual issue on elements with dashed border when the outskirts is not centered, i.e. inside or outside. Now it works:
For the moment, this improvement applies only for circle, rectangle and rounded rectangle, but will be extended to more geometries.
Gaussian shadow effect
In a nutshell, a general approach to create the shadow of a visual element is: – take the alpha-channel of the element (an 8-bit image) – apply an approximation of Gaussian blur on it – paint the shadow color (with an offset), using as a mask the intermediate 8-bit image – finally, paint the element on top of the shadow. For example, SVG specifies the drop shadow filter like that. The big question was how to do Gaussian blur step, as Cairo doesn’t provide it out-of-the-box. The current solution is the adaptation of a linear algorithm (on the number of pixels) that looks good:
This element was created with:
BlElement new
geometry: (BlRoundedRectangleGeometry cornerRadius: 50);
size: 500@100;
border: (BlBorder builder
paint: Color blue;
width: 15;
dashArray: #(20);
build);
effect: (BlGaussianShadowEffect
color: Color orange
width: 20
offset: 20 asPoint);
yourself
Warning
This implementation iterates over the pixels, all in CPU, which has its limitations. For example, on large and animated elements. If we implement some hardware accelerated alternative, this could be a solid fallback. However, in an element animation where only offset or color change, then caching the 8-bit image with the shadow mask should work very well.
Rendering regression tests
The following image gallery illustrates examples added to test regressions in rendering:
Binding for SVG and PDF Cairo surfaces
Our Cairo bindings in Alexandrie now support drawing to SVG and PDF files. Part of this code and tests are based on Athens implementation pushed to Pharo a couple of years ago.
A text with shadows exported to PDF using our Cairo bindings. Text is exported in vector-style, but shadow is a bitmap-style generated by our implementation previously mentioned.
Note: I exported this example using the Cairo bindings directly, without integration with Bloc. But it will be straightforward to integrate it.
I have released a new version of the Pharo VM for Pharo 9, Pharo 10 and Pharo 11. This VM is accessible right now from Zero-Conf, updating it in the Pharo Launcher or using the usual downloads (as described in pharo.org/download).
This version includes a series of bug fixes and upgrades on the third-party libraries.
Changelog:
– Implementing High resolution clock for ARM64 (Used during profiling) – Updating third party libraries for all the graphic layer.
– Fixing a performance regression on the allocation of opcodes and fix-ups. Cleaning only the ones that are going to be used. Like this, this version has the same speed than before when allocating in the stack. – Correctly handling the encoding of the command line arguments of the VM (Windows) – Allocating the opcodes and fixup structs only once and reusing them (Reducing risk of C Stack Overflow)
Blocks ====== Status: All tests green when compiling with ConstantBlockClosure (but the option is not yet enabled by default) Work continues to reducing the use of #home, this week by using #homeMethod instead of “home method”
This is a small experiment about syntax errors when evaluating code. With #evaluate:, we can compile and run a DoIt, for example:
Smalltalk compiler evaluate: ‘1+2’
Imagine you evaluate a String, but it is actually not syntactically correct:
Smalltalk compiler evaluate: ‘1+’
We get an exception (while parsing), a SyntaxErrorDebugger is opened:
This tool is not used often and is fairly odd, in practice we use it for two things: Look at the error message and use the (not easy to find) menu “debug calling process” to see where the eval was called to understand what went wrong (why we consructed a string of invalid code). The problem is that the tool is not easy to understand, and it is not easy to maintain as part of the debugger framework (it is a bit broken even now, especially wrt to editing and continue, which does not matter for DoIts, though).
Could we no do better?
Evaluating code means:
– We parse the string to an AST – we translate the AST to a DoIt Method – we execute that method
The error happens inside the parse phase: no method gets contructed, intead an exception is raised. There is quite some machinary to allow re-starting parsing after the error is fixed (see the exception ReparseAfterSourceEditing and how it is used, quite hard to understand).
The idea that syntax errors are hard errors is quite natural. With batch compilers, there is some effort done to do some auto-repair. This was very useful in old times (punch cards!) to avoid seeing an aborted job the next morning just due to a missing comma. These days it is used to allow better error messages. But it is common to treat syntax errors as hard errors. This is seen as good as they are detected very early and the fact that compiling is not possible is seen as the goal for any kind of error. In the end, type systems try to extend this to non-syntactic problems: If a program is wrong, it should not run. If we call a method that does not exist, the program should not compile.
What if we not follow this standard advice and go the exact opposite direction?
Let’s try:
Our tools parse code after *every* keystroke. The resulting AST is used for syntax highlighting. Which means, we can parse the expression
‘1+’
without any problem, if we use the same mode for the Parser:
We see that the parser created an AST, with a ParseErrorNode as the argument of the message send +.
This AST would used by the Syntax HighLighter, and for code completion. But it is in the end just the normal AST that the Compiler uses to compile source text to CompiledMethods.
So can we not just implement the code in the compiler to compile this AST?
The node implements the visitor pattern #acceptVisitor:, which calls visitParseErrorNode:. So we can add support to the compiler by just implementing OCASTTranslator>>#visitParseErrorNode:
This just reads the global Variable RuntimeSyntaxError (which is subclass of Error), pushes the error node itself as an argument and sends #signalSyntaxError:
signalSyntaxError: aNode “we use signalSyntaxError: instead of signal: so we can quickly check compiledMethods for syntax errors by checking the literals” ^(self new errorNode: aNode) signal
To see what the compiler compiled for that expression, lets compile a DoIt method (this is what #evaluate: does to get a method to execute):
– The error message is not shown other than in the debugger window title. (but all the infos to print a nice error in place is contained in the AST) – Maybe allowing to edit and proceed could be interesting (not that much for evaluate)
Hello, this is another report on my activity on the Bloc project.
Shadow Effect
Circle sectors with green shadows
This visual effect is quite common in graphical user interfaces, often to highlight certain elements or as a aesthetic decoration. When an element drops a shadow, it usually presents a darken and blurred-edges copy underneath. More in general, a shadow can have a x,y offset, an arbitrary color, and a Gaussian “radius” which can be zero meaning sharp edges.
We received requests to support shadows on the Alexandrie canvas and we have now a basic implementation, for the moment without blur. The first clear challenge was being fast on drawing the effect, however there were other challenges that I will comment briefly.
Open geometries and text
Straight lines, arcs and beziers can have a shadow, that on our first approach was working like on the right. This was following the Sparta-Cairo canvas implementation. But it didn’t look well so we changed it to be more like the SVG’s drop shadow effect (on the left side of the picture).
On text elements, we implemented it like on the picture. This also differs from how the other canvas behaves, casting a rectangular shadow.
Bordered elements with translucent or transparent background
The shadow of a completely opaque element, as on the left of this figure, looks fine as a uniform copy of it. When it is transparent as on the right, it’s clear that the shadow should take into account a border. However, the translucent background may be subject to discussion. We currently draw it as in the SVG specification, which is taking the alpha channel of the element that drops the shadow and uses it as a mask to paint the shadow color.
Border outskirts
Our Alexandrie canvas, on the left.
The border of a Bloc element may be drawn inside, center or outside of the geometry bounds. This parameter, named outskirt, required our consideration as the shadow should match the geometry extent with the appropriate border width and outskirt type.
The figure has a vertical line that separates two ways of drawing the shadow of elements. All hexagons have the same extent, only outskirts differ by column: The left column has outskirt inside, the center one has center outskirt, and the right column has outside outskirt.
Tests and benchmarks, and future work
All screen captures of this report are the output of new rendering tests. They cover a good number of combinations. Pixelated pictures ease comparation, and that’s why you may notice this characteristic on them.
There is a new benchmark for shadows, which shows performance issues that we are working on. Blurred shadow is work in progress.