Skip to content

Commit

Permalink
Fix asFormARGB32, which wasn't converting from premultiplied alpha, a…
Browse files Browse the repository at this point in the history
…s Form requires.

Fixes #35.
Includes 2 new tests
  • Loading branch information
tinchodias committed Sep 21, 2023
1 parent ec63ddb commit ddec70d
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 10 deletions.
58 changes: 58 additions & 0 deletions src/Alexandrie-Cairo-Tests/AeCairoImageSurfaceTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -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 [

Expand Down
28 changes: 18 additions & 10 deletions src/Alexandrie-Cairo/AeCairoImageSurface.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down

0 comments on commit ddec70d

Please sign in to comment.