Nicolas Cellier uploaded a new version of VMMaker to project VM Maker: http://source.squeak.org/VMMaker/VMMaker.oscog-nice.3170.mcz
==================== Summary ====================
Name: VMMaker.oscog-nice.3170 Author: nice Time: 24 February 2022, 2:48:39.990237 pm UUID: 6b851cd8-b485-1047-acfd-8adee37e3660 Ancestors: VMMaker.oscog-nice.3169
Revise the 3 new BitBlt rules again (see https://github.com/OpenSmalltalk/opensmalltalk-vm/issues/505)
- use a proper division by 255 (factor alpha/255) in alphaScale:with: instead of approximate factor alpha/256 previously - use a proper rounding (by adding 16r80 before the division by 255) in alphaScale:with: - split the unscale ops in 3 separate r/g/b channels, otherwise, when grouping rb, the division by alpha will leak red bits not only on green components but also on blue - fixup the carry used for saturating the rbg components in case of 16rFF overflow in alphaUnscale:with:
Note: division by 255 can be checked thru this snippet (should answer 0):
(0 to: 16rFF squared) inject: 0 into: [:max :num | | rat | rat := num + 16r80. "+ 16r80 for rounding" rat := rat + (rat - 1 >> 8 bitAnd: 16rFF) >> 8 bitAnd: 16rFF. "divide by 255" ((num/255) rounded - rat) abs max: max].
=============== Diff against VMMaker.oscog-nice.3169 ===============
Item was changed: ----- Method: BitBltSimulation>>alphaBlendUnscaled:with: (in category 'combination rules') ----- alphaBlendUnscaled: sourceWord with: destinationWord "Blend sourceWord with destinationWord using the alpha value from both sourceWord and destinationWord. Alpha is encoded as 0 meaning 0.0, and 255 meaning 1.0. The alpha channel and color produced are
srcAlpha + (destAlpha*(1-srcAlpha)) (srcAlpha*srcColor + (destAlpha*(1-srcAlpha)*dstColor)) / (srcAlpha + (destAlpha*(1-srcAlpha)))
In contrast to alphaBlend:with: the method does not assume that destination form is opaque. In contrast to alphaBlendScaled:with: the method does not assume that colors have been pre-scaled (muliplied) by alpha channel." + | alpha blendA result blendR blendB blendG | - | alpha blendA result blendRB blendG | <inline: false> <returnTypeC: 'unsigned int'> <var: 'sourceWord' type: #'unsigned int'> <var: 'destinationWord' type: #'unsigned int'> + <var: 'blendA' type: #'unsigned int'> + <var: 'blendR' type: #'unsigned int'> - <var: 'blendRB' type: #'unsigned int'> <var: 'blendG' type: #'unsigned int'> + <var: 'blendB' type: #'unsigned int'> <var: 'result' type: #'unsigned int'> <var: 'alpha' type: #'unsigned int'> - <var: 'blendA' type: #'unsigned int'> alpha := sourceWord >> 24. "High 8 bits of source pixel, assuming ARGB encoding" alpha = 0 ifTrue: [ ^ destinationWord ]. alpha = 255 ifTrue: [ ^ sourceWord ]. + blendA := 16rFF * alpha + (16rFF - alpha * (destinationWord >> 24)) + 16r80. "blend alpha channels" - blendA := 16rFF * alpha + (16rFF - alpha * (destinationWord >> 24)) + 16rFF. "blend alpha channels" blendA := blendA + (blendA - 1 >> 8 bitAnd: 16rFF) >> 8 bitAnd: 16rFF. "divide by 255"
+ blendR := ((sourceWord bitAnd: 16rFF0000) * alpha) + + ((destinationWord bitAnd: 16rFF0000) * (blendA-alpha)) + +(blendA<<15) + // blendA bitAnd: 16rFF0000. "blend red" + - blendRB := ((sourceWord bitAnd: 16rFF00FF) * alpha) + - ((destinationWord bitAnd: 16rFF00FF) * (blendA-alpha)) - / blendA bitAnd: 16rFF00FF. "blend red and blue" - blendG := ((sourceWord bitAnd: 16r00FF00) * alpha) + ((destinationWord bitAnd: 16r00FF00) * (blendA-alpha)) + +(blendA<<7) + // blendA bitAnd: 16r00FF00. "blend green" + + blendB := ((sourceWord bitAnd: 16r0000FF) * alpha) + + ((destinationWord bitAnd: 16r0000FF) * (blendA-alpha)) + +(blendA>>1) + // blendA bitAnd: 16r0000FF. "blend blue" + + result := ((blendR bitOr: blendB) bitOr: blendG) bitOr: blendA << 24. - / blendA bitAnd: 16r00FF00. "blend green" - result := (blendRB bitOr: blendG) bitOr: blendA << 24. ^ result !
Item was changed: ----- Method: BitBltSimulation>>alphaScale:with: (in category 'combination rules') ----- alphaScale: sourceWord with: destinationWord "Scale (premultiply) the destination with its alpha channel. Note that sourceWord is ignored." | alpha rb g | <inline: false> "Do NOT inline this into optimized loops" <returnTypeC: 'unsigned int'> <var: 'sourceWord' type: #'unsigned int'> <var: 'destinationWord' type: #'unsigned int'> <var: 'rb' type: #'unsigned int'> <var: 'g' type: #'unsigned int'> <var: 'alpha' type: #'unsigned int'> alpha := destinationWord >> 24. "High 8 bits is opacity (ARGB format)" + rb := (destinationWord bitAnd: 16rFF00FF) * alpha + 16r800080. "scale red and blue components" + rb := rb + (rb - 16r010001 >> 8 bitAnd: 16rFF00FF) >> 8 bitAnd: 16rFF00FF. "divide by 255" + g := (destinationWord bitAnd: 16r00FF00) * alpha + 16r008000. "scale green component" + g := g + (g - 16r000100 >> 8 bitAnd: 16r00FF00) >> 8 bitAnd: 16r00FF00. "divide by 255" - rb := ((destinationWord bitAnd: 16rFF00FF) * alpha >> 8 bitAnd: 16rFF00FF). "scale red and blue components" - g := ((destinationWord bitAnd: 16r00FF00) * alpha >> 8 bitAnd: 16r00FF00). "scale green component" ^(g bitOr: rb) bitOr: (destinationWord bitAnd: 16rFF000000) "recompose"!
Item was changed: ----- Method: BitBltSimulation>>alphaUnscale:with: (in category 'combination rules') ----- alphaUnscale: sourceWord with: destinationWord "Unscale (divide) the destination with its alpha channel. Note that sourceWord is ignored." + | alpha r b g rgb carry | - | alpha rb g rgb carry | <inline: false> "Do NOT inline this into optimized loops" <returnTypeC: 'unsigned int'> <var: 'sourceWord' type: #'unsigned int'> <var: 'destinationWord' type: #'unsigned int'> + <var: 'r' type: #'unsigned int'> - <var: 'rb' type: #'unsigned int'> <var: 'g' type: #'unsigned int'> + <var: 'b' type: #'unsigned int'> <var: 'rgb' type: #'unsigned int'> <var: 'alpha' type: #'unsigned int'> <var: 'carry' type: #'unsigned int'> alpha := destinationWord >> 24. "High 8 bits is opacity (ARGB format)" alpha = 0 ifTrue: [^0]. + r := (destinationWord bitAnd: 16rFF0000) * 255 + (alpha+1<<15) // alpha. "unscale red components" + g := (destinationWord bitAnd: 16r00FF00) * 255 + (alpha+1<<7) // alpha. "unscale green component" + b := (destinationWord bitAnd: 16r0000FF) * 255 + (alpha+1>>1) // alpha. "unscale blue components" + carry := (((r bitAnd: 16rFF000000) bitOr: (g bitAnd: 16rFF0000)) bitOr: (b bitAnd: 16rFF00)) >> 8. + carry := carry bitOr: ((carry bitAnd: 16rAAAAAA) >> 1 bitOr: (carry bitAnd: 16r555555) << 1). + carry := carry bitOr: ((carry bitAnd: 16rCCCCCC) >> 2 bitOr: (carry bitAnd: 16r333333) << 2). + carry := carry bitOr: ((carry bitAnd: 16rF0F0F0) >> 4 bitOr: (carry bitAnd: 16r0F0F0F) << 4). + rgb := ((r bitAnd: 16rFF0000) bitOr: (g bitAnd: 16r00FF00)) bitOr: (b bitAnd: 16r0000FF). - rb := (destinationWord bitAnd: 16rFF00FF) << 8 / alpha. "unscale red and blue components" - g := (destinationWord bitAnd: 16r00FF00) / alpha. "unscale green component" - carry := ((rb >> 8 bitAnd: 16rAA00AA) >> 1 bitOr: (rb >> 8 bitAnd: 16r550055) << 1) - bitOr: ((g bitAnd: 16r00AA00) >> 1 bitOr: (g bitAnd: 16r005500) << 1). - carry := (carry bitAnd: 16rCCCCCC) >> 2 bitOr: (carry bitAnd: 16r333333) << 2. - carry := (carry bitAnd: 16rF0F0F0) >> 4 bitOr: (carry bitAnd: 16r0F0F0F) << 4. - rgb := (rb bitAnd: 16rFF00FF) bitOr: (g << 8 bitAnd: 16r00FF00). rgb := rgb bitOr: carry. "saturate RGB components if division overflows" ^rgb bitOr: (destinationWord bitAnd: 16rFF000000) "restore alpha"!
vm-dev@lists.squeakfoundation.org