Skip to content

Make #pharoLogo for PolymorphSystemSettings use a FormSet for the logo at scale 1 and 2#16305

Merged
jecisc merged 1 commit intopharo-project:Pharo12from
Rinzwind:pharologo-formset
Mar 25, 2024
Merged

Make #pharoLogo for PolymorphSystemSettings use a FormSet for the logo at scale 1 and 2#16305
jecisc merged 1 commit intopharo-project:Pharo12from
Rinzwind:pharologo-formset

Conversation

@Rinzwind
Copy link
Copy Markdown
Contributor

This pull request makes #pharoLogo for PolymorphSystemSettings use a FormSet for the logo at scale 1 and 2. I used the snippet given below to generate #pharoLogoContents1 and #pharoLogoContents2. The send of #fill_opacity: in the snippet is a workaround, the ‘flare’ path is drawn fully opaque otherwise.

Metacello new
	baseline: 'AthensSVG';
	repository: 'github://pharo-contributions/Athens-SVG/src';
	load.
svgRoot := (Smalltalk at: #AthensSVGConverter) fromURL: 'https://files.pharo.org/media/logo/Pharo%20Logo%20SVG/Pharo_Logo_v3.0.svg'.
flarePath := (svgRoot instVarNamed: 'ids') at: 'flare'.
flarePath fill_opacity: (flarePath fill_opacity * flarePath opacity) asString.
extent := ((svgRoot width value @ svgRoot height value) / 2) rounded.
(1 to: 2) do: [ :scale |
	| surface byteArray methodSource |
	surface := AthensCairoSurface extent: extent * scale.
	surface drawDuring: [ :canvas |
		canvas pathTransform scaleBy: surface extent / (svgRoot width value @ svgRoot height value).
		svgRoot renderOn: canvas viewportExtent: surface extent ].
	byteArray := ByteArray streamContents: [ :byteArrayStream |
		PNGReadWriter putForm: surface asForm onStream: byteArrayStream ].
	methodSource := String streamContents: [ :stream |
		stream nextPutAll: 'pharoLogoContents'; nextPutAll: scale asString; cr.
		stream tab; nextPutAll: '^ '''; cr.
		(byteArray base64Encoded groupsOf: 80)
			do: [ :group | stream tab; tab; nextPutAll: group ]
			separatedBy: [ stream cr ].
		stream nextPutAll: '''' ].
	PolymorphSystemSettings class compile: methodSource classified: #desktop ].

@Rinzwind Rinzwind marked this pull request as draft March 17, 2024 15:56
@Rinzwind
Copy link
Copy Markdown
Contributor Author

I converted this to a draft. In the new forms, the shapes of the logo have darkened edges. Snippet to zoom in on the top of the ‘h’:

((Form fromBinaryStream: PolymorphSystemSettings pharoLogoContents base64Decoded readStream)
	contentsOfArea: (75@0 extent: 30@30)) magnifyBy: 5 smoothing: 1

Old form:

New form:

I’m not sure how to fix that though.

@Rinzwind
Copy link
Copy Markdown
Contributor Author

Rinzwind commented Mar 17, 2024

Having done some reading, I understand the problem is the new forms use premultiplied alpha, while what ImageMorph expects, and what the old form uses, is straight alpha. See the section ‘Straight versus premultiplied’ in the Wikipedia article on ‘Alpha compositing’. I could convert from premultiplied to straight: commit e4f54c1. It would seem better to draw the forms directly using straight alpha, but I’m not sure that’s possible: the documentation for image surfaces in Cairo doesn’t seem to list a counterpart that uses straight alpha for ‘CAIRO_FORMAT_ARGB32’ which uses premultiplied alpha. Unless it’s ‘CAIRO_FORMAT_RGBA128F’ but the description doesn’t say.

@tinchodias: Perhaps you know more about this question of straight versus premultiplied alpha in Cairo and Athens?

@tinchodias
Copy link
Copy Markdown
Contributor

tinchodias commented Mar 17, 2024

Hello @Rinzwind I will read more in detail of this FormSet issue during the week, but for the moment I send you the imformation I have at hand.

I learnt a bit of this topic, for example in: pharo-graphics/Alexandrie#35

The following are parts of emails I sent to bloc mailing list:


The Bloc image background had a Form converted to Cairo image, and the debugging tool converts again to Form to show it in a Morph.

An instance of AeCairoImageSurface can be created from a Form, and it provides asForm to get again a Form, but there was an error in the middle that is shown in this example:

f := Form extent: 1@1 depth: 32.
f fillColor: (Color red alpha: 0.4).
f colorAt: 0@0. 
">>> (Color r: 1.0 g: 0.0 b: 0.0 alpha: 0.4) --> this is right, the color we used to fill"

(AeCairoImageSurface fromForm: f) asForm colorAt: 0@0. 
">>> (Color r: 0.3998044965786901 g: 0.0 b: 0.0 alpha: 0.4) --> wrong, the red component is not 1.0"

Curiosity: Both formats are 32 bits, and ARGB order, but there is calculation to perform on each pixel on conversion.

Cairo format is described as follows:

CAIRO_FORMAT_ARGB32: each pixel is a 32-bit quantity, with alpha in the upper 8 bits, then red, then green, then blue. The 32-bit quantities are stored native-endian. Pre-multiplied alpha is used. (That is, 50% transparent red is 0x80800000, not 0x80ff0000.)
(Source: https://www.cairographics.org/manual/cairo-Image-Surfaces.html#cairo-format-t)

Form, is "straight alpha", that can be explained as:

"In this world, RGB and alpha are independent. You can change one without affecting the other. To make an object fade out, you would gradually reduce its alpha value while leaving RGB unchanged."
(Source: https://learn.microsoft.com/en-us/windows/apps/develop/win2d/premultiplied-alpha#straight-alpha)

We already had the conversion Form -> Cairo, but not the Cairo -> Form, so that's what I pushed to close the issue. Plus added 2 tests.


and then:


I had to push a further fix.

Now the conversion from Cairo (premultiplied alpha) to Form (straight alpha), restores the brightness (or saturation?) of each translucent pixel (non opaque).

As this is proper to Cairo to Form conversion, it's not only Alexandrie, also Athens-Cairo should do it.
I ignore what happens in the SurfacePlugin that Athens-Cairo bindings use, but I guess this is not done.
I remembered that when I worked in Hiedra, there was always an annoying effect in the "antialias" pixels
when the background is white. I did a test in a Hiedra example and it seems to be true.

Left: current, Right: applying the transformation from the premultiplied format of Cairo:
image

zoom:

image


@Ducasse
Copy link
Copy Markdown
Member

Ducasse commented Mar 18, 2024

Thank you for the discussion. This is great to have you around.

@Rinzwind Rinzwind force-pushed the pharologo-formset branch from 2a6989c to d11937b Compare March 18, 2024 17:42
@Rinzwind
Copy link
Copy Markdown
Contributor Author

@tinchodias: Thanks for the info!

I have now regenerated #pharoLogoContents1 and #pharoLogoContents2 with the assignment to ‘byteArray’ in the snippet changed to the following to convert from premultiplied to straight alpha:

byteArray := ByteArray streamContents: [ :byteArrayStream |
	| surfaceForm convertedForm |
	surfaceForm := surface asForm.
	convertedForm := Form extent: surfaceForm extent depth: 32.
	0 to: convertedForm width do: [ :x |
		0 to: convertedForm height do: [ :y |
			| color |
			color := surfaceForm colorAt: x@y.
			convertedForm colorAt: x@y put:
				(color alpha > 0 ifTrue: [ color / color alpha ] ifFalse: [ color ]) ] ].
	PNGReadWriter putForm: convertedForm onStream: byteArrayStream ].

I force-pushed the branch to a new commit to avoid keeping the previously generated methods, which are a bit large, in the history.

@Rinzwind Rinzwind marked this pull request as ready for review March 18, 2024 17:43
@Rinzwind Rinzwind closed this Mar 21, 2024
@Rinzwind Rinzwind reopened this Mar 21, 2024
@Ducasse Ducasse closed this Mar 22, 2024
@Ducasse Ducasse reopened this Mar 22, 2024
@Rinzwind Rinzwind closed this Mar 24, 2024
@Rinzwind Rinzwind reopened this Mar 24, 2024
@jecisc
Copy link
Copy Markdown
Member

jecisc commented Mar 25, 2024

Is it ready for the merge now?

@Rinzwind
Copy link
Copy Markdown
Contributor Author

Yes, please do merge this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants