From ddec70d99dce23d877837a970b08d613474e38b3 Mon Sep 17 00:00:00 2001 From: Martin Dias Date: Thu, 21 Sep 2023 02:39:29 -0300 Subject: [PATCH] Fix asFormARGB32, which wasn't converting from premultiplied alpha, as Form requires. Fixes #35. Includes 2 new tests --- .../AeCairoImageSurfaceTest.class.st | 58 +++++++++++++++++++ .../AeCairoImageSurface.class.st | 28 +++++---- 2 files changed, 76 insertions(+), 10 deletions(-) diff --git a/src/Alexandrie-Cairo-Tests/AeCairoImageSurfaceTest.class.st b/src/Alexandrie-Cairo-Tests/AeCairoImageSurfaceTest.class.st index 4135f7a..75fff07 100644 --- a/src/Alexandrie-Cairo-Tests/AeCairoImageSurfaceTest.class.st +++ b/src/Alexandrie-Cairo-Tests/AeCairoImageSurfaceTest.class.st @@ -7,6 +7,64 @@ Class { #category : #'Alexandrie-Cairo-Tests' } +{ #category : #tests } +AeCairoImageSurfaceTest >> testConvertRoundTrip2Translucent [ + "The challenge in this test is that Cairo uses premultiplied-alpha on it's argb32 format, and Form straight-alpha." + + | aColor aSurface aForm aSurfaceBytes anotherSurface anotherSurfaceBytes | + aColor := Color red alpha: 0.4. + + aSurface := AeCairoImageSurface + extent: 1@1 + format: AeCairoSurfaceFormat argb32. + aSurface newContext + sourceColor: aColor; + paint. + + aForm := aSurface asFormARGB32. + self + assert: (aForm colorAt: 0@0) + equals: aColor. + + "Take the byte array as a reference" + aSurface flush. + aSurfaceBytes := FFIExternalArray + fromHandle: aSurface data + type: FFIUInt8 + size: aSurface byteSize. + + anotherSurface := AeCairoImageSurface fromForm: aForm. + anotherSurface flush. + anotherSurfaceBytes := FFIExternalArray + fromHandle: anotherSurface data + type: FFIUInt8 + size: anotherSurface byteSize. + + "The bytes of the reconverted surface should match" + self + assert: anotherSurfaceBytes + equals: aSurfaceBytes +] + +{ #category : #tests } +AeCairoImageSurfaceTest >> testConvertRoundTripTranslucent [ + "The challenge in this test is that Cairo uses premultiplied-alpha on it's argb32 format, and Form straight-alpha." + + | aColor aSurface aForm anotherForm | + aColor := Color red alpha: 0.4. + aForm := Form extent: 100@100 depth: 32. + aForm fillColor: aColor. + self + assert: (aForm colorAt: 0@0) + equals: aColor. + + aSurface := AeCairoImageSurface fromForm: aForm. + anotherForm := aSurface asForm. + self + assert: (anotherForm colorAt: 0@0) + equals: aColor +] + { #category : #tests } AeCairoImageSurfaceTest >> testConvertZeroWidthAsFormARGB32 [ diff --git a/src/Alexandrie-Cairo/AeCairoImageSurface.class.st b/src/Alexandrie-Cairo/AeCairoImageSurface.class.st index d077dc3..b0ddf9a 100644 --- a/src/Alexandrie-Cairo/AeCairoImageSurface.class.st +++ b/src/Alexandrie-Cairo/AeCairoImageSurface.class.st @@ -218,32 +218,40 @@ AeCairoImageSurface >> asFormA8 [ { #category : #converting } AeCairoImageSurface >> asFormARGB32 [ - "Answer myself converted as a 32-bits Form. Loseless conversion. + "Answer myself converted as a 32-bits Form, which means converting pixels from premultiplied-alpha to straight-alpha. Assumes: self format = AeCairoSurfaceFormat CAIRO_FORMAT_ARGB32 " - | uint32Size theData | - self flush. - - uint32Size := self byteSize. + | aSizeInWords theData | + aSizeInWords := self byteSize >> 2. - uint32Size = 0 ifTrue: [ + aSizeInWords = 0 ifTrue: [ ^ Form extent: self extent depth: 32 ]. + self flush. + theData := FFIExternalArray - fromHandle: self data getHandle + fromHandle: self data type: FFIUInt32 - size: uint32Size / 4. + size: aSizeInWords. ^ Form extent: self extent depth: 32 - bits: (Bitmap newFrom: theData) - + bits: (Bitmap new: aSizeInWords streamContents: [ :stream | + theData do: [ :pixel | + | alphaByte alpha a r g b | + alphaByte := pixel >> 24. + alpha := alphaByte / 255.0. + a := pixel bitAnd: 16rFF000000. + r := ( (pixel bitAnd: 255) / alpha ) asInteger. + g := ( (pixel >> 8 bitAnd: 255) / alpha ) asInteger << 8. + b := ( (pixel >> 16 bitAnd: 255) / alpha ) asInteger << 16. + stream nextPut: a + r + g + b ] ]) ] { #category : #API }