ArcGIS engine中Display类库——Display(二)

来源:互联网 发布:电脑发送短信软件 编辑:程序博客网 时间:2024/06/11 04:07

透明­——Transparency

符号和图像都能使用透明的颜色。透明(α混合)的算法是基于栅格的。为了支持透明,矢量的图形必须首先转化为栅格。透明的对象被绘制到源位图中。那些绘制在背景上的对象必须以背景位图的形式存储。对所有的比特位使用单一的透明,或者对每单个像素使用包含了透明值的mask位图,通过这种方式把源位图混合掺入到背景位图中来实现透明。为了支持透明,IDisplay提供BackgroundDC属性来获取位图,该位图包含了发生在当前绘制过程中所有的图形。

显示透明——Display Transparency

透明算法封装在显示过滤对象TransparencyDisplayFilter中。相同的过滤器类能被要素图层,栅格图层,元素,第三方程序等使用。

 通过下面的方式来绘制透明:

[C#]

    ITransparencyDisplayFilter pFilter = new TransparencyDisplayFilterClass();

 

    pFilter.Transparency = 100;

    pDisplay.StartDrawing(hdc, cacheID);

    pDisplay.Filter = pFilter;

 

    DrawToDisplay();

 

    pDisplay.Filter = 0;

    pDisplay.FinishDrawing();

 

对于栅格图层,DrawToDisplay()意味著从显示(display)中获取目标DC,然后BitBlt栅格图像给它。

    当指定了过滤器时,显示(display)会生成一个内部的过滤器缓存,这个缓存随着记录缓存为过滤器提供必要的栅格信息。Output通向过滤缓存,这样当栅格图层请求目标DC时,它能得到过滤缓存DC。当pDisplay.Filter设置为0时,显示(display)应用过滤器。应用接受当前的背景位图(记录缓存),不透明的栅格图层影像(过滤缓存)和目标(窗口)。过滤器知道透明度的值是100,这样它混合(do the blending)并且发送结果到窗口。

符号透明——Symbol Transparency

我们如何使符号和图像支持透明呢?视图对象(数据和布局)处理绘图地图(drawing maps)到输出设备(窗口和打印机)和输出文件(位图和元文件)。所有的图形元素和图层都需要通报是否它们使用了透明颜色。这样,当视图开始生成输出是,它能检查是否存在透明的颜色。如果存在,它使用下面的算法来产生输出:

  1. 划分显示(display)表面成不同的波段,这样使要求生成透明颜色的内存中的位图不会消耗更多的内存。
  2. 生成一个波段大小的BackingDisplayBackingDisplay有一个独立位图的内部设备,它具有和输出设备相同的颜色深度和分辨率。直接绘制到位图上,在绘制完成之后,位图被复制到实际的输出设备(或文件)上。图层和符号使用BackingDisplay就像任何其它显示(display)一样,不需要特殊的编码。
  3. 对于每个波段:根据当前的波段来调节显示(display)的转换,绘制视图。因为转换的可见范围(bounds)等于波段的矩形,所以渲染将自动地剪切波段。

这将导致一系列的位图(波段)被复制到输出上或导出文件中。如果在地图中不存在透明的颜色,元文件将会自动生成,例如,它将包含一系列的矢量图形。

快速显示——Rapid Display

经常,有必要快速更新显示(Display)来显示活动对象的运动情况。例如,具有GPS跟踪的商业活动。存在两个方面的问题:

  1. 如何存储活动数据
  2. 如何快速地绘图到显示(Display

设计模式——Design Patterns

在我们的应用程序中有多种方式来存储和绘制时间数据。大部分通用的方法如下:

l         必要地生成要素类,添加和删除要素。使用标准的渲染或自定义符号来绘制要素。在其它图层的上面或下面绘图。为了更好的性能,捆绑具有自己显示缓存的要素图层(例如,设置ILayer::Cachedtrue)。

l         生成自定义图层。使用属性数据或要素类来存储要素。对图层完成自定义渲染(例如,直接使用GDI+)。在其它图层的上面或下面绘图。为了更好的性能,对每个要素图层指定自己的显示缓存。

l         生成图形图层。以图形元素的方式来存储数据。使用标准的或自定义符号来渲染。在所有其它图层上面绘制他。

l         响应IActiveViewEvents::AfterDraw(esriViewForeground)来绘图。基于私有数据来存储数据。直接使用GDI or IDisplay来绘图。在其他图层的顶板来绘图。快!GPS扩展来使用这种方法。

 有三种通用的方式来存储自定义数据:

1.         GeoDatabase里的要素

2.         图形图层里的元素

3.         私有的数据结构

哪一种方式是最好的选择取决于:在绘制顺序中绘制的位置,是否使用标准的渲染对象,是否需要支持私有的数据格式。

在所有的情况下,都应该使用标准的无效绘制模型。这就是生成绘图对象(如,图层,图形元素,时间句柄),把它插入到地图中,当想要绘制的时候调用IActiveView::PartialRefresh

动画支持——Animation Support

ArcObjects 9的版本中,一个新的接口IviewRefresh,大大地简化了快速地刷新显示(display)来显示活动对象。客户应该使用AnimationRefresh例行程序取代PartialRefresh来使他们的自定义绘图对象无效。例如,我们可以使用一个带有自己显示缓存的图层来存储“活动”要素。动画(Animation)是通过移动要素,无效化图层(利用AnimationRefresh)和自然地重绘来实现的。当用AnimationRefresh代替PartialRefresh时,下面的optimizations / tradeoffs启用了:

文本反锯齿(Text Anti-aliasing)临时无效了。这是为了防止当动画图层无效时标注必须每次都重绘。记住,反锯齿文本利用背景作为渲染的部分,当其下面有任何改变时,文本也需要从头开始绘制。

动画图层上面的透明图层不会随着动画图层而自动无效。 这加快了重绘的速度但有其局限性:位于动画图层里的要素将不再通过其上的透明图层显示出来。

所有的图形直接绘到记录缓存HDC而不是窗口。这就使得所有的绘制在幕后发生。当绘制完成时,窗口只需要从也完成的记录缓存中更新一次。这就很少有闪烁。

为了避免在快速绘制过程中过多地占用CPU的资源,我们可以在旧的无效位置和新的无效位置调用一次UpdateWindow

显示设计模式——Display Design Patterns

为了帮助理解各种显示对象如何一起工作,解决一般的开发需求,下面将给出了几个应用场景的详细实现。显示对象开始使用这些模式工作。

应用程序窗口——The application window

在应用程序窗口的客户区域绘制地图是大部分普通任务之一,这些窗口支持滚动和后备存储。显示对象通过如下方式来实现这些任务。

初始化——Initialization

当窗口创建时先生成ScreenDisplay。我们也需要生成一个或多个符号来绘制图形。转发应用程序的hWndpScreenDisplay.hWnd。从ScreenDisplay中获取其IDisplayTransformation接口,使用pTransformation.BoundspDisplayTransform.VisibleBounds设置地图的全部范围和可见范围。可见范围决定了当前缩放的等级。ScreenDisplay负责更新显示转换的DeviceFrameScreenDisplay监测窗口消息,自动地处理通用的事件,如窗口大小的改变或滚动。

[C#]

    private IScreenDisplay m_pScreenDisplay;

    private ISimpleFillSymbol m_pFillSymbol;

 

   private void Form_Load(object sender, System.EventArgs e)

   {

      m_pScreenDisplay = new ScreenDisplayClass();

      m_pScreenDisplay.hWnd = Picture1.hWnd;

      m_pFillSymbol = new SimpleFillSymbolClass();

      IEnvelope pEnv = new EnvelopeClass();

      pEnv.PutCoords(0, 0, 50, 50);

      m_pScreenDisplay.DisplayTransformation.bounds = pEnv;

      m_pScreenDisplay.DisplayTransformation.VisibleBounds = pEnv;

   }

绘制——Drawing

显示对象(the display object)定义了一个通用的接口IDraw,这个接口使得我们绘制图形到任何显示(display)上变得容易。只要我们使用IDraw IDisplay来实现绘制代码,就不必担心所绘制到的设备类型。绘制的过程以StartDrawing开始到FinishDrawing结束。例如,编写这样一个程序,在屏幕中央建立一个多边形,然后绘制出来。这个图形使用默认的符号来绘制。例子如下:

 

[C#]

    private IPolygon GetPolygon()

    {

      IPolygon GetPolygon;

      GetPolygon = new PolygonClass();

      IPointCollection pPointCollection;

      pPointCollection = GetPolygon as IPointCollection;

      IPoint pPoint = new PointClass();

      pPoint.PutCoords(20, 20);

      pPointCollection.AddPoints(1, ref pPoint);

      pPoint.PutCoords(30, 20);

      pPointCollection.AddPoints(1, ref pPoint);

      pPoint.PutCoords(30, 30);

      pPointCollection.AddPoints(1, ref pPoint);

      pPoint.PutCoords(20, 30);

      pPointCollection.AddPoints(1, ref pPoint);

      GetPolygon.Close();

      return GetPolygon;

    }

 

    private void MyDraw(IDisplay pDisplay, esriSystem.OLE_HANDLE hDC)

    {

      // Draw from Scratch

      IDraw pDraw;

      pDraw = pDisplay;

      pDraw.StartDrawing(hDC, esriNoScreenCache);

      IPolygon pPoly;

      pPoly = GetPolygon();

      pDraw.SetSymbol(m_pFillSymbol);

      pDraw.Draw(pPoly);

      pDraw.FinishDrawing();

    }

这个程序能用来绘制多边形到任何设备环境中。然而,我们需要绘制的第一个地方是窗口。为了处理这种情况,需要在Picture BoxPaint方法里写些代码,这个方法传递应用程序的ScreenDisplay指针和Picture Box HDCMyDraw程序(routine)中。注意这个程序接受显示指针(a display pointer)和窗口设备环境。

[C#]

    private void Picture1_Paint()

    {

       MyDraw(m_pScreenDisplay, Picture1.hDC);

    }

转发DC让显示(display)来授权这个剪切范围,Windows把它设置到paintHDC中。(Forwarding the DC allows the display to honor the clipping regions that Windows sets into the paint HDC.

添加显示缓存——Adding Display Caching

许多绘制过程需要花费一定时间来完成。优化我们应用程序的一个简单的办法就是开启显示缓存。这就涉及到ScreenDisplay's的能力,它可以把绘图过程记录到一个位图中,之后当Paint方法调用的时候,使用这个位图来刷新这个图片窗口。直到我们的数据变化了和我们调用IScreenDisplay::Invalidate来使缓存无效时,缓存才被使用。有两种类型的缓存:记录缓存和用户分配的缓存。在这个例子中,在应用程序的Paint方法使用记录缓存来实现显示缓存。

[C#]

    private void Picture1_Paint()

    {

        if ((m_pScreenDisplay.IsCacheDirty(esriScreenRecording)))

        {

          m_pScreenDisplay.StartRecording();

 

          MyDraw(m_pScreenDisplay, Picture1.hDC);

 

          m_pScreenDisplay.StopRecording();

        }

        else

        {

          tagRECT rect;

          m_pScreenDisplay.DrawCache(Picture1.hDC, esriScreenRecording, rect, rect);

        }

    }

当你执行这段代码是,将发现屏幕上没什么绘制出来。这是因为ScreenRecording缓存没有设置dirty标识。为了确保当第一个绘图消息传来时MyDraw函数被调用,我们必须是缓存无效。在方法的结尾处添加下面一行代码:

 [C#]

    m_pScreenDisplay.Invalidate(null, true, esriScreenRecording);

许多应用程序,例如ArcMap,可能需要多个显示缓存。为了利用多缓存,照下面的步骤来做:

1.         使用IScreenDisplay::AddCache来添加新的缓存。保存它返回的缓存ID

2.         为了绘制缓存,给StartDrawing指定缓存ID

3.         为了使缓存无效,给Invalidate指定缓存ID

4.         为了从缓存里绘图,给DrawCache指定缓存ID

 为了使这个例子程序支持它自己的缓存,作如下改变:

  • 添加一个成员变量来保存新的缓存。

 

[C#]

private long m_lCacheID;

  • Form_Load方法里生成这个缓存。

 

[C#]

    m_lCacheID = m_pScreenDisplay.AddCache;

  • 使用变量来改变恰当的调用,从Paint方法里清除开始和停止的记录。

漫游,缩放和旋转——Pan, zoom, and rotate

显示对象(The display object)一个强大的功能(feature)是对我们的图形进行放大和缩小。很容易实现这个工具让用户缩放或漫游。滚动能自动地操作。为了对我们的图形进行缩放,只需简单地设置显示(display)的可见范围。例如,添加一个命令按钮到form上,在这个按钮的事件里通过合适的数量来缩放屏幕,代码如下:

[C#]

    private void Command1_Click()

    {

      IEnvelope pEnv = m_pScreenDisplay.DisplayTransformation.VisibleBounds;

      pEnv.Expand(0.75, 0.75, true);

      m_pScreenDisplay.DisplayTransformation.VisibleBounds = pEnv;

 

      m_pScreenDisplay.Invalidate(null, true, esriAllScreenCaches);

    }

ScreenDisplay实现了TrackPan,它在响应mouse down的事件里被调用,让用户在显示(display)上漫游。通过设置DisplayTransformation旋转属性为非零值,我们可以以屏幕为中心来旋转整个图形。旋转以度数的形式被指定。ScreenDisplay实现了TrackRotate,它在响应mouse down的事件里被调用,让用户在显示(display)上交互旋转。

打印——Printing

打印与绘图到屏幕上非常相似。因为绘图到打印机上时不必担心缓存或滚动,所以可以使用SimpleDisplay。生成一个SimpleDisplay对象,通过拷贝ScreenDisplay的转换来初始化它的转换。设置打印机转换的DeviceFrame到打印机页面的像素范围。最后,利用SimpleDisplay和打印机的HDC从头开始绘制。

输出到元文件——Output to a metafile

GDIDisplay对象用来代表一个元文件。生成一个元文件和生成一个打印机没有什么不同。如果我们指定CreateEnhMetaFilelpBounds参数0MyDraw程序可以被使用。只是hPrinterDC把替换为hMetafileDC。如果你想为CreateEnhMetafFile(以HIMETRIC为单位)指定一个范围,设置DisplayTransformationDeviceFrame为相同矩形的像素版本。

Print to a frame

许多工程可能要求直接打印输出到输出设备的某个子矩形中。这容易处理,通过设置DisplayTransformation的设备框架给一个像素范围,这个范围要比整个设备的范围要小。

过滤器——Filters

使用显示过滤器(display filters),非常先进的绘图效果,如颜色透明,都能够被实现。过滤器(Filter)同显示缓存一起工作,可以操纵栅格版本的图形。当为显示(使用IDisplay::putref_DisplayFilte)指定了一个过滤器时,显示生成了一个内部的过滤缓存,这个过滤缓存同记录缓存一起为过滤器提供了栅格信息。输出都会经过过滤缓存直到这个过滤器被清除(这就是putref_DisplayFilter(0))。在这时,display调用IDisplayFilter::ApplyApply接受当前的背景位图(记录缓存),绘制缓存(包括在绘制中发生的所有的,因为过滤器被指定了),和目标HDC。透明过滤器对这些位图进行透明混合处理,然后把它们绘制到目标HDC来达到颜色的透明。能生产新的过滤器能来实现其他的效果。