亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Delphi > 正文

Delphi中如何編寫圖像解析組件

2019-11-18 18:35:31
字體:
來源:轉載
供稿:網友
 

Delphi中如何編寫圖像解析組件

Delphi作為一個強大的RAD開發工具,在應用軟件的開發方面一直有著它的獨特優勢。這種優勢同樣體現在圖像相關軟件的開發上。如果你要在桌面上放置一張圖像,只需要簡單的在桌面上放置一個Image控件,然后就可以通過其Image屬性任意的加載BMP、WMF、EMF等格式的圖像。如果還想增加對JPEG的支持,只需要添加一個JPEG單元即可。甚至在Image中加載一張JPEG后,Delphi會自動添加一個JPEG單元。一切做起來就是這么的簡單?;靖袷蕉家呀浄庋b在了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;
  //由于讀取數據的順序問題,這里需要調用上面的SwapLong改變順序。
  if (Header.Magic = $956AA659) and
  (Header.Width<>0) and (Header.Height<>0) and
  (Header.Depth in [1,8,24,32]) and (Header.RasType in [RT_OLD,RT_STANDARD,RT_BYTE_ENCODED,RT_FORMAT_RGB]) then
  begin
    Width := Header.Width;
    Height := Header.Height;
    MapReaded := False;

    case Header.Depth of
      1:PixelFormat := pf1Bit;
      8:
      begin
        PixelFormat := pf8Bit;

        case Header.MapType of
          RMT_NONE:
          begin
            Pal.palVersion:=$300;
            Pal.palNumEntries:=256;
            for I := 0 to 255 do
            begin
              Pal.palPalEntry[I].peRed:=I;
              Pal.palPalEntry[I].peGreen:=I;
              Pal.palPalEntry[I].peBlue:=I;
              Pal.palPalEntry[I].peFlags:=0;
            end;
            Palette := CreatePalette(PLogPalette(@Pal)^);
            //當圖像色深為8位,而又不存在調色板信息時,創建一個8位的灰度調色板
          end;
          RMT_EQUAL_RGB:
          begin
            if (Header.MapLength = 3*256) then
            begin
              Pal.palVersion:=$300;
              Pal.palNumEntries:=256;
              ReadBuffer(R,256);
              ReadBuffer(G,256);
              ReadBuffer(B,256);
              for I := 0 to 255 do
              begin
                Pal.palPalEntry[I].peRed:=R[I];
                Pal.palPalEntry[I].peGreen:=G[I];
                Pal.palPalEntry[I].peBlue:=B[I];
                Pal.palPalEntry[I].peFlags:=0;
              end;
              Palette := CreatePalette(PLogPalette(@Pal)^);
              //讀取文件中的調色板信息
              //相關調色板操作的API請查詢MSDN
            end
            else
              RasError('調色板長度錯誤!');
            MapReaded := True;
          end;
          RMT_RAW:
          begin
            RasError('不支持的文件格式!');
          end;
        end;
      end;
      24:PixelFormat := pf24Bit;
      32:
      begin
        PixelFormat := pf32Bit;
        //
      end;
    end;

    if (not MapReaded) and (Header.MapLength>0) then
    begin
      Position := Position + Header.MapLength;
    end;
    //如果調色板長度不為0,而又未正確讀取相關信息時,跳過這一段數據

    case Header.Depth of
      8:
      begin
        if Header.RasType = RT_BYTE_ENCODED then
        begin
          //ENCODE
          //關于RLE壓縮的編碼解碼請自行查閱資料
          RasError('不支持壓縮格式!');
        end
        else
        begin
          for Y := 0 to Height-1 do
          begin
            Row8:=ScanLine[Y];
            ReadBuffer(Row8^,Width);
            if (Width mod 2)=1 then
            begin
               Position := Position + 1;
            end;
          end;
        end;
      end;{end of 8Bit}
      24:
      begin
        case Header.RasType of
          RT_OLD,
          RT_STANDARD:
          begin
            for Y := 0 to Height-1 do
            begin
              Row24:=ScanLine[Y];
              ReadBuffer(Row24^,Width*3);
              if (Width mod 2)=1 then
              begin
                 Position := Position + 1;
              end;
            end;
          end;
          RT_BYTE_ENCODED:
          begin
            //ENCODE
            //關于RLE壓縮的編碼解碼請自行查閱資料
            RasError('不支持壓縮格式!');
          end;
          RT_FORMAT_RGB:
          begin
            for Y := 0 to Height-1 do
            begin
              Row24:=ScanLine[Y];
              ReadBuffer(Row24^,Width*3);
              for I := 0 to Width-1 do
              begin
                ColorByte := Row24^.rgbtRed;
                Row24^.rgbtRed := Row24^.rgbtBlue;
                Row24^.rgbtBlue := ColorByte;
                Inc(Row24);
              end;
              //當為RT_FORMAT_RGB格式時,按RGB獲取數據,這里需要交換R和B的值
              if (Width mod 2)=1 then
              begin
                 Position := Position + 1;
              end;
            end;
          end;{end of RT_FORMAT_RGB}
          else
            RasError('不支持的文件格式!');
        end;
      end;{end of 24Bit}
      32:
      begin
        case Header.RasType of
          RT_OLD,
          RT_STANDARD:
          begin
            for Y := 0 to Height-1 do
            begin
              Row32:=ScanLine[Y];
              ReadBuffer(Row32^,Width*4);
              for I := 0 to Width-1 do
              begin
                ColorByte := Row32^.rgbReserved;
                Row32^.rgbReserved := Row32^.rgbBlue;
                Row32^.rgbBlue := Row32^.rgbGreen;
                Row32^.rgbGreen := Row32^.rgbRed;
                Row32^.rgbRed := ColorByte;
                Inc(Row32);
              end;
              //32位色時,需要調整讀取后數據的順序
            end;
          end;
          RT_BYTE_ENCODED:
          begin
            //ENCODE
            //關于RLE壓縮的編碼解碼請自行查閱資料
            RasError('不支持壓縮格式!');
          end;
          RT_FORMAT_RGB:
          begin
            For Y := 0 to Height-1 do
            begin
              Row32:=ScanLine[Y];
              ReadBuffer(Row32^,Width*4);
              for I := 0 to Width-1 do
              begin
                ColorByte := Row32^.rgbBlue;
                Row32^.rgbBlue := Row32^.rgbReserved;
                Row32^.rgbReserved := ColorByte;
                ColorByte := Row32^.rgbGreen;
                Row32^.rgbGreen := Row32^.rgbRed;
                Row32^.rgbRed := ColorByte;
                Inc(Row32);
              end;
              //這里將順序調整和R和B值的交換的代碼進行了合并
            end;
          end;{end of RT_FORMAT_RGB}
          else
            RasError('不支持的文件格式!');
        end;{end of 32Bit}

      end;
      else
      begin
        FreeImage;
        RasError('不支持的文件格式!');
      end;
    end;
  end
  else
    RasError('不支持的文件格式!');

end;{end with}
end;

{上面的代碼中多次出現如下代碼:
if (Width mod 2)=1 then
begin
  Position := Position + 1;
end;
這是因為每行的數據都要按字對齊,既每行的數據都要用偶數的字節記錄。當每個像素的顏色信息用1字節(8位)或3字節(24位)記錄且每行像素數為奇數時,要補齊一個字節。所以這里跳過一個字節。
后面代碼中的
if (Width mod 2) = 1 then
begin
  FillByte:=0;
  Stream.Write(FillByte,1);
end;
也是基于同一道理。}

procedure TRASGraphic.SaveToStream(Stream: TStream);
var
  Header: TRASHeader;
  Row8: PByte;
  Row24: PRGBTriple;
  Row32: PRGBQuad;
  FillByte: Byte;
  Y: Integer;
  I: Integer;
  Pal: TMaxLogPalette;
  R,G,B:array[0..255] of Byte;
begin
Header.Magic := $956AA659;
Header.Width := SwapLong(Width);
Header.Height := SwapLong(Height);
Header.RasType := SwapLong(RT_STANDARD);
if (PixelFormat = pf1bit) or (PixelFormat = pf4bit) then
  PixelFormat:=pf8bit
else if (PixelFormat <> pf8bit) and (PixelFormat <> pf24bit) and (PixelFormat <> pf32bit) then
  PixelFormat:=pf24bit;
case PixelFormat of
  pf8bit:
  begin
    Header.Length := SwapLong(Height*(Width+(Width mod 2)));
    Header.Depth := SwapLong(8);
    Header.MapType := SwapLong(RMT_EQUAL_RGB);
    Header.MapLength := SwapLong(3*256);
    Stream.WriteBuffer(Header,SizeOf(Header));
    GetPaletteEntries(Palette, 0, 256, Pal.palPalEntry);
    for I := 0 to 255 do
    begin
      R[I]:=Pal.palPalEntry[I].peRed;
      G[I]:=Pal.palPalEntry[I].peGreen;
      B[I]:=Pal.palPalEntry[I].peBlue;
    end;
    //相關調色板操作的API請查詢MSDN
    Stream.WriteBuffer(R,256);
    Stream.WriteBuffer(G,256);
    Stream.WriteBuffer(B,256);
    for Y := 0 to Height-1 do
    begin
      Row8 := ScanLine[Y];
      Stream.WriteBuffer(Row8^,Width);
      if (Width mod 2) = 1 then
      begin
        FillByte:=0;
        Stream.Write(FillByte,1);
      end;
    end;
  end;
  pf32bit:
  begin
    Header.Length := SwapLong(Height*Width*4);
    Header.Depth := SwapLong(32);
    Header.MapType := SwapLong(RMT_NONE);
    Header.MapLength := 0;
    Stream.WriteBuffer(Header,SizeOf(Header));
    for Y := 0 to Height-1 do
    begin
      Row32 := ScanLine[Y];
      for I := 0 to Width-1 do
      begin
        Stream.WriteBuffer(Row32.rgbReserved,1);
        Stream.WriteBuffer(Row32^,3);
        Inc(Row32);
      end;
    end;
  end;
  else
  begin
    Header.Length := SwapLong(Height*Width*3);
    Header.Depth := SwapLong(24);
    Header.MapType := SwapLong(RMT_NONE);
    Header.MapLength := 0;
    Stream.WriteBuffer(Header,SizeOf(Header));
    for Y := 0 to Height-1 do
    begin
      Row24 := ScanLine[Y];
      Stream.WriteBuffer(Row24^,Width*3);
      if (Width mod 2) = 1 then
      begin
        FillByte:=0;
        Stream.Write(FillByte,1);
      end;     
    end;
  end;
end;
//SaveToStream基本上就是LoadFromStream的逆過程。

end;

initialization
  TPicture.RegisterFileFormat('RAS', 'Sun RAS', TRASGraphic);
finalization
  TPicture.UnregisterGraphicClass(TRASGraphic);

加上這幾句代碼,一個完整的圖像解析組件就完成了。


上一篇:Delphi的組件讀寫機制(三)

下一篇:Delphi+匯編例子1(求和的比較)

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
學習交流
熱門圖片

新聞熱點

疑難解答

圖片精選

網友關注

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
精品调教chinesegay| 欧美一级视频一区二区| 国产精品pans私拍| 7777精品视频| 一本色道久久综合狠狠躁篇的优点| 美女精品视频一区| 国产精品一区二区久久久| 亚洲一区999| 琪琪第一精品导航| 久久久www成人免费精品张筱雨| 国产日韩中文在线| 中文字幕成人精品久久不卡| 亚洲国产高清福利视频| 亚洲精品第一国产综合精品| 色噜噜亚洲精品中文字幕| 亚洲男人天堂手机在线| 亚洲激情自拍图| 亚洲欧美日韩在线一区| 国产精品入口日韩视频大尺度| 中文字幕精品影院| 日韩福利伦理影院免费| 亚洲色在线视频| 国产日韩精品在线| 91色在线视频| 成人av在线网址| 国产精品女视频| 久久精品视频在线播放| 日韩电影免费观看在线观看| 精品亚洲国产视频| 日韩视频第一页| 日韩有码在线视频| 国产精品久久激情| 亚洲国产日韩欧美在线99| 九色成人免费视频| 国产在线观看一区二区三区| 亚洲午夜精品久久久久久久久久久久| 久久婷婷国产麻豆91天堂| 国产精品久久久久久久久免费看| 色综合久久精品亚洲国产| 亚洲精品小视频| 欧美亚洲成人xxx| 亚洲国语精品自产拍在线观看| 成人午夜在线观看| 国产精品十八以下禁看| 国产精品十八以下禁看| 国产精品视频大全| 国产精品自产拍在线观看| 日韩在线小视频| 久久精品久久久久电影| 国产精品久久婷婷六月丁香| 操日韩av在线电影| 成人观看高清在线观看免费| 欧美电影免费观看| 午夜精品久久久久久久99热浪潮| x99av成人免费| 亚洲欧美成人在线| 91免费国产视频| 日韩av中文字幕在线免费观看| 色狠狠久久aa北条麻妃| 日韩黄在线观看| 日韩av在线免费观看| 青青久久av北条麻妃海外网| 日韩精品视频在线| 亚洲精品一区中文字幕乱码| 久久伊人精品天天| 在线国产精品视频| 北条麻妃一区二区三区中文字幕| 日韩精品在线免费观看视频| 亚洲欧美在线看| 欧洲成人在线观看| 正在播放欧美视频| 国产大片精品免费永久看nba| 久久精品男人天堂| 日韩av观看网址| 久久五月天色综合| 久久久久国产精品一区| 久久久av亚洲男天堂| 国内外成人免费激情在线视频网站| 国产日韩精品在线播放| 欧美黄色小视频| 综合网日日天干夜夜久久| 欧美激情视频网址| 国产人妖伪娘一区91| 尤物99国产成人精品视频| 91精品国产色综合久久不卡98口| 日韩美女视频免费在线观看| 欧美激情videos| 亚洲www视频| 成人午夜高潮视频| 成人国产精品久久久久久亚洲| 国产精品丝袜久久久久久高清| 91探花福利精品国产自产在线| 久久久久久中文| 成人免费淫片aa视频免费| 97激碰免费视频| 国产在线精品成人一区二区三区| 91久久国产精品91久久性色| 97久久精品在线| 亚洲欧美另类中文字幕| 久久在线免费观看视频| 91中文字幕一区| 日韩av中文字幕在线免费观看| 国产成人在线精品| 欧美在线www| 欧美高清激情视频| 456亚洲影院| 国产伦精品一区二区三区精品视频| 久久久久久久一区二区| 成人福利网站在线观看| 欧美麻豆久久久久久中文| 国模gogo一区二区大胆私拍| 日韩av手机在线看| 久久久国产精彩视频美女艺术照福利| 国产v综合ⅴ日韩v欧美大片| 久久免费观看视频| 精品中文字幕视频| 日韩中文字幕欧美| 欧美第一黄色网| 欧美电影免费看| 久久免费视频网| 亚洲人永久免费| 亚洲综合在线小说| 777国产偷窥盗摄精品视频| 日韩人体视频一二区| 亚洲国产福利在线| 欧美精品手机在线| 国产精品aaa| 色综合亚洲精品激情狠狠| 国产不卡在线观看| 国内精品久久久| 成人激情综合网| 日韩电影免费观看在线| 91视频九色网站| 精品亚洲一区二区三区在线观看| 97精品视频在线| 最新的欧美黄色| 欧美成人激情图片网| 国产精品午夜一区二区欲梦| 最好看的2019年中文视频| 亚洲人线精品午夜| 欧美成人激情视频免费观看| 最近免费中文字幕视频2019| 久久精品国产成人精品| 国产日韩欧美夫妻视频在线观看| 国产精品久久久久久久久免费| 亚洲性无码av在线| 久久精品国产一区二区电影| 日韩电影免费在线观看中文字幕| 欧美三级欧美成人高清www| 国产精品女人久久久久久| 亚洲综合大片69999| 欧美多人乱p欧美4p久久| 亚洲精品动漫久久久久| 久久国产精品久久久久| 日韩中文字幕精品| 自拍偷拍亚洲欧美| 欧美乱大交xxxxx另类电影| 国产一区二区免费| 国产一区二区三区直播精品电影| 国产精品日韩一区| 最新国产精品拍自在线播放| 国产精品视频999| 亚洲直播在线一区| 亚洲一区二区三区成人在线视频精品|