Method for software Displacement Map Filtering

With optional, bilinear filtering. This may be an initial implementation for now. Can be improved later on.
This commit is contained in:
Łukasz Modliński
2018-10-18 09:57:55 +02:00
committed by Joshua Granick
parent f6d24f2d19
commit e2a09d3910

View File

@@ -31,7 +31,144 @@ import lime.utils.UInt8Array;
class ImageDataUtil { class ImageDataUtil {
public static function displaceMap(
target:Image, source:Image, map:Image, mapPoint:Vector2,
componentX:Vector4, componentY:Vector4,
smooth:Bool
):Void {
var targetData:UInt8Array = target.buffer.data;
var sourceData:UInt8Array = source.buffer.data;
var mapData:UInt8Array = map.buffer.data;
var targetFormat:PixelFormat = target.buffer.format;
var sourceFormat:PixelFormat = source.buffer.format;
var mapFormat:PixelFormat = map.buffer.format;
var targetPremultiplied:Bool = target.premultiplied;
var sourcePremultiplied:Bool = source.premultiplied;
var mapPremultiplied:Bool = map.premultiplied;
var sourceView:ImageDataView = new ImageDataView(source);
var mapView:ImageDataView = new ImageDataView(map);
var row:Int;
var sourceOffset:Int;
var sourcePixel:RGBA;
var mapPixel:RGBA;
var targetPixel:RGBA;
var mapPixelX:Float;
var mapPixelY:Float;
var mapPixelA:Float;
// for bilinear smoothing
var s1:RGBA;
var s2:RGBA;
var s3:RGBA;
var s4:RGBA;
var mPointXFloor:Int;
var mPointYFloor:Int;
var disOffsetXFloor:Int;
var disOffsetYFloor:Int;
var disX:Float;
var disY:Float;
for (y in 0...sourceView.height) {
row = sourceView.row(y);
for (x in 0...sourceView.width) {
sourceOffset = row + (x * 4);
mPointXFloor = Std.int(mapPoint.x);
mPointYFloor = Std.int(mapPoint.y);
if (smooth) {
s1.readUInt8(mapData, sourceView.row(y - mPointYFloor + 1) + (x - mPointXFloor) * 4, mapFormat, mapPremultiplied);
s2.readUInt8(mapData, sourceView.row(y - mPointYFloor) + (x - mPointXFloor + 1) * 4, mapFormat, mapPremultiplied);
s3.readUInt8(mapData, sourceView.row(y - mPointYFloor + 1) + (x - mPointXFloor + 1) * 4, mapFormat, mapPremultiplied);
s4.readUInt8(mapData, sourceView.row(y - mPointYFloor) + (x - mPointXFloor) * 4, mapFormat, mapPremultiplied);
mapPixel = bilinear(
s1, s2, s3, s4,
mapPoint.x - mPointXFloor,
mapPoint.y - mPointYFloor
);
} else {
mapPixel.readUInt8(mapData, mapView.row(y - mPointYFloor) + (x - mPointXFloor) * 4, mapFormat, mapPremultiplied);
}
mapPixelA = mapPixel.a / 255.0;
mapPixelX = (((mapPixel.r - 128) / 255.0)) * mapPixelA;
mapPixelY = (((mapPixel.g - 128) / 255.0)) * mapPixelA;
disX = mapPixelX * componentX.x + mapPixelY * componentY.x;
disY = mapPixelX * componentX.y + mapPixelY * componentY.y;
disOffsetXFloor = Math.floor(disX * sourceView.width);
disOffsetYFloor = Math.floor(disY * sourceView.height);
if (smooth) {
s1.readUInt8(sourceData, sourceView.row(y + disOffsetYFloor + 1) + (x + disOffsetXFloor) * 4, sourceFormat, sourcePremultiplied);
s2.readUInt8(sourceData, sourceView.row(y + disOffsetYFloor) + (x + disOffsetXFloor + 1) * 4, sourceFormat, sourcePremultiplied);
s3.readUInt8(sourceData, sourceView.row(y + disOffsetYFloor + 1) + (x + disOffsetXFloor + 1) * 4, sourceFormat, sourcePremultiplied);
s4.readUInt8(sourceData, sourceView.row(y + disOffsetYFloor) + (x + disOffsetXFloor) * 4, sourceFormat, sourcePremultiplied);
sourcePixel = bilinear(
s1, s2, s3, s4,
disX * sourceView.width - disOffsetXFloor,
disY * sourceView.height - disOffsetYFloor
);
} else {
sourcePixel.readUInt8(sourceData, sourceView.row(y + disOffsetYFloor) + (x + disOffsetXFloor) * 4, sourceFormat, sourcePremultiplied);
}
sourcePixel.writeUInt8(targetData, sourceOffset, targetFormat, targetPremultiplied);
}
}
target.dirty = true;
target.version++;
}
// s1 = (x, y+1)
// s2 = (x + 1, y);
// s3 = (x + 1, y + 1);
// s4 = (x, y)
private static function bilinear(s1:RGBA, s2:RGBA, s3:RGBA, s4:RGBA, su:Float, sv:Float):RGBA {
return lerpRGBA(
lerpRGBA(s4, s2, su),
lerpRGBA(s1, s3, su),
sv
);
}
private static function lerpRGBA(v0:RGBA, v1:RGBA, x:Float):RGBA {
var result:RGBA = new RGBA();
result.r = Math.floor(lerp(v0.r, v1.r, x));
result.g = Math.floor(lerp(v0.g, v1.g, x));
result.b = Math.floor(lerp(v0.b, v1.b, x));
result.a = Math.floor(lerp(v0.a, v1.a, x));
return result;
}
private static function lerp4f(v0:Vector4, v1:Vector4, x:Float):Vector4 {
return new Vector4(
lerp(v0.x, v1.x, x),
lerp(v0.y, v1.y, x),
lerp(v0.z, v1.z, x),
lerp(v0.w, v1.w, x)
);
}
private static function lerp(v0:Float, v1:Float, x:Float):Float {
return (1.0 - x) * v0 + x * v1;
}
public static function colorTransform (image:Image, rect:Rectangle, colorMatrix:ColorMatrix):Void { public static function colorTransform (image:Image, rect:Rectangle, colorMatrix:ColorMatrix):Void {