超酷的代码
采用 Silverlight 深度缩放到下一个级别
Jeff Prosise
内容
修复编辑器的平移逻辑
访问 Sub-images 和元数据
动态深度缩放: 在运行时提供图像像素为单位)
DeepZoomTools.dll
世界 MIX 2008 在 Silverlight Deep Zoom 推出后,周围获得商业信息 (有关保留周)。在 Seadragon 项目的一个 outgrowthMicrosoft 实时实验室一种技术的带宽的高效的方式向用户图示的数据的大量的在 Silverlight 适配,Deep Zoom。目标,Windows Mobile 和 AJAX sister 改编并且用于增加范围扩大平台。
如果您没有看到 Zoom 之前,放在您执行的操作,并访问在 memorabilia.hardrock.com 规范 Deep Zoom 站点。使用鼠标在屏幕和鼠标滚轮进行缩放入和签出平移周围。由于要 Deep Zoom 并不需要下载千兆字节 (或万亿字节) 的图像浏览硬摇滚 Café 的大量 memorabilia 集合。Deep Zoom 下载它需要在解析它需要并在 Silverlight 中的像素,一个名为 MultiScaleImage 的明显控件后面屏蔽 Deep Zoom 的复杂性。一旦 Deep Zoom 场景由组成 (通常具有 Deep Zoom 编辑器,您可以从 go.microsoft.com/fwlink/?LinkId=148861 免费下载的),演示在浏览器中的场景需要很少多个声明 MultiScaleImage 控件 Deep Zoom 编辑器的输出中指向该控件的来源属性。支持交互式平移和缩放需要一些控件,但 Deep Zoom 编辑器将为您甚至提供的这些天进行交互的鼠标处理代码。
尽管轻松地生成一个基本的 Deep Zoom 的应用程序可以与您正在不了解的则返回 True Deep Zoom 丰富的程度如果您不会再比 Deep Zoom 编辑器使您转。您是否知道,渚嬪的方式 您可以以编程方式操作中一个 Zoom 场景,您可以创建元数据并将其与每个的图像相关联的图像或 Deep Zoom 图像可以来自数据库被动态组合吗?某些应用真正明显 Deep Zoom 程序出有依赖的平台添加整个新维度的 Deep Zoom 一鲜为人知的功能。
如果您关心要带至下一级别的 Silverlight Zoom,下面是三个方法只需这样做。
修复编辑器的平移逻辑
要事第一个: 如果您希望从中获益更多 Deep Zoom,首先您应该知道是不信任发出 Deep Zoom 编辑器鼠标处理代码。pans MouseMove 事件以响应屏幕周围的代码"丢失"鼠标如果您平移太快。再试。采取任何 Deep Zoom 应用程序由 Deep Zoom 的编辑器,并将鼠标光标放置一个可识别的点或像素场景中。然后移动快速来回向上和向下几次鼠标。请注意当您停止场景 springs 回到光标时光标位于不再在您启动时的点。更多和快移动,越差。这不是交易断字符,但尝试相同硬摇滚 Memorabilia 网站上的试验而您会发现场景可靠地对齐回原来的光标位置,无论您如何硬试图欺骗它。
图 1 显示了如何修改 Deep Zoom 编辑器的代码,以解决此问题。第一次,声明两个名为 lastViewportOrigin 和 lastMousePosition Page.xaml.cs 的页类中的新字段。(您在它时删除名为 dragOffset 和 currentPosition,字段因为您不需要它们。 然后重写 MouseLeftButtonDown 和 MouseMove 处理程序,如下所示。您会发现场景回时对齐到精确原来的光标位置停止移动鼠标,如果 fastidious 我有关这些操作您您将能够在夜间次睡眠状态。
图 1 修复深度缩放编辑器的移动代码
Point lastViewportOrigin;
Point lastMousePosition;
...
this.MouseLeftButtonDown += delegate(object sender, MouseButtonEventArgs e)
{
mouseButtonPressed = true;
mouseIsDragging = false;
lastViewportOrigin = msi.ViewportOrigin;
lastMousePosition = e.GetPosition(msi);
};
this.MouseMove += delegate(object sender, MouseEventArgs e)
{
if (mouseIsDragging)
{
Point pos = e.GetPosition(msi);
Point origin = lastViewportOrigin;
origin.X += (lastMousePosition.X - pos.X) /
msi.ActualWidth * msi.ViewportWidth;
origin.Y += (lastMousePosition.Y - pos.Y) /
msi.ActualWidth * msi.ViewportWidth;
msi.ViewportOrigin = lastViewportOrigin = origin;
lastMousePosition = pos;
}
};
访问 Sub-images 和元数据
您可能已经注意到 Deep Zoom 编辑器从导出项目时, 您将提供您选择导出为一个组合或集合后一种选项附带了一个非常理想的优点是: 它要而不是导出包含所有的图像添加到一个单一的图像一起 lumped Deep Zoom 场景,导出包含单独的可寻址 sub-images 场景。通过 MultiScaleImage 控件的 SubImages 属性公开该 sub-images 和因为它们是单独的可寻址对象,在 sub-images 可以操作、 动画,fumigated Deep Zoom 的应用程序中添加闪耀和交互的 (只 kidding !)。
SubImages 集合中的每个项是 MultiScaleSubImage 的派生自 DependencyObject,包括属性 AspectRatio、 不透明度、 ZIndex、 ViewportOrigin 和 ViewportWidth 的实例。后一种两个组合来确定一个子-图像的大小和在 Deep Zoom 场景中的位置。应注意首次加载 MultiScaleImage 控件时, 其 SubImages 属性为空。控件将触发其 ImageOpenSucceeded 事件时,您第一个能够循环访问该 sub-images。
SubImages 属性的用途之一是为了显示元数据命中测试单个图像标题、 说明,等) 以响应单击或 mouseovers。另一个用于是以编程方式重新排列 Deep Zoom 场景中的图像。图 2 所示 DeepZoomTravelDemo 应用程序演示如何执行上述操作。鼠标放在一个场景中的图像时,部分透明信息面板右侧包含一个图像标题和说明。并重新当您在无序播放中单击按钮时左上角,图像排列本身以随机顺序。
图 2 DeepZoomTravelDemo
特色 DeepZoomTravelDemo 在九个图像是我在某些我海外行程对齐的照片。我 Deep Zoom 的编辑器中将它们导入、 网格,排列它们,并导出场景 (并确保选择"导出为集合")。然后,我从 Deep Zoom 的编辑器的输出导入到 Silverlight 项目并添加到前一节中的缩放和移动的逻辑类似于。若要将下载大小易于管理 (13 MB 170 MB 与删除的图像棱锥图底部两层之前我上载到 MSDN 代码库的应用程序生成的编辑器。下载的版本的工作只是效果很的好但当您缩放,图像收到图片比它们原始版本中许多 quicker。
DeepZoomTravelDemo 一样显示图像元数据的开发人员提供两个挑战。第一次,您是否在其中存储在的元数据并将如何执行您它与关联场景中的图像?第二如何执行您关联 MultiScaleImage 控件的 SubImages 集合中项的场景中的图像由于 MultiScaleSubImage 类提供了有关这两个没有信息?
第一个任务存储元数据) 通过选中图像后显示低右上角 Deep Zoom 的编辑器的标记框中输入一个文本字符串来完成的。我使用它存储每个图像的标题和说明,分隔加号。编辑器写入 Metadata.xml 文件导出项目时创建的标记。场景中每个图像表示 <Image> 元素中 Metadata.xml,并且 <Image> 的每个元素包含子元素名为 <Tag> 包含相应的标记中。图 3 显示了在中的图像在屏幕的左上角写入 Metadata.xml <image> 元素。编辑界面的编辑器的标记是某种程度上而言,因为标记框是太是小,但您可以始终手动编辑该 Metadata.xml 文件与我将标记每幅图像具有标题和说明。
图 3 中 Metadata.xml 的 <image> 元素
<Image>
<FileName>
C:\Users\Jeff\Documents\Expression\Deep Zoom Composer
Projects\DeepZoomTravelDemo\source images\great wall of china.jpg
</FileName>
<x>0</x>
<y>0</y>
<Width>0.316957210776545</Width>
<Height>0.313807531380753</Height>
<ZOrder>1</ZOrder>
<Tag>
Great Wall of China+The Great Wall of China near Badaling, about an hour
north of Beijing. This portion of the Great Wall has been restored and
offers outstanding views of the surrounding mountains.
</Tag>
</Image>
如果 MultiScaleSubImage 类已自动初始化的 <tag> 元素 ; 内容的标记属性会很好,但是不,因此必须 improvise。 第一次,您可以编写的下载 Metadata.xml 和分析从该标记的代码。 第二个,可以使用 <zorder> 元素中 Metadata.xml 使 <image> 元素相关联的 Zoom 场景中的图像。 如果场景包含九个图像 (并因此 MultiScaleImage 控件的 SubImages 集合包含的九个 MultiScaleSubImage 对象) 则 SubImages [0] 与的图像的 <ZOrder> 为 1,SubImages [1] 对应于其 <ZOrder> 是 2,图像。
DeepZoomTravelDemo 使用此相关存储图像的标题和说明。 在启动时,Page 构造函数使用 WebClient 对象初始化 Metadata.xml 服务器的 ClientBin 文件夹中的一个异步下载 (请参见 图 4 )。 完成下载后 WebClient_OpenReadCompleted 方法将分析与一个 XmlReader 下载的 XML,并初始化命名 _Metadata 数组 SubImageInfo 包含的对象的信息,包括标题和说明的场景中的图像字段。 类如下所示:
public class SubImageInfo
{
public string Caption { get; set; }
public string Description { get; set; }
public int Index { get; set; }
}
图 4 的下载 metadata.xml 和与 Sub-images 相关元数据
private SubImageInfo[] _Metadata;
...
public Page()
{
InitializeComponent();
// Register mousewheel event handler
HtmlPage.Window.AttachEvent("DOMMouseScroll ", OnMouseWheelTurned);
HtmlPage.Window.AttachEvent( "onmousewheel ", OnMouseWheelTurned);
HtmlPage.Document.AttachEvent( "onmousewheel ", OnMouseWheelTurned);
// Fetch Metadata.xml from the server
WebClient wc = new WebClient();
wc.OpenReadCompleted += new
OpenReadCompletedEventHandler(WebClient_OpenReadCompleted);
wc.OpenReadAsync(new Uri( "Metadata.xml ", UriKind.Relative));
}
private void WebClient_OpenReadCompleted(object sender,
OpenReadCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show( "Unable to load XML metadata ");
return;
}
// Create a collection of SubImageInfo objects from Metadata.xml
List<SubImageInfo> images = new List<SubImageInfo>();
try
{
XmlReader reader = XmlReader.Create(e.Result);
SubImageInfo info = null;
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element &&
reader.Name == "Image ")
info = new SubImageInfo();
else if (reader.NodeType == XmlNodeType.Element &&
reader.Name == "ZOrder ")
info.Index = reader.ReadElementContentAsInt();
else if (reader.NodeType == XmlNodeType.Element &&
reader.Name == "Tag ")
{
string[] substrings =
reader.ReadElementContentAsString().Split('+');
info.Caption = substrings[0];
if (substrings.Length > 1)
info.Description = substrings[1];
else
info.Description = String.Empty;
}
else if (reader.NodeType == XmlNodeType.EndElement &&
reader.Name == "Image ")
images.Add(info);
}
}
catch (XmlException)
{
MessageBox.Show( "Error parsing XML metadata ");
}
// Populate the _Metadata array with ordered data
_Metadata = new SubImageInfo[images.Count];
foreach (SubImageInfo image in images)
_Metadata[image.Index - 1] = image;
}
读取 Metadata.xml <zorder> 值用于 _Metadata 数组,确保 _Metadata 数组中的项的顺序相同 MultiScaleImage 的 SubImages 集合中项的顺序中的顺序 SubImageInfo 对象。 换而言 _Metadata [0] 包含标题和说明 SubImages [0],_Metadata [1] 标题和说明 SubImages [1],等等。 顺便说一下,我用 XmlReader 而不是 LINQ to XML 来避免增加通过引入 LINQ to XML (System.Xml.Linq.dll) 所需的额外程序集的 XAP 文件的大小。
现在 _Metadata 已初始化为 SubImageInfo 对象包含标题和说明、 下一步是编写代码以显示标题和说明。 发生这种情况在 图 5 中 。 MouseMove 处理程序 pans Deep Zoom 场景,如果向下左的鼠标按钮的行为是不同如果鼠标左的按钮设置的: 它命中的测试以确定是否光标当前是在一个在 sub-images 场景。 名为 GetSubImageIndex,返回-1,如果光标不是通过一个 sub-image 或一个基于 0 的图像的索引,如果它是帮助器方法执行命中测试。 该索引标识一个 sub-image MutliScaleImage.SubImages 中的和 _Metadata 中的一个 SubImageInfo 对象。 将几行代码复制标题和说明从 SubImageInfo 对象到 TextBlocks 一对和触发器代码的一个更多的行显示信息面板中,如果没有显示动画。 请注意 GetSubImageIndex 检查的点击该 sub-images 按相反的顺序由于 MultiScaleimage 控件的 SubImages 集合中,最终 sub-image 是最高的 Z 顺序中,在下一步的最后一个 sub-image 是第二个最高的 Z 顺序等等。
图 5 在 sub-images 命中测试
private int _LastIndex = -1;
...
private void MSI_MouseMove(object sender, MouseEventArgs e)
{
if (_Dragging)
{
// If the left mouse button is down, pan the Deep Zoom scene
...
}
else
{
// If the left mouse button isn't down, update the infobar
if (_Metadata != null)
{
int index = GetSubImageIndex(e.GetPosition(MSI));
if (index != _LastIndex)
{
_LastIndex = index;
if (index != -1)
{
Caption.Text = _Metadata[index].Caption;
Description.Text = _Metadata[index].Description;
FadeIn.Begin();
}
else
{
FadeOut.Begin();
}
}
}
}
}
private int GetSubImageIndex(Point point)
{
// Hit-test each sub-image in the MultiScaleImage control to determine
// whether "point " lies within a sub-image
for (int i = MSI.SubImages.Count - 1; i >= 0; i--)
{
MultiScaleSubImage image = MSI.SubImages[i];
double width = MSI.ActualWidth /
(MSI.ViewportWidth * image.ViewportWidth);
double height = MSI.ActualWidth /
(MSI.ViewportWidth * image.ViewportWidth * image.AspectRatio);
Point pos = MSI.LogicalToElementPoint(new Point(
-image.ViewportOrigin.X / image.ViewportWidth,
-image.ViewportOrigin.Y / image.ViewportWidth)
);
Rect rect = new Rect(pos.X, pos.Y, width, height);
if (rect.Contains(point))
{
// Return the image index
return i;
}
}
// No corresponding sub-image
return -1;
}
除了 mouseovers 的支持 DeepZoomTravelDemo 可以重新排列场景中的图像。 如果您尚未尝试单击屏幕的左上角中的无序播放按钮。 (实际上,单击几次,图像将每个时间假定不同的顺序。 图 6 ,创建数组,包含所有图像的 ViewportOrigins、 重新排序该数组,使用一个随机数字的生成器,然后创建一个 Storyboard 和一系列 PointAnimations 移至位置重新排序数组中包含的在 sub-images 中无序播放方法执行,重新排列。 此处关键是 MultiScaleimage 控件的 SubImages 属性公开该 sub-images 到您的代码,您可以修改要更改其位置场景中的一个子-图像的 ViewportOrigin 属性。
图 6 混排在 sub-images
private void Shuffle()
{
// Create a randomly ordered list of sub-image viewport origins
List<Point> origins = new List<Point>();
foreach (MultiScaleSubImage image in MSI.SubImages)
origins.Add(image.ViewportOrigin);
Random rand = new Random();
int count = origins.Count;
for (int i = 0; i < count; i++)
{
Point origin = origins[i];
origins.RemoveAt(i);
origins.Insert(rand.Next(count), origin);
}
// Create a Storyboard and animations for shuffling
Storyboard sb = new Storyboard();
for (int i = 0; i < count; i++)
{
PointAnimation animation = new PointAnimation();
animation.Duration = TimeSpan.FromMilliseconds(250);
animation.To = origins[i];
Storyboard.SetTarget(animation, MSI.SubImages[i]);
Storyboard.SetTargetProperty(animation,
new PropertyPath( "ViewportOrigin "));
sb.Children.Add(animation);
}
// Run the animations
sb.Begin();
}
为我研究本专栏,我发现提供有用的信息的多个网络日志项。一个是 Jaime Rodriquez"使用集合的深度缩放." 另一个是"深度缩放编辑器 — — 通过标记示例的筛选",Expression Blend 团队提供一种筛选 Deep Zoom 的图像的技术基于图像标记的成员的写入。实现 mouseovers,重新排列是的场景中的图像和筛选基于数据的标记,但地址中一个 Zoom 场景和关联元数据与它们在单个 sub-images 能够通过该功能的几个图像。
进行更好的动态深度缩放
我已 fascinated 由分形 ever 由于某些 20 年前发现它们。如果内存正确提供我编写我的第一个 Mandelbrot 查看器中,最早 20 世纪 90 年代。我书架 oldie-但-goodie 计算机书籍的仍然包含简介册标题不规则图像压缩为 Barnsley 和中等 90s 中的数据压缩信息检索项目中使用 Hurd pristine 的副本。并且之一的所有时间我最喜爱的图书 James Gleick (Penguin,2008年) 的混乱。
构建一个交互式和引人入胜 Mandelbrot 浏览器查看器正是了希望在 Silverlight 布局眼睛,我第一天后执行。动态 Deep Zoom 将使它成为可能。不利方面,当然,是图像是在服务器上生成,导致不需要的滞后时间和还增加在服务器上的负载该客户端下载。
Silverlight 2 不包含用于生成客户端,位图的 API,但您可以生成仍要使用Joe Stegman Silverlight PNG 编码器. Minh Nguyen 用于它生成 Mandelbrot 资源管理器,您可以阅读关于他的博客文章中的"Minh T。在 Silverlight 2.0 中使用源代码 Nguyen 的 Mandelbrot 资源管理器 1.0." 您阅读此,时将是在测试中的 Silverlight 3 位图 API,但仍保留问题 Deep Zoom 想要从服务器中提取图像。不清楚目前是否下的忕殑鐗 Deep Zoom 鍏锋将湁一个客户端文章但如果,可以当然我将被修改 MandelbrotDemo Silverlight 3 完全在客户端上工作的。
动态深度缩放: 在运行时提供图像像素为单位)
Deep Zoom 编辑器的导出功能生成 MultiScaleImage 控件所需的所有数据。这些数据包含引用其他又引用单个图像场景中的 XML 文件的 XML 文件 (dzc_output.xml)。编辑器的输出还包括数百个 (有时千位) 的生成从这些图像的图像平铺。拼贴窗体的图像棱锥图与每个包含原来的图像的平铺的版本棱锥图级别和每个级别代表一个不同的分辨率。渚嬪的方式 顶部的棱锥图,级别可能包含单个平铺的图像的一个 256 x 256 再现。下的一级别,则包含四个 256 x 256 的平铺的整理,窗体的图像 512 x 512 版本。下的一级别都包含十六个 256 x 256 表示为 1、 024 024 x 1 的不同部分的平铺图像,等等。深度缩放编辑器根据需要以描述在其本机分辨率将原始图像生成为很多级别。要获取正确的分辨率的图像平铺用户缩放和平移一个详细的缩放场景中,MultiScaleImage 控件是不断激发 HTTP 请求到服务器。它还实现一些 slick 混合平滑从一个级别转换到另一工作。
是您可能没有意识到有关 MultiScaleImage 控件是它不需要 Deep Zoom 的编辑器。编辑器是实际上只是一个工具快速而轻松地创建 Deep Zoom 的项目合并场景静态图像从生成的。作为的替代方法 MultiScaleImage 提供静态内容可以生成在运行时从 MultiScaleImage 响应请求的内容,并下载到客户端的内容。
为什么要将您需要在运行时生成 Deep Zoom 的内容?开发人员询问如何执行此操作在所有时间。 "是否可以动态地提供 Deep Zoom 的图像数据吗?" 原因是它使一个全新流派 Deep Zoom 的应用程序提取图像平铺与数据库的生成图像平铺动态。
需要一个示例吗?签出,深地球项目和在工作的详细的地球示例deepearth.soulsolutions.com.au/ . 深地球被称为映射控件由 Microsoft 的 Silverlight 2 平台的组合供电和 DeepZoom (MultiScaleImage) 控件。换而言,它是一个控件可以拖放到 Silverlight 应用程序公开大量可从 Microsoft 虚拟地球通过一个 Zoom 前端的地理数据。outer 空间中,一直到您的前端草坪缩放。在缩放非常平滑,这要感谢通过 MultiScaleImage 和 Deep Zoom 运行时在后台完成工作。
深地球不通过 XML 文件和图像平铺输出 Deep Zoom 的编辑器的一组驱动 ; 它动态,提供图像平铺到 MultiScaleImage 控件和它从虚拟地球提取图像平铺。详细的地球的用户这称为"动态 Deep Zoom。
图 7 所示,应用程序演示了动态 Deep Zoom 基本的操作。MandelbrotDemo Mandelbrot 集中提供一个 Zoom 窗口 — — 可能是世界上的最著名不规则。这意味着,您可以放大永远级别会永远不会降低 Mandelbrot 集是明细的无限复杂。Mandelbrot 查看器正在常见该软件世界但少与使用 Zoom 为简洁。; 运行 MandelbrotDemo 试试放大旋涡区域设置 (在黑色和亮色之间边界) 在 Mandelbrot 边缘的一部分。您不能放大永远因为即使是动态的 Zoom 场景具有有限的宽度和高度,但场景的维度可以非常,非常大 (最多 232 每像素端数)。
两个视图的 Mandlebrot 设置
实现动态 Deep Zoom 中的第一步是从 Silverlight 的 MultiScaleTileSource 类的 System.Windows.dll,System.Windows.Media 命名空间中找到派生和重写 GetTileLayers 方法。每次 MultiScaleImage 控件需要一个平铺,时,它调用 GetTileLayers。您的工作是创建一个图像平铺,并回到 MultiScaleImage 控件添加到在 IList 传递 GetTileLayers 的参数列表中。输入 GetTileLayers 其他参数指定的缩放级别 (按其原义,从中正在请求平铺图像棱锥图的级别) 和 X 和 Y 棱锥图的该级别中的位置所请求的拼贴。只是因为 X、 Y,和 Z 值足以确定三维坐标空间、 一个 X 值、 一个 Y 值和一个级别中的一个点,唯一地标识一个 Zoom 图像棱锥图中的图像平铺。
图 8 显示了 MultiScaleTileSource 派生类中 MandelbrotDemo 特色。GetTileLayers 重写不会稍有多个提交到服务器图像平铺的 HTTP 请求。请求的终结点是名为 MandelbrotImageGenerator.ashx HTTP 处理程序。让我们介绍该处理程序之前,但是,看看如何 MandelbrotTileSource 绑定到 MultiScaleImage 控件。
图 8 MultiScaleTileSource 阶导数
public class MandelbrotTileSource : MultiScaleTileSource
{
private int _width; // Tile width
private int _height; // Tile height
public MandelbrotTileSource(int imageWidth, int imageHeight,
int tileWidth, int tileHeight) :
base(imageWidth, imageHeight, tileWidth, tileHeight, 0)
{
_width = tileWidth;
_height = tileHeight;
}
protected override void GetTileLayers(int level, int posx, int posy,
IList<object> sources)
{
string source = string.Format(
"http://localhost:50216/MandelbrotImageGenerator.ashx? " +
"level={0}&x={1}&y={2}&width={3}&height={4} ",
level, posx, posy, _width, _height);
sources.Add(new Uri(source, UriKind.Absolute));
}
}
图 9 显示了从 MandelbrotDemo 的 Page.xaml.cs 文件摘录 — — 特别,XAML 代码隐藏类的构造函数。 该密钥的语句是,创建一个 MandelbrotTileSource 对象赋予 MultiScaleImage 控件的来源属性对它的引用。 对于静态 Deep Zoom,您设置的 dzc_output.xml 的 URI 源。 对于动态 Deep Zoom,您指向它 MultiScaleTileSource 对象而是。 MandelbrotTileSource 对象创建指定的图像上提供测量每一侧 230 像素被划分为 128 x 128 像素平铺。
图 9 注册一个深度缩放平铺源
public Page()
{
InitializeComponent();
// Point MultiScaleImage control to dynamic tile source
MSI.Source = new MandelbrotTileSource((int)Math.Pow(2, 30),
(int)Math.Pow(2, 30), 128, 128);
// Register mousewheel event handler
HtmlPage.Window.AttachEvent( "DOMMouseScroll ", OnMouseWheelTurned);
HtmlPage.Window.AttachEvent( "onmousewheel ", OnMouseWheelTurned);
HtmlPage.Document.AttachEvent( "onmousewheel ", OnMouseWheelTurned);
}
生成该图像平铺的工作由执行 MandelbrotImageGenerator.ashx 回服务器上 (请参见 图 10 )。 从查询字符串检索输入的参数后, 创建位图说明请求的平铺,并写入 HTTP 响应的图像位。 DrawMandelbrotTile 会生成像素。 将调用时, 它级别 Y X 的值转换标识该图像平铺的请求到复杂的平面中的坐标 (数学飞机的实数 graphed 沿 X 轴和虚数字 — — 合并-1 的平方根的数字 — — graphed 沿 Y 轴)。 然后它将遍历所有数据点在复杂的平面中对应于中检查以确定它是否属于 Mandelbrot,设置和分配相应的像素颜色表示它的关系,Mandelbrot 每个点设置 (详细介绍在一段时间) 在图像平铺的像素。
图 10 HTTP 处理程序用于生成深度缩放图像平铺
public class MandelbrotImageGenerator : IHttpHandler
{
private const int _max = 128; // Maximum number of iterations
private const double _escape = 4; // Escape value squared
public void ProcessRequest(HttpContext context)
{
// Grab input parameters
int level = Int32.Parse(context.Request[ "level "]);
int x = Int32.Parse(context.Request[ "x "]);
int y = Int32.Parse(context.Request[ "y "]);
int width = Int32.Parse(context.Request[ "width "]);
int height = Int32.Parse(context.Request[ "height "]);
// Generate the bitmap
Bitmap bitmap = DrawMandelbrotTile(level, x, y, width, height);
// Set the response's content type to image/jpeg
context.Response.ContentType = "image/jpeg ";
// Write the image to the HTTP response
bitmap.Save(context.Response.OutputStream, ImageFormat.Jpeg);
// Clean up and return
bitmap.Dispose ();
}
public bool IsReusable
{
get { return true; }
}
private Bitmap DrawMandelbrotTile(int level, int posx, int posy,
int width, int height)
{
// Create a bitmap to represent the requested tile
Bitmap tile = new Bitmap(width, height);
// Compute the number of tiles in each direction at this level
int cx = Math.Max(1, (int)Math.Pow(2, level) / width);
int cy = Math.Max(1, (int)Math.Pow(2, level) / height);
// Compute starting values for real and imaginary components
// (from -2.0 - 1.5i to 1.0 + 1.5i)
double r0 = -2.0 + (3.0 * posx / cx);
double i0 = -1.5 + (3.0 * posy / cy);
// Compute increments for real and imaginary components
double dr = (3.0 / cx) / (width - 1);
double di = (3.0 / cy) / (height - 1);
// Iterate by row and column checking each pixel for
// inclusion in the Mandelbrot set
for (int x = 0; x < width; x++)
{
double cr = r0 + (x * dr);
for (int y = 0; y < height; y++)
{
double ci = i0 + (y * di);
double zr = cr;
double zi = ci;
int count = 0;
while (count < _max)
{
double zr2 = zr * zr;
double zi2 = zi * zi;
if (zr2 + zi2 > _escape)
{
tile.SetPixel(x, y,
ColorMapper.GetColor(count, _max));
break;
}
zi = ci + (2.0 * zr * zi);
zr = cr + zr2 - zi2;
count++;
}
if (count == _max)
tile.SetPixel(x, y, Color.Black);
}
}
// Return the bitmap
return tile;
}
}
sadly,几乎没有 Silverlight 的 MultiScaleTileSource 类上的文档。 列出您认为我的确定所有天才 (任何人都知道我将证明我不) 此出,给贷方贷方位置是因为我。 为我 wrestled 输入的参数,以及如何将 Deep Zoom X Y 级别的值映射到复杂的平面的含义与,我发现由 Mike Ormond,一个极好的日志 Deep Zoom MultiScaleTileSource 和该 mandelbrot 设置. 他后提供密钥深入动态 Deep Zoom,并且还引用另一个日志 mandelbrot 集描述计算 Mandelbrot 集的有效方法。 我的工作是可能减半由其他人有之前我完成的工作。
在我的实现上的一个最终注意: 呈现 Mandelbrot 集实际上是每个应用程序使用不同的配色方案。 选择了指定表示属于该 Mandelbrot 坐标的像素的方案设置黑色,表示该 Mandelbrot 之外的坐标的像素设置 RGB 颜色。 在进一步一个坐标位于中 Mandelbrot 集,"冷却器"bluer 颜色 ; 它在更靠近位于对 Mandelbrot 集,"hotter"颜色。 距离 Mandelbrot 集取决于如何快速将点转义为无穷大。 在代码中此处,这是迭代的花费 DrawMandelbrotTile 的 while 循环,以确定该点不是 Mandelbrot 集的一部分。 更少,迭代,进一步点位于从构成 Mandelbrot 一组点的集合。 我分解到单独的类名为 ( 图 11 ) ColorMapper 迭代次数中生成一个 RGB 颜色值的代码。 如果要试验不同的配色方案,只修改 GetColor 方法。 您可以通过执行下列看到灰色刻度呈现的结果:
int val = (count * 255) / max;
return Color.FromArgb(val, val, val);
图 11 ColorMapper 类
public class ColorMapper
{
public static Color GetColor(int count, int max)
{
int h = max >> 1; // Divide max by 2
int q = max >> 2; // Divide max by 4
int r = (count * 255) / max;
int g = ((count % h) * 255) / h;
int b = ((count % q) * 255) / q;
return Color.FromArgb(r, g, b);
}
}
DeepZoomTools.dll
有关 Deep Zoom,您可能会发现有用的信息的一个最终 tidbit 包括名为 DeepZoomTools.dll 程序集。 深缩放编辑器使用此程序集从所生成的后台生成平铺的图像和元数据。 在理论上,您可以使用它生成您自己的组合工具。 我说"在理论上",因为宝贵稍有签出的文档。 了解有关 DeepZoomTools.dll 上, 表达式混合和设计网络日志。 如果对于 Deep Zoom 提出一些唯一的、 具有创造性的使用,但不完全确定如何使它在您需要将其将我拍摄的电子邮件。
将您的问题和提出发送到 Jeff 的 wicked@microsoft.com.
Jeff Prosise 是一个特约 MSDN Magazine 包括 Programming Microsoft .NET (微软出版社,2002年) 的多个的图书的作者。 他也是 cofounder Wintellect (的 www.wintellect.com),软件咨询和培训固定的专门在 Microsoft.NET 中。 对此列中有注释吗? 与在 Jeff wicked@microsoft.com.