Delphi中如何编写图像解析组件[1]

[入库:2005年8月18日] [更新:2007年3月24日]

本文简介:选择自 ehom 的 blog

delphi中如何编写图像解析组件

delphi作为一个强大的rad开发工具,在应用软件的开发方面一直有着它的独特优势。这种优势同样体现在图像相关软件的开发上。如果你要在桌面上放置一张图像,只需要简单的在桌面上放置一个image控件,然后就可以通过其image属性任意的加载bmp、wmf、emf等格式的图像。如果还想增加对jpeg的支持,只需要添加一个jpeg单元即可。甚至在image中加载一张jpeg后,delphi会自动添加一个jpeg单元。一切做起来就是这么的简单。基本格式都已经封装在了vcl中,那么delphi对类似jpeg这样图像格式的支持是如何实现的呢?

其实从tpicture中很容易看出其中的实现过程,它可以理解为所有图像对象的容器。

如jpeg.pas中有如下两句代码:

tpicture.registerfileformat('jpeg', sjpegimagefile, tjpegimage);
tpicture.registerfileformat('jpg', sjpegimagefile, tjpegimage);

(sjpegimagefile = 'jpeg image file',见jconsts.pas)

什么意思呢?可以理解为将tjpegimage注册为jpeg、jpg两种后缀图像文件的类。

其实质就是将后缀,图像描述,具体图像解析类等信息保存到了fileformats。

具体见如下代码:

var fileformats: tfileformatslist = nil;

class procedure tpicture.registerfileformat(const aextension,
  adescription: string; agraphicclass: tgraphicclass);
begin
  getfileformats.add(aextension, adescription, 0, agraphicclass);
end;

function getfileformats: tfileformatslist;
begin
  if fileformats = nil then fileformats := tfileformatslist.create;
  result := fileformats;
end;

而tpicture默认支持四种图像格式是因为tfileformatslist的构造函数中已进行了添加。

constructor tfileformatslist.create;
begin
  inherited create;
  add('wmf', svmetafiles, 0, tmetafile);
  add('emf', svenhmetafiles, 0, tmetafile);
  add('ico', svicons, 0, ticon);
  add('bmp', svbitmaps, 0, tbitmap);
end;

也正是通过fileformats中保存的信息,控件openpicturedialog中自动生成了所支持文件类型的列表。

那么该如何编写这些图像解析类呢?

tgraphic是tbitmap、ticon、tmetafile对象的基类。同样这里的图像解析类也应该从tgraphic派生,利用很多vcl中已经封装了的代码,可以省去很多工作。

实现基本功能一般只需要重载三个成员:

txxximage = class(tgraphic)
protected
  procedure draw(acanvas: tcanvas; const rect: trect); override;//绘制图像到画布
public
  procedure loadfromstream(stream: tstream); override; //从流中获取图像数据
  procedure savetostream(stream: tstream); override; //将图像数据写入流中
end;

因为tgraphic.loadfromfile/tgraphic.savetofile中已经实现了由文件名读取数据到流的/将流中的数据写入到对应文件的功能,无特殊需要这里可以不用重载。而成员draw自然就是用于实现将图像绘制到画布,由于tcanvas对gdi的完善封装,这里不需要考虑如何将图像利用gdi绘制到窗体的这个过程。剩下的就只是编写图像解析部分的代码啦。

下面就以ras格式为例做进一步的探讨。

这里没有用tgraphic作为基类,而是用了tbitmap,这样进一步把draw的实现过程都省了,只需要在loadfromstream中实现转化为位图的过程就可以了。

type

trasgraphic = class(tbitmap)
public
  procedure loadfromstream(stream: tstream); override;
  procedure savetostream(stream: tstream); override;
end;

//定义描述ras文件头的记录类型
trasheader = packed record
  magic,               //标记
  width,               //宽
  height,              //高
  depth,               //色深
  length,              //图像数据长度,可能会等于0
  rastype,             //格式类型
  maptype,             //调色板类型
  maplength: cardinal; //调色板数据长度
end;

//定义一个用来描述ras文件头的记录类型是非常必要的

const

//定义代表ras所有类型的常量
  rt_old = 0;
  rt_standard = 1;
  rt_byte_encoded = 2;
  rt_format_rgb = 3;
  rt_format_tiff = 4;
  rt_format_iff = 5;
  rt_experimental = $ffff;

//定义代表调色板类型的常量
  rmt_none = 0;//无调色板数据
  rmt_equal_rgb = 1;
  rmt_raw = 2;

{如果ras的格式为rt_old,数据长度可能为0}


function swaplong(const value: cardinal): cardinal;
asm
  bswap eax//调用字节交换指令
end;

//抛出异常,参数为具体的异常信息
procedure raserror(const errorstring: string);
begin
  raise einvalidgraphic.create(errorstring);
end;

{下面是实现部分的代码。}

procedure trasgraphic.loadfromstream(stream: tstream);
var
  header: trasheader;
  row8: pbyte;
  row24: prgbtriple;
  row32: prgbquad;
  pmap: pbyte;
  y: integer;
  i: integer;
  mapreaded: boolean;
  pal: tmaxlogpalette;
  r,g,b:array[0..255] of byte;
  colorbyte: byte;
begin
with stream do
begin
  readbuffer(header, sizeof(header)); //将文件头数据读取到记录header中
  with header do
  begin
    width := swaplong(width);
    height := swaplong(height);
    depth := swaplong(depth);
    length := swaplong(length);
    rastype := swaplong(rastype);
    maptype := swaplong(maptype);
    maplength := swaplong(maplength);
  end;

本文关键:图像解析,RAS,TGraphic,TPicture
  相关方案
Google
 

本站最佳浏览方式为 分辨率 1024x768 IE 6.0(或更高版本的 IE浏览器)

go top