A Basic Image Viewer

题记:最近一直想做一个基于swt的图像处理桌面工具条。在网上收集了一些资料,偶然的发现这篇e文,感觉这张帖子文显义深,并且内容比较丰富。网上的东西总是凌乱的,于是很想把他翻译整理一下,算是对自己约束也是学习吧。

原文链接:http://www.eclipse.org/articles/Article-Image-Viewer/Image_viewer.html#Introduction

demo:  http://www.eclipse.org/articles/Article-Image-Viewer/imageviewer.zip

译者注:安装方法的翻译被我省去就是一般插件的安装方法

Translated by Aivin  ,Written by Chengdong Li

摘要:

     这篇文章讲述了如何通过java2d的转换来扩展swt画布来完成一个迷你的图像查看器插件。它的功能可用于旋转和放大图片,并且也可以衍生到其他变换中 。该软件完全基于swt且不含awt内容。该插件已经在Windows, Linux GTK, Mac OS X Carbon平台用Eclipse2.1或以上版本测试过。

    目录

·          介绍

·          类图描述

·          画布实现

o   导入图片

o   扩展 org.eclipse.swt.widgets.Canvas

o   绘制图片

o   变换

o   滚动条同步

·          旋转

·          插件实现

o   建立插件视图

o   添加延伸视图

·          归纳

·          鸣谢

·          参考

约定&术语

在该文档中下列印刷的约定:

  斜体:用于对文章的链接

  courier new字体:用于代码和变量

下面的是在文档中的约定:

(因为下面已经译出)

 

介绍

 

如果是是SWT的初学者请先读完这篇 Taking a look at SWT Images. 图一是工具的效果图:

 

                             Figure 1 - Image viewer

 

     该软件并不同于从Eclipse example中得到的Image Analyzer软件。该软件用于滚动和变焦图像的转换。

     该软件的有点:

1.   它提供了无限尺寸的变焦

2.   它对于大的图片效果很好

     在下一个环节中,我们将首先总体看一下包的结构和类之间的关系,然后我们将讨论图像画布是怎样工作和怎样实现能够滚屏的和变焦的图像画布——SWTImageCanvas

 

类图描述    

下面的图2显示在源代码中所有的类结构。 SWTImageCanvasorg.eclipse.swt..widgets.Canvas的子类;它实现了图像的加载,绘制,滚动和变焦。ImageView类是org.eclipse.ui.part.ViewPart的子类;它有一个SWTImageCanvas实例。辅助类SWT2Dutil实现了有用的功能。PushActionDelegate继承org.eclipse.ui.plugin.AbstractUIPlugin(这个类是PDE-generated)。

 

    Figure 2 - Class diagram

 

 plugin.xml文件定义了运行时的需求和对Eclipse的影响(视图和视图操作的扩充)。Eclipse将生成工具条能工作的工具条按钮。

 

画布实现

      SWTImageCanvas类控制了图片的加载,绘制,滚动和变焦。它在内存中保存了原始图片的镜像,然后用java.awt.geom.AffineTransform翻译并且按标度图像。绘制和转换仅仅被用于图像在屏幕上能被看见(视觉效果最佳)的部分。AffineTransform类用于实现滚动条。

    

l          加载图片

首先让我们看看如何把一张图片加载到内存中。下面提供了几种方法。

1.   从本地的文件系统中加载图片

2.   从工作区间加载图片

3.   从网页上加载图片

      在这个简单的软件中,我们仅仅从本地的文件系统加载图片。为改进一下,你将引入org.eclipse.ui.popupMenus包。这样无论一个图片文件是否被选中,菜单项依然可用。用户可以选择从工作区间加载图片(你需要添加图片名字并且你可能还需要使用空间的程序接口(API)。为了搞清楚如何从URL上加载图片,请参考SWT 自带的例子Image Analyzer

      图像的加载过程如图3所示:

 

            Figure 3 - Image-loading diagram

 

SWT提供了ImageLoader来加载图片到内存。你可以通过构造器Image(Display,String)来完成图片的加载。为了使图片的加载我们靠SWT ImageLoader支持 提供对话框来定位所有的图片。

public void onFileOpen(){
        FileDialog fileChooser = new FileDialog(getShell(), SWT.OPEN);
        fileChooser.setText("Open image file");
         fileChooser.setFilterPath(currentDir);
        fileChooser.setFilterExtensions(
            new String[] { "*.gif; *.jpg; *.png; *.ico; *.bmp" });
        fileChooser.setFilterNames{
            new String[] { "SWT image" + " (gif, jpeg, png, ico, bmp)" });
        String filename = fileChooser.open();
        if (filename != null)
      loadImage(filename);
         currentDir = fileChooser.getFilterPath();
        }
}
     
     
public Image loadImage(String filename) { 
        if(sourceImage!=null && !sourceImage.isDisposed()){
            sourceImage.dispose();
            sourceImage=null;
        }

  
  

   
    
    
    

  
       sourceImage= new Image(getDisplay(),filename);
    showOriginal();
        return sourceImage;
}

13中我们用currentDir来寄存文件打开对话框的目录,这样用户稍后能在相同的目录下选择其他的文件。

2中的loadImage方法处理旧图像创建新的图片,然后它调用showOriginal()方法来通知画布来绘制(paint)新的图像。如果加载失败,画布将清除绘画区域和禁用滚动条。注意我们在上面的代码中不能直接看到ImageLoader,然而,当我们在第4步中调用Image(Display,String)方法时,Eclipse将调用ImageLoader.load()来加载图片到内存。第5步通常用于显示原始尺寸的图片;稍后我们将更详细头论这些。

 

 事实上,上面的两个子程序可以合成一个方法。 我们之所不那样做是因为我们可以在不同的函数中单独的调用他们。比如, 我们可以从数据库中得到函数的名字, 然后我们可以通过调用loadImage()重新加载他们

 

   扩展 org.eclipse.swt.widgets.Canvas

   

 现在,让我们来看看如何创建一个画布来绘制图片并且作出一些变换org.eclipse.swt.widgets.Canvas适合于被扩展来绘制图片。SWTImageCanvas扩充了它并且添加了滚动条。通过在Canvas的函数体中设置SWT.V_SCROLLSWT.H_SCROLL格式可以做到。


   
   
    
     
   
   
public SWTImageCanvas(final Composite parent, int style) {
    
    

  
  

   
    
    
    

  
   super(parent,style|SWT.BORDER|SWT.V_SCROLL|SWT.H_SCROLL
    
    
                      |SWT.NO_BACKGROUND);
    
    

  
  

   
    
    
    

  
   addControlListener(new ControlAdapter() { /* resize listener */
    
    
        public void controlResized(ControlEvent event) {
    
    
            syncScrollBars();
    
    
        }
    
    
    });
    
    

  
  

   
    
    
    

  
   addPaintListener(new PaintListener() { /* paint listener */
    
    
        public void paintControl(PaintEvent event) {
    
    
            paint(event.gc);
    
    
        }
    
    
    });
    
    

  
  

   
    
    
    

  
   initScrollBars();
    
    
}
     
     
private void initScrollBars() {
    
    
    ScrollBar horizontal = getHorizontalBar();
    
    
    horizontal.setEnabled(false);
    
    
    horizontal.addSelectionListener(new SelectionAdapter() {
     
     
        public void widgetSelected(SelectionEvent event) {
    
    
            scrollHorizontally((ScrollBar) event.widget);
    
    
        }
    
    
    });
    
    
    ScrollBar vertical = getVerticalBar();
    
    
    vertical.setEnabled(false);
    
    
    vertical.addSelectionListener(new SelectionAdapter() {
     
     
        public void widgetSelected(SelectionEvent event) {
    
    
            scrollVertically((ScrollBar) event.widget);
    
    
        }
    
    
    });
    
    
}

为了加快绘制的进程减少颤动, 我们在 设置格式SWT.NO_BACKGROUND (稍后我们将用双缓冲来绘制) 以便背景(绘制区域)不会被清除。 新的图片覆盖背景。 当新的图片没有完全覆盖背景时我们需要填充背景和图片之间的空隙。

注册了一个调整尺寸的监听器来锁定尺寸 并且滚动条拨弄(注:作者大概是说是用鼠标轮滚动)图片聚焦尺寸和平移; 添加了绘制监听(这个它做 paint(GC gc))只要PaintEvent被触发就将绘制图片; 为每个滚动条添加了 SelectionListener, SelectionListener 通知 SWTImageCanvas to 滚动和聚焦当前滚动条下的图片;SelectionListener 另一个功能是是基于图像尺寸和变比尺寸的滚动条失效和有效。

  绘制图片

每当swt paintevent被激发后,绘画监听器(paint(GC,gc))将被调用来绘画受损区域。因为我们支持回滚和变焦,我们需要指明原始区域的哪一部分被当作绘画区域那部分。绘画的过程如下:

1.  在源图中找到一块矩形区域;在矩形区域的图片将被当作要被绘制的部分;

2. 映射imageRect到绘制区域得到destRect

3. imageRect中绘画得到destRect(如果尺寸不同则按比例调整);

4. 如果必要填充空隙(在下面的图中显示用红蓝块来填充)。

 

1)和 2) 基于AffineTransform完成,我们将在下面讨论。3) GcdrawImage绘制了源图像的一部分到 目的区域;

drawImage (Image image, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight)——它能自动按比例调整矩形区域。

    如果我们直接在屏幕上绘制举行区域,我们需要在4)中计算空隙并且填充他们,这里我们充分利用了缓冲并且填充他们。

 

  我们用下面的方法来绘制图像: 我们仅保存源图像。 当画布需要更新可视区域, 它在画布上从源图像到目的图像复制对应的区域。这种方法能提供非常大的变焦尺寸并且保存到内存。因为它不需要保存正张放大图片。绘画的过程也被加速。如果画布的面积非常大,我们能把画布切割为几个小格子,并且用我们的方法绘制每个小格子;这样这个方法将能做一些可伸缩的扩展。

下面来让我们具体地看一下代码:

private void paint(GC gc) {
    
    
1   Rectangle clientRect = getClientArea(); /* canvas' painting area */
    
    
2   if (sourceImage != null) {
/**
* Line 3 to line 10 are used to find a rectangle (imageRect) in the 
* sourceimage 
**/

  
  
   
    
  
  
3       Rectangle imageRect=SWT2Dutil.inverseTransformRect(transform, clientRect);
    
    
4       
    
    
5       int gap = 2; /* find a better start point to render. */
    
    
6       imageRect.x -= gap; imageRect.y -= gap;
    
    
7       imageRect.width += 2 * gap; imageRect.height += 2 * gap;
    
    
8
    
    
9       Rectangle imageBound=sourceImage.getBounds();
//The imageRect of line 10 is the exact rectangle we need
    
    
10      imageRect = imageRect.intersection(imageBound);
//we transform imageRect back to the canvas domain in line 11
    
    
11      Rectangle destRect = SWT2Dutil.transformRect(transform, imageRect);
    
    
12      
    
    
13      if (screenImage != null){screenImage.dispose();}
    
    
14      screenImage = new Image( getDisplay(),clientRect.width, clientRect.height);
    
    
15      GC newGC = new GC(screenImage);
    
    
16      newGC.setClipping(clientRect);
    
    
17      newGC.drawImage( sourceImage,
    
    
18            imageRect.x,
    
    
19            imageRect.y,
    
    
20            imageRect.width,
    
    
21            imageRect.height,
    
    
22            destRect.x,
    
    
23            destRect.y,
    
    
24            destRect.width,
    
    
25            destRect.height);
     
     
26      newGC.dispose();
     
     
27
     
     
28      gc.drawImage(screenImage, 0, 0);
     
     
29  } else {
/**Line 30 to line 32 are used to clear the canvas and reset the scrollbar * whenever the source image is set to null
 **/
    
    
30      gc.setClipping(clientRect);
    
    
31      gc.fillRectangle(clientRect);
    
    
32      initScrollBars();
    
    
33  }
    
    
}
    
    

Line 5 to line 7用于发现更好的坐标绘制矩形图像, 因为转换会压缩或放大没一象素的尺寸。 为了使回滚和聚焦平缓,我们一直从一个象素的开头就绘制图像. 这也保证了如果它比画布大图像将填充画布。

流程图见 (Figure 5)

 

 

 

                                                                         Figure 5 - Rendering flowchart

《未完待续》

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值