Delphi에서 이미지 구문 분석 구성 요소를 작성하는 방법
강력한 RAD 개발 도구인 Delphi는 항상 애플리케이션 소프트웨어 개발에 있어 고유한 장점을 갖고 있었습니다. 이러한 장점은 이미지 관련 소프트웨어 개발에도 반영됩니다. 데스크탑에 이미지를 배치하려면 데스크탑에 Image 컨트롤을 배치하기만 하면 됩니다. 그러면 Image 속성을 통해 BMP, WMF, EMF 및 기타 형식의 이미지를 임의로 로드할 수 있습니다. JPEG에 대한 지원을 추가하려면 JPEG 단위만 추가하면 됩니다. 이미지에 JPEG를 로드한 후에도 Delphi는 자동으로 JPEG 단위를 추가합니다. 모든 것이 매우 간단합니다. 기본 형식은 VCL에 캡슐화되어 있는데, Delphi는 JPEG와 같은 이미지 형식에 대한 지원을 어떻게 구현합니까?
실제로 모든 이미지 객체의 컨테이너로 이해될 수 있는 TPicture에서 구현 프로세스를 쉽게 볼 수 있습니다.
예를 들어 JPEG.pas에는 다음 두 줄의 코드가 있습니다.
TPicture.RegisterFileFormat('jpeg', sJPEGImageFile, TJPEGImage);
TPicture.RegisterFileFormat('jpg', sJPEGImageFile, TJPEGImage);
(sJPEGImageFile = 'JPEG 이미지 파일', JConsts.pas 참조)
그것은 무엇을 의미합니까? TJPEGImage를 jpeg와 jpg라는 두 개의 접미사가 붙은 이미지 파일로 등록하는 클래스로 이해될 수 있습니다.
핵심은 접미사, 이미지 설명, 특정 이미지 분석 클래스 및 기타 정보를 FileFormats에 저장하는 것입니다.
자세한 내용은 다음 코드를 참조하세요.
var FileFormats: TFileFormatsList = nil;
클래스 PRocedure TPicture.RegisterFileFormat(const AExtension,
ADescription: 문자열; AGraphicClass: TGraphicClass);
시작하다
GetFileFormats.Add(AExtension, ADescription, 0, AGraphicClass);
끝;
함수 GetFileFormats: TFileFormatsList;
시작하다
if FileFormats = nil then FileFormats := TFileFormatsList.Create;
결과 := 파일 형식;
끝;
TPicture는 TFileFormatsList의 생성자에 추가되었기 때문에 기본적으로 4가지 이미지 형식을 지원합니다.
생성자 TFileFormatsList.Create;
시작하다
상속된 생성;
Add('wmf', SVMetafiles, 0, TMetafile);
Add('emf', SVEnhMetafiles, 0, TMetafile);
Add('ico', SVIcons, 0, TIcon);
Add('bmp', SVBitmaps, 0, TBitmap);
끝;
OpenPictureDialog 컨트롤은 FileFormats에 저장된 정보를 통해 지원되는 파일 형식 목록을 자동으로 생성합니다.
그렇다면 이러한 이미지 구문 분석 클래스를 작성하는 방법은 무엇입니까?
TGraphic은 TBitmap, TIcon 및 TMetafile 객체의 기본 클래스입니다. 마찬가지로 여기의 이미지 구문 분석 클래스도 TGraphic에서 파생되어야 합니다. VCL에 캡슐화된 많은 코드를 사용하여 많은 작업을 절약할 수 있습니다.
기본 기능을 구현하려면 일반적으로 다음 세 멤버만 오버로드하면 됩니다.
TXXXImage = 클래스(TGraphic)
보호됨
절차 Draw(ACanvas: TCanvas; const Rect: TRect);//캔버스에 이미지를 그립니다.
공공의
절차 LoadFromStream(Stream: TStream); //스트림에서 이미지 데이터 가져오기;
Procedure SaveToStream(Stream: TStream); //이미지 데이터를 스트림에 씁니다.
끝;
TGraphic.LoadFromFile/TGraphic.SaveToFile에는 파일명에서 스트림으로 데이터를 읽어오는 기능/스트림에 있는 데이터를 해당 파일에 쓰는 기능이 이미 구현되어 있으므로 특별한 필요 없이 오버로드할 필요는 없습니다. Draw 멤버는 캔버스에 이미지를 그리는 데 자연스럽게 사용됩니다. TCanvas의 GDI 완벽 캡슐화로 인해 GDI를 사용하여 이미지를 양식에 그리는 과정을 고려할 필요가 없습니다. 이제 남은 것은 이미지 파싱 부분에 대한 코드를 작성하는 것뿐입니다.
추가 논의를 위해 RAS 형식을 예로 들어 보겠습니다.
여기서는 TGraphic을 기본 클래스로 사용하지 않고 TBitmap을 사용하여 Draw 구현 과정을 더욱 절약하고 LoadFromStream에서 비트맵으로 변환하는 과정만 구현하면 됩니다.
유형
TRASGraphic = 클래스(TBitmap)
공공의
절차 LoadFromStream(스트림: TStream);
절차 SaveToStream(Stream: TStream) 재정의;
끝;
//RAS 파일 헤더를 설명하는 레코드 유형 정의
TRASHeader = 압축된 레코드
매직, //마크
너비, //너비
높이, //높음
심도, //색상 심도
길이, //이미지 데이터 길이는 0과 같을 수 있습니다.
RasType, //포맷 유형
MapType, //팔레트 유형
MapLength: 기본 길이; //팔레트 데이터 길이
끝;
//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): 추기경;
asm
BSWAP EAX//콜 바이트 교환 명령
끝;
//예외 발생, 매개변수는 특정 예외 정보입니다.
프로시저 RasError(const ErrorString: String);
시작하다
EInvalidGraphic.Create(ErrorString)를 발생시킵니다.
끝;
{다음은 구현 부분에 대한 코드입니다. }
절차 TRASGraphic.LoadFromStream(스트림: TStream);
var
헤더: TRASHeader;
Row8: PByte;
Row24: PRGBTriple;
Row32: PRGB쿼드;
PMap: PByte;
Y: 정수;
I: 정수;
MapReaded: 부울;
Pal: TMaxLogPalette;
R,G,B:바이트 배열[0..255];
컬러바이트: 바이트;
시작하다
Stream do와 함께
시작하다
ReadBuffer(Header, SizeOf(Header)); //파일 헤더 데이터를 레코드 Header로 읽어옵니다.
헤더로
시작하다
너비 := SwapLong(너비);
높이 := SwapLong(높이);
깊이 := SwapLong(깊이);
길이 := SwapLong(길이);
RASType := SwapLong(RASType);
MapType := SwapLong(MapType);
MapLength := SwapLong(MapLength);
끝;
//데이터를 읽는 순서 때문에 순서를 변경하려면 위의 SwapLong을 호출해야 합니다.
if (Header.Magic = $956AA659) 그리고
(Header.Width<>0) 및 (Header.Height<>0) 및
([1,8,24,32]의 Header.Depth) 및 ([RT_OLD,RT_STANDARD,RT_BYTE_ENCODED,RT_FORMAT_RGB]의 Header.RasType)
시작하다
너비 := 헤더.너비;
높이 := 헤더.높이;
지도읽기 := 거짓;
케이스 헤더.깊이
1:픽셀 형식 := pf1Bit;
8:
시작하다
PixelFormat := pf8Bit;
케이스 Header.MapType of
RMT_NONE:
시작하다
Pal.palVersion:=$300;
Pal.palNumEntries:=256;
I의 경우 := 0 ~ 255 do
시작하다
Pal.palPalEntry[I].peRed:=I;
Pal.palPalEntry[I].peGreen:=I;
Pal.palPalEntry[I].peBlue:=I;
Pal.palPalEntry[I].peFlags:=0;
끝;
팔레트 := CreatePalette(PLogPalette(@Pal)^);
//이미지 색상 심도가 8비트이고 팔레트 정보가 없는 경우 8비트 그레이스케일 팔레트를 생성합니다.
끝;
RMT_EQUAL_RGB:
시작하다
if (Header.MapLength = 3*256) 그러면
시작하다
Pal.palVersion:=$300;
Pal.palNumEntries:=256;
ReadBuffer(R,256);
ReadBuffer(G,256);
ReadBuffer(B,256);
I의 경우 := 0 ~ 255 do
시작하다
Pal.palPalEntry[I].peRed:=R[I];
Pal.palPalEntry[I].peGreen:=G[I];
Pal.palPalEntry[I].peBlue:=B[I];
Pal.palPalEntry[I].peFlags:=0;
끝;
팔레트 := CreatePalette(PLogPalette(@Pal)^);
//파일의 팔레트 정보를 읽습니다.
//API 관련 팔레트 작업은 MSDN을 확인하세요.
끝
또 다른
RasError('팔레트 길이가 잘못되었습니다!');
MapReaded := 참;
끝;
RMT_RAW:
시작하다
RasError('지원되지 않는 파일 형식입니다!');
끝;
끝;
끝;
24:픽셀 형식 := pf24Bit;
32:
시작하다
PixelFormat := pf32Bit;
//
끝;
끝;
(MapReaded가 아님) 및 (Header.MapLength>0)인 경우
시작하다
위치 := 위치 + Header.MapLength;
끝;
//팔레트 길이가 0이 아니고 해당 정보를 제대로 읽지 못한 경우 이 데이터를 건너뜁니다.
케이스 헤더.깊이
8:
시작하다
Header.RasType = RT_BYTE_ENCODED인 경우
시작하다
//인코딩
//RLE 압축의 인코딩 및 디코딩에 대한 정보를 직접 확인하시기 바랍니다.
RasError('압축 형식은 지원되지 않습니다!');
끝
또 다른
시작하다
Y := 0 ~ 높이-1 do
시작하다
Row8:=스캔라인[Y];
ReadBuffer(Row8^,Width);
(너비 모드 2)=1이면
시작하다
위치 := 위치 + 1;
끝;
끝;
끝;
end;{8Bit의 끝}
스물넷:
시작하다
케이스 헤더.Ras 유형
RT_OLD,
RT_표준:
시작하다
Y := 0 ~ 높이-1 do
시작하다
Row24:=스캔라인[Y];
ReadBuffer(Row24^,너비*3);
(너비 모드 2)=1이면
시작하다
위치 := 위치 + 1;
끝;
끝;
끝;
RT_BYTE_ENCODED:
시작하다
//인코딩
//RLE 압축의 인코딩 및 디코딩에 대한 정보를 직접 확인하시기 바랍니다.
RasError('압축 형식은 지원되지 않습니다!');
끝;
RT_FORMAT_RGB:
시작하다
Y := 0 ~ 높이-1 do
시작하다
Row24:=스캔라인[Y];
ReadBuffer(Row24^,너비*3);
I := 0 ~ Width-1 do
시작하다
ColorByte := Row24^.rgbtRed;
Row24^.rgbtRed := Row24^.rgbtBlue;
Row24^.rgbtBlue := ColorByte;
Inc(Row24);
끝;
//RT_FORMAT_RGB 형식인 경우 RGB로 데이터를 가져오는데, 여기서 R과 B의 값을 교환해야 합니다.
(너비 모드 2)=1이면
시작하다
위치 := 위치 + 1;
끝;
끝;
끝;{RT_FORMAT_RGB 끝}
또 다른
RasError('지원되지 않는 파일 형식입니다!');
끝;
end;{24Bit의 끝}
32:
시작하다
케이스 헤더.Ras 유형
RT_OLD,
RT_표준:
시작하다
Y := 0 ~ 높이-1 do
시작하다
Row32:=스캔라인[Y];
ReadBuffer(Row32^,너비*4);
I := 0 ~ Width-1 do
시작하다
ColorByte := Row32^.rgbReserved;
Row32^.rgbReserved := Row32^.rgbBlue;
Row32^.rgbBlue := Row32^.rgbGreen;
Row32^.rgbGreen := Row32^.rgbRed;
Row32^.rgbRed := ColorByte;
Inc(Row32);
끝;
//32비트 컬러를 사용할 경우 데이터를 읽은 후 순서를 조정해야 합니다.
끝;
끝;
RT_BYTE_ENCODED:
시작하다
//인코딩
//RLE 압축의 인코딩 및 디코딩에 대한 정보를 직접 확인하시기 바랍니다.
RasError('압축 형식은 지원되지 않습니다!');
끝;
RT_FORMAT_RGB:
시작하다
Y의 경우 := 0 ~ 높이-1
시작하다
Row32:=스캔라인[Y];
ReadBuffer(Row32^,너비*4);
I := 0 ~ Width-1 do
시작하다
ColorByte := Row32^.rgbBlue;
Row32^.rgbBlue := Row32^.rgbReserved;
Row32^.rgbReserved := ColorByte;
ColorByte := Row32^.rgbGreen;
Row32^.rgbGreen := Row32^.rgbRed;
Row32^.rgbRed := ColorByte;
Inc(Row32);
끝;
//주문 조정 및 R, B 값 교환을 위한 코드가 여기에 병합됩니다.
끝;
끝;{RT_FORMAT_RGB 끝}
또 다른
RasError('지원되지 않는 파일 형식입니다!');
end;{32Bit의 끝}
끝;
또 다른
시작하다
무료이미지;
RasError('지원되지 않는 파일 형식입니다!');
끝;
끝;
끝
또 다른
RasError('지원되지 않는 파일 형식입니다!');
끝;{끝나다}
끝;
{다음 코드는 위 코드에 여러 번 나타납니다.
(너비 모드 2)=1이면
시작하다
위치 := 위치 + 1;
끝;
이는 각 행의 데이터가 워드 정렬되어야 하기 때문입니다. 즉, 각 행의 데이터는 짝수 바이트로 기록되어야 합니다. 각 픽셀의 색상 정보를 1바이트(8비트) 또는 3바이트(24비트)로 기록하고 각 행의 픽셀 수가 홀수인 경우 1바이트를 채워야 합니다. 따라서 여기서는 1바이트를 건너뜁니다.
뒤에있는 코드에서
if (Width mod 2) = 1이면
시작하다
FillByte:=0;
Stream.Write(FillByte,1);
끝;
이 역시 같은 원리에 기초하고 있다. }
절차 TRASGraphic.SaveToStream(스트림: TStream);
var
헤더: TRASHeader;
Row8: PByte;
Row24: PRGBTriple;
Row32: PRGB쿼드;
FillByte: 바이트;
Y: 정수;
I: 정수;
Pal: TMaxLogPalette;
R,G,B:바이트 배열[0..255];
시작하다
헤더.매직 := $956AA659;
헤더.폭 := SwapLong(너비);
Header.Height := SwapLong(높이);
Header.RasType := SwapLong(RT_STANDARD);
if (PixelFormat = pf1bit) 또는 (PixelFormat = pf4bit) 그러면
PixelFormat:=pf8bit
그렇지 않은 경우 (PixelFormat <> pf8bit) 및 (PixelFormat <> pf24bit) 및 (PixelFormat <> pf32bit)
PixelFormat:=pf24bit;
PixelFormat의 경우
pf8bit:
시작하다
Header.Length := SwapLong(Height*(Width+(Width mod 2)));
헤더.깊이 := SwapLong(8);
Header.MapType := SwapLong(RMT_EQUAL_RGB);
Header.MapLength := SwapLong(3*256);
Stream.WriteBuffer(헤더,SizeOf(헤더));
GetPaletteEntries(Palette, 0, 256, Pal.palPalEntry);
I의 경우 := 0 ~ 255 do
시작하다
R[I]:=Pal.palPalEntry[I].peRed;
G[I]:=Pal.palPalEntry[I].peGreen;
B[I]:=Pal.palPalEntry[I].peBlue;
끝;
//API 관련 팔레트 작업은 MSDN을 확인하세요.
Stream.WriteBuffer(R,256);
Stream.WriteBuffer(G,256);
Stream.WriteBuffer(B,256);
Y := 0 ~ 높이-1 do
시작하다
Row8 := ScanLine[Y];
Stream.WriteBuffer(Row8^,Width);
if (Width mod 2) = 1이면
시작하다
FillByte:=0;
Stream.Write(FillByte,1);
끝;
끝;
끝;
pf32비트:
시작하다
Header.Length := SwapLong(높이*너비*4);
헤더.깊이 := SwapLong(32);
Header.MapType := SwapLong(RMT_NONE);
헤더.MapLength := 0;
Stream.WriteBuffer(헤더,SizeOf(헤더));
Y := 0 ~ 높이-1 do
시작하다
Row32 := ScanLine[Y];
I := 0 ~ Width-1 do
시작하다
Stream.WriteBuffer(Row32.rgbReserved,1);
Stream.WriteBuffer(Row32^,3);
Inc(Row32);
끝;
끝;
끝;
또 다른
시작하다
Header.Length := SwapLong(높이*너비*3);
헤더.깊이 := SwapLong(24);
Header.MapType := SwapLong(RMT_NONE);
헤더.MapLength := 0;
Stream.WriteBuffer(헤더,SizeOf(헤더));
Y := 0 ~ 높이-1 do
시작하다
Row24 := ScanLine[Y];
Stream.WriteBuffer(Row24^,너비*3);
if (Width mod 2) = 1이면
시작하다
FillByte:=0;
Stream.Write(FillByte,1);
끝;
끝;
끝;
끝;
//SaveToStream은 기본적으로 LoadFromStream의 역 프로세스입니다.
끝;
초기화
TPicture.RegisterFileFormat('RAS', 'Sun RAS', TRASGraphic);
마무리
TPicture.UnregisterGraphicClass(TRASGraphic);
이 몇 줄의 코드로 완전한 이미지 구문 분석 구성 요소가 완성됩니다.