置换图滤镜

Flash Player 9 和更高版本,Adobe AIR 1.0 和更高版本

DisplacementMapFilter 类使用 BitmapData 对象(称为置换图图像)的像素值在新对象上执行置换效果。通常,置换图图像与将要应用滤镜的实际显示对象或 BitmapData 实例不同。置换效果包括置换经过过滤的图像中的像素,即让这些像素离开各自原始位置一定距离。此滤镜可用于产生移位、扭曲或斑点效果。

应用于给定像素的置换位置和置换量由置换图图像的颜色值确定。使用滤镜时,除了指定置换图图像外,还要指定以下值,以便控制置换图图像中计算置换的方式:

  • 映射点:过滤图像上的位置,在该点将应用置换滤镜的左上角。如果只想对图像的一部分应用滤镜,可以使用此值。

  • X 组件:影响像素的 x 位置的置换图图像的颜色通道。

  • Y 组件:影响像素的 y 位置的置换图图像的颜色通道。

  • X 缩放比例:指定 x 轴置换强度的乘数值。

  • Y 缩放比例:指定 y 轴置换强度的乘数值。

  • 滤镜模式:确定在移开像素后形成的空白区域中要执行什么操作。在 DisplacementMapFilterMode 类中定义为常量的选项可以显示原始像素(滤镜模式 IGNORE)、从图像的另一侧环绕像素(滤镜模式 WRAP,这是默认设置)、使用最近的移位像素(滤镜模式 CLAMP)或用颜色填充空间(滤镜模式 COLOR)。

若要了解置换图滤镜的工作原理,请查看下面的基本示例。在以下代码中,将加载一个图像,完成加载后使图像在舞台上居中并对它应用置换图滤镜,使整个图像中的像素向左水平移位。

import flash.display.BitmapData; 
import flash.display.Loader; 
import flash.events.MouseEvent; 
import flash.filters.DisplacementMapFilter; 
import flash.geom.Point; 
import flash.net.URLRequest; 
 
// Load an image onto the Stage. 
var loader:Loader = new Loader(); 
var url:URLRequest = new URLRequest("http://www.helpexamples.com/flash/images/image3.jpg"); 
loader.load(url); 
this.addChild(loader); 
 
var mapImage:BitmapData; 
var displacementMap:DisplacementMapFilter; 
 
// This function is called when the image finishes loading. 
function setupStage(event:Event):void  
{ 
    // Center the loaded image on the Stage. 
    loader.x = (stage.stageWidth - loader.width) / 2;  
    loader.y = (stage.stageHeight - loader.height) / 2; 
     
    // Create the displacement map image. 
    mapImage = new BitmapData(loader.width, loader.height, false, 0xFF0000); 
     
    // Create the displacement filter. 
    displacementMap = new DisplacementMapFilter(); 
    displacementMap.mapBitmap = mapImage; 
    displacementMap.mapPoint = new Point(0, 0); 
    displacementMap.componentX = BitmapDataChannel.RED; 
    displacementMap.scaleX = 250; 
    loader.filters = [displacementMap]; 
} 
 
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, setupStage);

用于定义置换的属性有:

  • 置换图位图:置换位图是由代码创建的新的 BitmapData 实例。它的尺寸与加载的图像的尺寸匹配(因此会将置换应用于整个图像)。用纯红色像素填充此实例。

  • 映射点:将此值设置为点 0, 0,使置换再次应用于整个图像。

  • X 组件:此值设置为常量 BitmapDataChannel.RED,表示置换图位图的红色值将决定沿着 x 轴置换像素的程度(像素的移动程度)。

  • X 缩放比例:此值设置为 250。由于全部置换量(与全红的置换图图像的距离)仅使图像置换很小的量(大约为一个像素的一半),因此,如果将此值设置为 1,图像只会水平移动 0.5 个像素。将此值设置为 250,图像将移动大约 125 个像素。

这些设置可使过滤图像的像素向左移动 250 个像素。移动的方向(向左或向右)和移动量取决于置换图图像中的像素的颜色值。从概念上来说,滤镜会逐一处理过滤图像的像素(至少处理将应用滤镜的区域中的像素,在本例中指所有像素),并对每个像素执行以下操作:

  1. Flash Player 在置换图图像中查找相应的像素。例如,当滤镜计算过滤图像左上角像素的置换量时,会在置换图图像左上角中查找像素。

  2. Flash Player 确定置换图像素中指定颜色通道的值。在本例中,x 组件颜色通道是红色通道,因此滤镜将查看映射图像中该问题像素所在位置处的红色通道所对应的值。由于置换图图像是纯红色的,所以像素的红色通道为 0xFF(即 255)。该值将用作置换值。

  3. 比较置换值和“中间”值(127,它是 0 和 255 之间的中间值)。如果置换值低于中间值,则像素正向移位(x 置换向右;y 置换向下)。另一方面,如果置换值高于中间值(如本示例),则像素负向移位(x 置换向左;y 置换向上)。为更精确起见,滤镜会从 127 中减去置换值,结果(正或负)即是应用的相对置换量。

  4. 最后,Flash Player 通过确定相对置换值所表示的完全置换量的百分比来确定实际置换量。在本例中,全红色意味着 100% 置换。然后用 x 缩放比例值或 y 缩放比例值乘以该百分比,以确定将应用的置换像素数。在本示例中,100% 乘以一个乘数 250 可以确定置换量(大约为向左移动 125 个像素)。

因为没有为 y 分量和 y 缩放比例指定值,所以使用默认值(不发生置换),这就是图像在垂直方向不移位的原因。

由于在本示例中使用了默认滤镜模式设置 WRAP,因此在像素向左移位时,会用移到图像左边缘以外的像素填充右侧空白区域。您可以对此设置试用不同的值以查看不同的效果。例如,如果您在设置置换属性的代码部分中(在 loader.filters = [displacementMap] 行之前)添加以下一行内容,则会使图像在舞台上产生涂抹的效果:

displacementMap.mode = DisplacementMapFilterMode.CLAMP;

下面是一个更为复杂的示例,代码清单中使用置换图滤镜在图像上创建放大镜效果:

import flash.display.Bitmap; 
import flash.display.BitmapData; 
import flash.display.BitmapDataChannel; 
import flash.display.GradientType; 
import flash.display.Loader; 
import flash.display.Shape; 
import flash.events.MouseEvent; 
import flash.filters.DisplacementMapFilter; 
import flash.filters.DisplacementMapFilterMode; 
import flash.geom.Matrix; 
import flash.geom.Point; 
import flash.net.URLRequest; 
 
// Create the gradient circles that will together form the  
// displacement map image 
var radius:uint = 50; 
 
var type:String = GradientType.LINEAR; 
var redColors:Array = [0xFF0000, 0x000000]; 
var blueColors:Array = [0x0000FF, 0x000000]; 
var alphas:Array = [1, 1]; 
var ratios:Array = [0, 255]; 
var xMatrix:Matrix = new Matrix(); 
xMatrix.createGradientBox(radius * 2, radius * 2); 
var yMatrix:Matrix = new Matrix(); 
yMatrix.createGradientBox(radius * 2, radius * 2, Math.PI / 2); 
 
var xCircle:Shape = new Shape(); 
xCircle.graphics.lineStyle(0, 0, 0); 
xCircle.graphics.beginGradientFill(type, redColors, alphas, ratios, xMatrix); 
xCircle.graphics.drawCircle(radius, radius, radius); 
 
var yCircle:Shape = new Shape(); 
yCircle.graphics.lineStyle(0, 0, 0); 
yCircle.graphics.beginGradientFill(type, blueColors, alphas, ratios, yMatrix); 
yCircle.graphics.drawCircle(radius, radius, radius); 
 
// Position the circles at the bottom of the screen, for reference. 
this.addChild(xCircle); 
xCircle.y = stage.stageHeight - xCircle.height; 
this.addChild(yCircle); 
yCircle.y = stage.stageHeight - yCircle.height; 
yCircle.x = 200; 
 
// Load an image onto the Stage. 
var loader:Loader = new Loader(); 
var url:URLRequest = new URLRequest("http://www.helpexamples.com/flash/images/image1.jpg"); 
loader.load(url); 
this.addChild(loader); 
 
// Create the map image by combining the two gradient circles. 
var map:BitmapData = new BitmapData(xCircle.width, xCircle.height, false, 0x7F7F7F); 
map.draw(xCircle); 
var yMap:BitmapData = new BitmapData(yCircle.width, yCircle.height, false, 0x7F7F7F); 
yMap.draw(yCircle); 
map.copyChannel(yMap, yMap.rect, new Point(0, 0), BitmapDataChannel.BLUE, BitmapDataChannel.BLUE); 
yMap.dispose(); 
 
// Display the map image on the Stage, for reference. 
var mapBitmap:Bitmap = new Bitmap(map); 
this.addChild(mapBitmap); 
mapBitmap.x = 400; 
mapBitmap.y = stage.stageHeight - mapBitmap.height; 
 
// This function creates the displacement map filter at the mouse location. 
function magnify():void 
{ 
    // Position the filter. 
    var filterX:Number = (loader.mouseX) - (map.width / 2); 
    var filterY:Number = (loader.mouseY) - (map.height / 2); 
    var pt:Point = new Point(filterX, filterY); 
    var xyFilter:DisplacementMapFilter = new DisplacementMapFilter(); 
    xyFilter.mapBitmap = map; 
    xyFilter.mapPoint = pt; 
    // The red in the map image will control x displacement. 
    xyFilter.componentX = BitmapDataChannel.RED; 
    // The blue in the map image will control y displacement. 
    xyFilter.componentY = BitmapDataChannel.BLUE; 
    xyFilter.scaleX = 35; 
    xyFilter.scaleY = 35; 
    xyFilter.mode = DisplacementMapFilterMode.IGNORE; 
    loader.filters = [xyFilter]; 
} 
 
// This function is called when the mouse moves. If the mouse is 
// over the loaded image, it applies the filter. 
function moveMagnifier(event:MouseEvent):void 
{ 
    if (loader.hitTestPoint(loader.mouseX, loader.mouseY)) 
    { 
        magnify(); 
    } 
} 
loader.addEventListener(MouseEvent.MOUSE_MOVE, moveMagnifier);

代码首先生成两个渐变圆,它们合并在一起构成置换图图像。红色圆创建 x 轴置换 (xyFilter.componentX = BitmapDataChannel.RED),蓝色圆创建 y 轴置换 (xyFilter.componentY = BitmapDataChannel.BLUE)。为帮助您理解置换图图像的外观,代码向屏幕底部添加了原始圆形以及合并后作为置换图图像的圆形。

显示花朵照片的图像,鼠标光标之下的圆形部分进行了放大。

然后,代码加载一个图像,当鼠标移动时,将置换滤镜应用于鼠标下方的图像部分。用作置换图图像的渐变圆使置换区域从指针处向外扩展。请注意,置换图图像的灰色区域不会发生置换。灰色为 0x7F7F7F。该灰色的蓝色和红色通道与这些颜色通道中的中间色度完全匹配,因此在置换图图像的灰色区域不会发生置换。同样,在圆的中心也不会发生置换。由于蓝色和红色是引起置换的颜色,虽然颜色中没有灰色,但该颜色的蓝色通道和红色通道与中度灰的蓝色通道和红色通道完全相同,因此该处不发生置换。