I’m not really super into math behind Gaussian Blur but this implementation has good theory behind it as it was measured in the link I’ve found - it’s really fast compared to the standard implementation - that modification of both source and target is the disadvantage but comparing its time to other implementations it is ahead a lot
I’d love to hear your opinion on that clone() i’ve mentioned and shown on code samples - from my perspective, at least for now it is mandatory for these filters to work correctly but I’m not sure whether that may be any problem. I can look into algorithm itself tomorrow since I’ll get some spare time to work on these things.
For now here are two of my patches for OpenFL and Lime respectively from today’s researches - you can check them out and apply them if you want:
For OpenFL:
Index: src/openfl/filters/DropShadowFilter.hx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/openfl/filters/DropShadowFilter.hx (revision c7a2800757565a565ebead9ab952e25660128c61)
+++ src/openfl/filters/DropShadowFilter.hx (revision )
@@ -268,19 +268,12 @@
var r = (__color >> 16) & 0xFF;
var g = (__color >> 8) & 0xFF;
var b = __color & 0xFF;
- sourceBitmapData.colorTransform (sourceBitmapData.rect, new ColorTransform (0, 0, 0, __alpha, r, g, b, 0));
-
- destPoint.x += __offsetX;
- destPoint.y += __offsetY;
+ bitmapData.colorTransform (bitmapData.rect, new ColorTransform (0, 0, 0, __alpha, r, g, b, 0));
+
+ var point:Point = new Point(__offsetX, __offsetY);
+ var finalImage = ImageDataUtil.gaussianBlur (sourceBitmapData.clone().image, bitmapData.image, sourceRect.__toLimeRectangle (), point.__toLimeVector2 (), __blurX, __blurY, __quality, __strength);
+ return finalImage == sourceBitmapData.image ? sourceBitmapData : bitmapData;
- var finalImage = ImageDataUtil.gaussianBlur (bitmapData.image, sourceBitmapData.image, sourceRect.__toLimeRectangle (), destPoint.__toLimeVector2 (), __blurX, __blurY, __quality, __strength);
-
- destPoint.x = __offsetX;
- destPoint.y = __offsetY;
-
- if (finalImage == bitmapData.image) return bitmapData;
- return sourceBitmapData;
-
}
Index: src/openfl/filters/GlowFilter.hx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/openfl/filters/GlowFilter.hx (revision c7a2800757565a565ebead9ab952e25660128c61)
+++ src/openfl/filters/GlowFilter.hx (revision )
@@ -240,12 +240,10 @@
var r = (__color >> 16) & 0xFF;
var g = (__color >> 8) & 0xFF;
var b = __color & 0xFF;
- sourceBitmapData.colorTransform (sourceBitmapData.rect, new ColorTransform (0, 0, 0, __alpha, r, g, b, 0));
+ bitmapData.colorTransform (bitmapData.rect, new ColorTransform (0, 0, 0, __alpha, r, g, b, 0));
- var finalImage = ImageDataUtil.gaussianBlur (bitmapData.image, sourceBitmapData.image, sourceRect.__toLimeRectangle (), destPoint.__toLimeVector2 (), __blurX, __blurY, __quality, __strength);
-
- if (finalImage == bitmapData.image) return bitmapData;
- return sourceBitmapData;
+ var finalImage = ImageDataUtil.gaussianBlur (sourceBitmapData.clone().image, bitmapData.image, sourceRect.__toLimeRectangle (), destPoint.__toLimeVector2 (), __blurX, __blurY, __quality, __strength);
+ return finalImage == sourceBitmapData.image ? sourceBitmapData : bitmapData;
}
Index: src/openfl/filters/BlurFilter.hx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/openfl/filters/BlurFilter.hx (revision c7a2800757565a565ebead9ab952e25660128c61)
+++ src/openfl/filters/BlurFilter.hx (revision )
@@ -188,10 +188,9 @@
@:noCompletion private override function __applyFilter (bitmapData:BitmapData, sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point):BitmapData {
- var finalImage = ImageDataUtil.gaussianBlur (bitmapData.image, sourceBitmapData.image, sourceRect.__toLimeRectangle (), destPoint.__toLimeVector2 (), __blurX, __blurY, __quality);
- if (finalImage == bitmapData.image) return bitmapData;
- return sourceBitmapData;
-
+ var finalImage = ImageDataUtil.gaussianBlur (sourceBitmapData.clone().image, bitmapData.image, sourceRect.__toLimeRectangle (), destPoint.__toLimeVector2 (), __blurX, __blurY, __quality);
+ return finalImage == sourceBitmapData.image ? sourceBitmapData : bitmapData;
+
}
Index: src/openfl/display/DisplayObject.hx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/openfl/display/DisplayObject.hx (revision c7a2800757565a565ebead9ab952e25660128c61)
+++ src/openfl/display/DisplayObject.hx (revision )
@@ -2394,7 +2394,7 @@
bitmap3.copyPixels (bitmap, bitmap.rect, destPoint);
}
- lastBitmap = filter.__applyFilter (bitmap2, bitmap, sourceRect, destPoint);
+ lastBitmap = filter.__applyFilter (bitmap, bitmap2, sourceRect, destPoint);
if (filter.__preserveObject) {
lastBitmap.draw (bitmap3, null, __objectTransform != null ? __objectTransform.colorTransform : null);
For Lime:
Index: src/lime/_internal/graphics/ImageDataUtil.hx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/lime/_internal/graphics/ImageDataUtil.hx (revision 4a8125e03641d1dc7bf18b6e6d779b1d24e7d3cb)
+++ src/lime/_internal/graphics/ImageDataUtil.hx (revision )
@@ -507,15 +507,15 @@
}
- public static function gaussianBlur (image:Image, sourceImage:Image, sourceRect:Rectangle, destPoint:Vector2, blurX:Float = 4, blurY:Float = 4, quality:Int = 1, strength:Float = 1):Image {
+ public static function gaussianBlur (sourceImage:Image, destImage:Image, sourceRect:Rectangle, destPoint:Vector2, blurX:Float = 4, blurY:Float = 4, quality:Int = 1, strength:Float = 1):Image {
// TODO: Support sourceRect better, do not modify sourceImage, create C++ implementation for native
// TODO: Faster approach
- var imagePremultiplied = image.premultiplied;
var sourceImagePremultiplied = sourceImage.premultiplied;
- if (imagePremultiplied) image.premultiplied = false;
+ var destImagePremultiplied = destImage.premultiplied;
if (sourceImagePremultiplied) sourceImage.premultiplied = false;
+ if (destImagePremultiplied) destImage.premultiplied = false;
// if (image.buffer.premultiplied || sourceImage.buffer.premultiplied) {
// // TODO: Better handling of premultiplied alpha
@@ -618,8 +618,8 @@
boxBlurT(imgA, imgB, w, h, Std.int(by), 3);
}
- var imgB = image.data;
- var imgA = sourceImage.data;
+ var imgB = sourceImage.data;
+ var imgA = destImage.data;
var w = Std.int (sourceRect.width);
var h = Std.int (sourceRect.height);
var bx = Std.int (blurX);
@@ -650,7 +650,7 @@
while (y < h) {
x = 0;
while (x < w) {
- translatePixel(imgB, sourceImage.rect, image.rect, destPoint, x, y, strength);
+ translatePixel(imgA, sourceImage.rect, destImage.rect, destPoint, x, y, strength);
x += 1;
}
y += 1;
@@ -660,23 +660,23 @@
while (y >= 0 ) {
x = w-1;
while (x >= 0) {
- translatePixel(imgB, sourceImage.rect, image.rect, destPoint, x, y, strength);
+ translatePixel(imgA, sourceImage.rect, destImage.rect, destPoint, x, y, strength);
x -= 1;
}
y -= 1;
}
}
- image.dirty = true;
- image.version++;
sourceImage.dirty = true;
sourceImage.version++;
-
- if (imagePremultiplied) image.premultiplied = true;
+ destImage.dirty = true;
+ destImage.version++;
+
if (sourceImagePremultiplied) sourceImage.premultiplied = true;
+ if (destImagePremultiplied) destImage.premultiplied = true;
- if (imgB == image.data) return image;
- return sourceImage;
+ if (imgA == sourceImage.data) return sourceImage;
+ return destImage;
}
and here is my sample to test - by clicking on the screen you can update .text
property with Math.random()
package;
import openfl.filters.DropShadowFilter;
import openfl.events.MouseEvent;
import openfl.text.AntiAliasType;
import openfl.display.Bitmap;
import openfl.utils.Assets;
import openfl.text.TextFormatAlign;
import openfl.filters.BitmapFilterQuality;
import openfl.filters.GlowFilter;
import openfl.text.TextFormat;
import openfl.display.Sprite;
import openfl.text.TextField;
class Main extends Sprite {
private var label_glow:TextField;
private var label_base:TextField;
public function new() {
super();
var dropShadow:DropShadowFilter = new DropShadowFilter(5.0, 45.0, 0x000000, 1.0, 0.0, 0.0, 150.0, BitmapFilterQuality.HIGH, false, false, false);
var glow:GlowFilter = new GlowFilter(0x000000, 1.0, 2.0, 2.0, 150.0, BitmapFilterQuality.HIGH, false, false);
var format:TextFormat = new TextFormat('Arial', 50, 0xFFFFFF, null, null, null, null, null, TextFormatAlign.CENTER, null, null, null, null);
format.align = TextFormatAlign.CENTER;
format.size = 100;
format.color = 0x00FFFF;
// base
this.label_base = new TextField();
this.label_base.defaultTextFormat = format;
this.label_base.width = 700;
this.label_base.height = 150;
this.label_base.selectable = false;
this.label_base.text = Std.string(Math.random());
this.addChild(this.label_base);
this.label_base.y = 0;
this.label_base.filters = [ dropShadow ];
// glow
this.label_glow = new TextField();
this.label_glow.defaultTextFormat = format;
this.label_glow.width = 700;
this.label_glow.height = 150;
this.label_glow.selectable = false;
this.label_glow.cacheAsBitmap = true;
this.label_glow.text = Std.string(Math.random());
this.addChild(this.label_glow);
this.label_glow.y = 150;
this.label_glow.filters = [ glow ];
this.addEventListener(MouseEvent.CLICK, function(e:MouseEvent){
this.label_base.text = Std.string(Math.random());
this.label_glow.text = Std.string(Math.random());
});
}
}