 [squeak-dev] Who understand bilinear interpolation for reducing image size?

tim Rowledge tim at rowledge.org
Mon Dec 8 20:15:04 UTC 2014

The ScratchPlugin implements a prim to shrink a 32bpp image by use of bilinear interpolation. Unfortunately it completely ignores the alpha channel in 32bpp pixels and does some rather odd futzing to kinda-sorta fake handling of transparency.

I can see how to add in (what I think would be) proper ARGB interpolating, and I think that simply removing the futzing would be correct - but I’d much rather have some input from somebody with a bit of image processing theory so there is some hope of my final result being actually correct.

This the core of the code -
inX := inY := 0.					"source x and y, scaled by 1024"
xIncr := (inW * 1024) // outW.	"source x increment, scaled by 1024"
yIncr := (inH * 1024) // outH.		"source y increment, scaled by 1024"

0 to: (outH - 1) do: [:outY |
inX := 0.
0 to: (outW - 1) do: [:outX |
"compute weights, scaled by 2^20"
w1 := (1024 - (inX bitAnd: 1023))	* (1024 - (inY bitAnd: 1023)).
w2 := (inX bitAnd: 1023)			* (1024 - (inY bitAnd: 1023)).
w3 := (1024 - (inX bitAnd: 1023))	* (inY bitAnd: 1023).
w4 := (inX bitAnd: 1023)			* (inY bitAnd: 1023).

"get source pixels"
t := ((inY >> 10) * inW) + (inX >> 10).
p1 := in at: t.
((inX >> 10) < (inW - 1)) ifTrue: [p2 := in at: t + 1] ifFalse: [p2 := p1].
(inY >> 10) < (inH - 1) ifTrue: [t := t + inW].  "next row"
p3 := in at: t.
((inX >> 10) < (inW - 1)) ifTrue: [p4 := in at: t + 1] ifFalse: [p4 := p3].

"deal with transparent pixels"
tWeight := 0.
p1 = 0 ifTrue: [p1 := p2. tWeight := tWeight + w1].
p2 = 0 ifTrue: [p2 := p1. tWeight := tWeight + w2].
p3 = 0 ifTrue: [p3 := p4. tWeight := tWeight + w3].
p4 = 0 ifTrue: [p4 := p3. tWeight := tWeight + w4].
p1 = 0 ifTrue: [p1 := p3. p2 := p4].  "both top pixels were transparent; use bottom row"
p3 = 0 ifTrue: [p3 := p1. p4 := p2].  "both bottom pixels were transparent; use top row"

outPix := 0.
tWeight < 500000 ifTrue: [  "compute an (opaque) output pixel if less than 50% transparent"
t := (w1 * ((p1 >> 16) bitAnd: 255)) + (w2 * ((p2 >> 16) bitAnd: 255)) + (w3 * ((p3 >> 16) bitAnd: 255)) + (w4 * ((p4 >> 16) bitAnd: 255)).
outPix := ((t >> 20) bitAnd: 255) << 16.
t := (w1 * ((p1 >> 8) bitAnd: 255)) + (w2 * ((p2 >> 8) bitAnd: 255)) + (w3 * ((p3 >> 8) bitAnd: 255)) + (w4 * ((p4 >> 8) bitAnd: 255)).
outPix := outPix bitOr: (((t >> 20) bitAnd: 255) << 8).
t := (w1 * (p1 bitAnd: 255)) + (w2 * (p2 bitAnd: 255)) + (w3 * (p3 bitAnd: 255)) + (w4 * (p4 bitAnd: 255)).
outPix := outPix bitOr: ((t >> 20) bitAnd: 255).
outPix = 0 ifTrue: [outPix := 1]].

out at: (outY * outW) + outX put: outPix.
inX := inX + xIncr].
inY := inY + yIncr].

Note that it doesn’t do any clipping, relying upon having only a full 32bpp input Form and a pre-built correctly sized 32bpp output Form.
On a Pi this prim is roughly 5 times faster than using a warpblt, so it would be nice to keep it in use.

tim
--
tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim
Strange OpCodes: BPP: Branch Pretty Please