Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
672d415
BlurFilter: Flash-faithful box blur behind -Dflash_box_blur
m0rkeulv Aug 31, 2026
c92ccd8
filters: box blur for glow/shadow/bevel too (-Dflash_box_blur)
m0rkeulv Aug 31, 2026
1cbd34b
filters: add GradientGlowFilter (missing from OpenFL)
m0rkeulv Aug 31, 2026
82366e1
filters: add GradientBevelFilter (missing from OpenFL)
m0rkeulv Aug 31, 2026
564bff5
Scale filter extension with quality to fix box-blur clip
m0rkeulv Aug 31, 2026
7908c67
GradientBevel: extension = true bevel extent, fixing over-wide band
m0rkeulv Aug 31, 2026
93aa2e2
Make Flash-faithful box blur the default (remove -Dflash_box_blur gate)
m0rkeulv Sep 1, 2026
af3b0bf
filters: name box-blur helpers/fields after the shader they configure
m0rkeulv Sep 1, 2026
5d9bda8
Box blur: per-texel fractional box (replaces the fused bilinear-pair …
m0rkeulv Sep 1, 2026
edf5f77
BlurFilter: rename __boxBlurShader field to __blurShader
m0rkeulv Sep 1, 2026
6cee260
refactoring
m0rkeulv Sep 1, 2026
0c1d00f
Bring the software filter path up to the GL path
m0rkeulv Sep 1, 2026
4fa70aa
Scale filter distances by the device pixel ratio
m0rkeulv Sep 1, 2026
c892081
Snap the filter cache bitmap to a whole device pixel
m0rkeulv Sep 1, 2026
0404537
refactoring
m0rkeulv Sep 2, 2026
6ff7aca
refactoring
m0rkeulv Sep 3, 2026
8f6cda6
Rename the CPU filter helpers from "field" to "mask"
m0rkeulv Sep 3, 2026
ad15e85
Bevel filters: name the mask samples and colours by meaning
m0rkeulv Sep 3, 2026
26a0d0e
refactoring
m0rkeulv Sep 3, 2026
27b6c53
refactoring
m0rkeulv Sep 3, 2026
0ef10fb
refactoring
m0rkeulv Sep 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/openfl/display/BitmapData.hx
Original file line number Diff line number Diff line change
Expand Up @@ -382,12 +382,12 @@ class BitmapData implements IBitmapDrawable

if (filter.__preserveObject)
{
bitmapData3.copyPixels(this, rect, destPoint);
bitmapData3.copyPixels(sourceBitmapData, sourceRect, destPoint);
}

var lastBitmap = filter.__applyFilter(bitmapData2, this, sourceRect, destPoint);
var lastBitmap = filter.__applyFilter(bitmapData2, sourceBitmapData, sourceRect, destPoint);

if (filter.__preserveObject)
if (filter.__preserveObject && !filter.__softwareComposite)
{
lastBitmap.draw(bitmapData3, null, null);
}
Expand Down
14 changes: 11 additions & 3 deletions src/openfl/display/DisplayObjectRenderer.hx
Original file line number Diff line number Diff line change
Expand Up @@ -387,8 +387,10 @@ class DisplayObjectRenderer extends EventDispatcher
filterWidth = rect.width > 0 ? Math.ceil((rect.width + 1) * pixelRatio) : 0;
filterHeight = rect.height > 0 ? Math.ceil((rect.height + 1) * pixelRatio) : 0;

offsetX = rect.x > 0 ? Math.ceil(rect.x) : Math.floor(rect.x);
offsetY = rect.y > 0 ? Math.ceil(rect.y) : Math.floor(rect.y);

// PixelRatio is adjusting for HDPI screns with fractional scaling. (1.5x, 1.25x etc)
offsetX = (rect.x > 0 ? Math.ceil(rect.x * pixelRatio) : Math.floor(rect.x * pixelRatio)) / pixelRatio;
offsetY = (rect.y > 0 ? Math.ceil(rect.y * pixelRatio) : Math.floor(rect.y * pixelRatio)) / pixelRatio;

if (displayObject.__cacheBitmapData != null)
{
Expand Down Expand Up @@ -668,6 +670,10 @@ class DisplayObjectRenderer extends EventDispatcher

for (filter in displayObject.__filters)
{
// the cache bitmap is drawn at pixelRatio, so blur radius and
// offsets (in logical pixels) must scale to match
filter.__renderScale = pixelRatio;

if (filter.__preserveObject)
{
childRenderer.__setRenderTarget(bitmap3);
Expand Down Expand Up @@ -785,14 +791,16 @@ class DisplayObjectRenderer extends EventDispatcher

for (filter in displayObject.__filters)
{
filter.__renderScale = pixelRatio;

if (filter.__preserveObject)
{
bitmap3.copyPixels(bitmap, bitmap.rect, destPoint);
}

lastBitmap = filter.__applyFilter(bitmap2, bitmap, bitmap.rect, destPoint);

if (filter.__preserveObject)
if (filter.__preserveObject && !filter.__softwareComposite)
{
lastBitmap.draw(bitmap3, null,
displayObject.__objectTransform != null ? displayObject.__objectTransform.__colorTransform : null);
Expand Down
97 changes: 66 additions & 31 deletions src/openfl/filters/BevelFilter.hx
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ import lime._internal.graphics.ImageDataUtil;

__needSecondBitmapData = true;
__preserveObject = true;
__softwareComposite = true;
__renderDirty = true;
}

Expand All @@ -155,15 +156,52 @@ import lime._internal.graphics.ImageDataUtil;

@:noCompletion private override function __applyFilter(bitmapData:BitmapData, sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point):BitmapData
{
#if lime
var time = Timer.stamp();
var finalImage = ImageDataUtil.gaussianBlur(bitmapData.image, sourceBitmapData.image, sourceRect.__toLimeRectangle(), destPoint.__toLimeVector2(),
__blurX, __blurY, __quality);
var elapsed = Timer.stamp() - time;
// trace("blurX: " + __blurX + " blurY: " + __blurY + " quality: " + __quality + " elapsed: " + elapsed * 1000 + "ms");
if (finalImage == bitmapData.image) return bitmapData;
#end
return sourceBitmapData;
var width = bitmapData.width;
var height = bitmapData.height;

// Same Shader: blur the source alpha, then compare the mask either side of the light direction.
// The signed difference drives the highlight (light side) and the shadow (dark side).
var mask = BitmapFilter.__alphaMask(sourceBitmapData, sourceRect, destPoint, width, height);
BitmapFilter.__blurMask(mask, width, height, __blurX * __renderScale, __blurY * __renderScale, __quality);

var rad = __angle * Math.PI / 180;
var dx = Std.int(Math.round(__distance * Math.cos(rad) * __renderScale));
var dy = Std.int(Math.round(__distance * Math.sin(rad) * __renderScale));

// highlight / shadow colours, each channel premultiplied by its alpha
// the same values the shader receives as uLightColor / uShadowColor
var highlightR = (((__highlightColor >> 16) & 0xFF) / 255.0) * __highlightAlpha;
var highlightG = (((__highlightColor >> 8) & 0xFF) / 255.0) * __highlightAlpha;
var highlightB = ((__highlightColor & 0xFF) / 255.0) * __highlightAlpha;
var shadowR = (((__shadowColor >> 16) & 0xFF) / 255.0) * __shadowAlpha;
var shadowG = (((__shadowColor >> 8) & 0xFF) / 255.0) * __shadowAlpha;
var shadowB = ((__shadowColor & 0xFF) / 255.0) * __shadowAlpha;

var fxR = new Array<Float>(), fxG = new Array<Float>(), fxB = new Array<Float>(), fxA = new Array<Float>();
for (y in 0...height)
{
for (x in 0...width)
{
// Sample the blurred mask a short way along the light angle in both
// directions. Flash's `angle` is where the light comes from, so `+offset`
// points the way a shadow falls and `-offset` points toward the light.
// (called blurLeft / blurRight in BevelShader)
var maskTowardShadow = BitmapFilter.__maskAt(mask, width, height, x + dx, y + dy);
var maskTowardLight = BitmapFilter.__maskAt(mask, width, height, x - dx, y - dy);
var dist = (maskTowardShadow - maskTowardLight) * __strength;

var highlight = dist > 1 ? 1.0 : (dist < 0 ? 0.0 : dist);
var shadow = -dist > 1 ? 1.0 : (-dist < 0 ? 0.0 : -dist);

fxR.push(highlightR * highlight + shadowR * shadow);
fxG.push(highlightG * highlight + shadowG * shadow);
fxB.push(highlightB * highlight + shadowB * shadow);
fxA.push(__highlightAlpha * highlight + __shadowAlpha * shadow);
}
}

var type:BitmapFilterType = (__type == "inner") ? INNER : ((__type == "outer") ? OUTER : FULL);
return BitmapFilter.__compositeEffect(bitmapData, sourceBitmapData, sourceRect, destPoint, fxR, fxG, fxB, fxA, type, __knockout);
}

@:noCompletion private override function __initShader(renderer:DisplayObjectRenderer, pass:Int, sourceBitmapData:BitmapData):Shader
Expand All @@ -173,22 +211,12 @@ import lime._internal.graphics.ImageDataUtil;
var numBlurPasses = __horizontalPasses + __verticalPasses;
if (blurPass < numBlurPasses)
{
var shader = BlurFilter.__blurShader;
if (pass < __horizontalPasses)
{
var scale = Math.pow(0.5, pass >> 1);
shader.uRadius.value[0] = blurX * scale;
shader.uRadius.value[1] = 0;
}
else
{
var scale = Math.pow(0.5, (pass - __horizontalPasses) >> 1);
shader.uRadius.value[0] = 0;
shader.uRadius.value[1] = blurY * scale;
}
return shader;
var horizontal = pass < __horizontalPasses;
return BlurFilter.__setupBlurShader(horizontal, (horizontal ? blurX : blurY) * __renderScale);
}

// transform is scaled by __renderScale, which is not known until render time
__updateTransform();
__bevelShader.sourceBitmap.input = sourceBitmapData;
#end

Expand Down Expand Up @@ -346,12 +374,14 @@ import lime._internal.graphics.ImageDataUtil;
value = value < 1 ? 1 : value;
value = value > 15 ? 15 : value;

__horizontalPasses = (__blurX <= 0) ? 0 : Math.round(__blurX * (value / 4));
__verticalPasses = (__blurY <= 0) ? 0 : Math.round(__blurY * (value / 4));
__horizontalPasses = (__blurX <= 0) ? 0 : value;
__verticalPasses = (__blurY <= 0) ? 0 : value;

__numShaderPasses = __horizontalPasses + __verticalPasses + 1;

if (value != __quality) __renderDirty = true;
__quality = value;
__updateSize(); // depends on filter's quality settings
return __quality = value;
}

Expand Down Expand Up @@ -418,8 +448,8 @@ import lime._internal.graphics.ImageDataUtil;
@:noCompletion private function __updateTransform():Void
{
var rad:Float = __angle * Math.PI / 180;
__bevelShader.uTransformX.value[0] = (__distance * Math.cos(rad));
__bevelShader.uTransformY.value[0] = (__distance * Math.sin(rad));
__bevelShader.uTransformX.value[0] = (__distance * Math.cos(rad)) * __renderScale;
__bevelShader.uTransformY.value[0] = (__distance * Math.sin(rad)) * __renderScale;
}

@:noCompletion private function __updateColors():Void
Expand All @@ -445,10 +475,15 @@ import lime._internal.graphics.ImageDataUtil;
{
var offsetX:Int = __type != "inner" ? Math.ceil(__distance * Math.cos(__angle * Math.PI / 180)) : 0;
var offsetY:Int = __type != "inner" ? Math.ceil(__distance * Math.sin(__angle * Math.PI / 180)) : 0;
__topExtension = Math.ceil((offsetY < 0 ? -offsetY : 0) + __blurY);
__bottomExtension = Math.ceil((offsetY > 0 ? offsetY : 0) + __blurY);
__leftExtension = Math.ceil((offsetX < 0 ? -offsetX : 0) + __blurX);
__rightExtension = Math.ceil((offsetX > 0 ? offsetX : 0) + __blurX);
// Box blur reach grows to ~quality*blur/2 per side (see DropShadowFilter);
// reserving the full spread so the bevel isn't clipped at high quality.
var q = (__quality > 0) ? __quality : 1;
var exX = Math.ceil(__blurX * 0.5 * q) + 4;
var exY = Math.ceil(__blurY * 0.5 * q) + 4;
__topExtension = (offsetY < 0 ? -offsetY : 0) + exY;
__bottomExtension = (offsetY > 0 ? offsetY : 0) + exY;
__leftExtension = (offsetX < 0 ? -offsetX : 0) + exX;
__rightExtension = (offsetX > 0 ? offsetX : 0) + exX;
}
}

Expand Down
Loading
Loading