unit was;
interface
uses
SysUtils, Classes, Graphics, Dialogs, StrUtils;
type
TWasPixel = record
Alpha: Byte;
Index: Byte;
end;
TWasImageInfo = record
Key_X: Integer;
Key_Y: Integer;
Width: Integer;
Height: Integer;
end;
TWasImage = record
Info: TWasImageInfo;
Data: array of TWasPixel;
end;
TWasFileInfo = record
wfType: Word;
whSize: Word;
SpriteCount: Word;
FrameCount: Word;
MaxWidth: Word;
MaxHeight: Word;
Key_X: Word;
Key_Y: Word;
end;
HighPal = array [0..255] of Word;
TWasFile = class
private
// 图片信息
Info: TWasFileInfo;
Data: array of TWasImage;
HeadData: array of Byte; // 存储多余的文件头
HeadDataSize: Integer;
// 2个调色板
OriginalPal: array [0..255] of Word;
CurrentPal: array [0..255] of Word;
// 原始文件
FName: string;
FSize: Cardinal;
FData: array of Byte;
fInitialized: Boolean;
// xxxxxxxxxxxxxxxxxx
procedure DecodeFromStream (var Dest: TWasImage; Stream: TStream);
procedure Init ();
procedure Burn ();
public
constructor Create ();
destructor Destroy (); override;
// 相关方法
procedure LoadFromFile (const FileName: string);
procedure SaveToFile (const FileName: string);
procedure Draw (Dest: TBitmap; X: Integer; Y: Integer; Index: Integer);
procedure DrawByKey (Dest: TBitmap; X: Integer; Y: Integer; Index: Integer);
function GetSpriteCount (): Integer;
function GetFrameCount (): Integer;
function GetWidth (): Integer;
function GetHeight (): Integer;
function GetFileName (): String;
function GetIsInit (): Boolean;
procedure GetCurrentPal (var aHighPal: HighPal);
procedure GetOriginalPal (var aHighPal: HighPal);
procedure SetCurrentPal (aHighPal: HighPal);
procedure ResetCurrentPal ();
procedure SaveToTGA ();
procedure DrawToTGA (Dest: TBitmap; X: Integer; Y: Integer; Index: Integer);
procedure Close;
end;
function PosLast (substr1,str1: widestring):Integer;
implementation
procedure TWasFile.Close;
begin
Burn;
Init;
end;
procedure TWasFile.Init ();
begin
Info.wfType := 0;
Info.whSize := 0;
Info.SpriteCount := 0;
Info.FrameCount := 0;
Info.MaxWidth := 0;
Info.MaxHeight := 0;
Info.Key_X := 0;
Info.Key_Y := 0;
Self.FName := Self.FSize := 0;
Self.HeadDataSize := 0;
end;
procedure TWasFile.Burn ();
var
i: Integer;
begin
for i:=Low (Data) to High (Data) do
begin
Data[i].Data := nil;
end;
Data := nil;
FData := nil;
HeadData := nil;
fInitialized := false;
end;
constructor TWasFile.Create ();
begin
Init;
fInitialized := false;
end;
destructor TWasFile.Destroy ();
begin
Burn;
end;
procedure TWasFile.DecodeFromStream (var Dest: TWasImage; Stream: TStream);
var
StartPos: Int64; // 文件在 Stream 中的 Offset
LineOffset: array of Cardinal;
x,y: Integer;
tmp, tmp2: Byte;
alpha: Byte;
i: Integer;
Width, Height: Integer;
begin
StartPos := Stream.Position;
Stream.Read (Dest.Info, SizeOf (Dest.Info));
Width := Dest.Info.Width;
Height := Dest.Info.Height;
LineOffset := nil;
SetLength (LineOffset, Height);
Dest.Data := nil;
SetLength (Dest.Data, Width * Height);
for i:=Low (Dest.Data) to High (Dest.Data) do
begin
// 初始化Data,因为后面有跳过某些字节的操作
Dest.Data[i].Alpha := $0;
Dest.Data[i].Index := $0;
end;
Stream.Read (LineOffset[0], 4 * Height);
for y:=0 to Height-1 do
begin
x := 0;
Stream.Seek (StartPos + LineOffset[y], soFromBeginning);
while (x < Width) do
begin
Stream.Read (tmp, 1);
case (tmp and $C0) of
$00: // 00 - 透明色
begin
if (tmp and $20)>0 then
begin
tmp2 := tmp and $1F; // alpha
Stream.Read (tmp, 1);
Dest.Data[y*Width+x].Index := tmp;
Dest.Data[y*Width+x].Alpha := tmp2;
Inc (x);
end
else if tmp<>0 then
begin
// 重复alpha像素的分支
tmp2 := tmp and $1F; // 次数
Stream.Read (alpha, 1);
alpha := alpha and $1F;
Stream.Read (tmp, 1);
for i:=1 to tmp2 do
begin
Dest.Data[y*Width+x].Alpha := alpha;
Dest.Data[y*Width+x].Index := tmp;
Inc (x);
end;
end
else
begin
// tmp 是$0表示数据段结束
if x>Width then
raise Exception.Create ( x := Width;
end;
end;
$40: // 01 - 像素组
begin
tmp2 := tmp and $3F;
for i:=1 to tmp2 do
begin
Stream.Read (tmp, 1);
Dest.Data[y*Width+x].Alpha := 32;//$1F;
Dest.Data[y*Width+x].Index := tmp;
inc (x);
end;
end;
$80: // 10 - 重复n次
begin
tmp2 := tmp and $3F;
Stream.Read (tmp, 1);
for i:=1 to tmp2 do
begin
Dest.Data[y*Width+x].Alpha := 32;//$1F;
Dest.Data[y*Width+x].Index := tmp;
inc (x);
end;
end;
$C0: // 11 - 跳过n字节
begin
tmp2 := tmp and $3F;
x := x + tmp2;
end;
end; // end of case
if x>Width then
raise Exception.Create ( end; // end of while
end;
LineOffset := nil;
end;
procedure TWasFile.LoadFromFile (const FileName: string);
var
Stream: TStream;
FileOffset: array of Cardinal;
i: Integer;
begin
Stream := TMemoryStream.Create;
try
TMemoryStream (Stream).LoadFromFile (FileName);
Burn;
Init;
Stream.Read (Info, SizeOf (Info));
if Info.wfType <> $5053 then
raise Exception.Create ( if Info.whSize < $0C then
raise Exception.Create ( else if Info.whSize > $0C then
begin
// 当文件头大小大于$0C的时候
HeadDataSize := Info.whSize - $0C;
SetLength (HeadData, HeadDataSize);
Stream.Read (HeadData[0], HeadDataSize);
end;
if (Info.SpriteCount < 1) or (Info.FrameCount < 1) then
raise Exception.Create ( // 开始加载图片
i := Info.SpriteCount * Info.FrameCount;
SetLength (Data, i);
SetLength (FileOffset, i);
Stream.Read (OriginalPal[0], 512); // 高彩调色板
Stream.Read (FileOffset[0], 4*i); // 子文件偏移量
for i:=Low (FileOffset) to High (FileOffset) do
begin
if FileOffset[i]=0 then
begin
// 想不出更合适的办法了
with Data[i] do
begin
Info.Width := 1;
Info.Height := 1;
Info.Key_X := 0;
Info.Key_Y := 0;
SetLength (Data, 1);
Data[0].Alpha := $0;
Data[0].Index := $0;
end;
end
else
begin
Stream.Seek (FileOffset[i]+Info.whSize+4, soFromBeginning);
DecodeFromStream (Data[i], Stream);
end;
end;
FName := FileName;
FSize := Stream.Size - $204 - Info.whSize;
FData := nil;
SetLength (FData, FSize);
Stream.Seek ($4+Info.whSize, soFromBeginning);
Stream.Read (CurrentPal[0], $200);
Stream.Read (FData[0], FSize);
fInitialized := true;
finally
Stream.Free;
end;
end;
procedure TWasFile.DrawByKey (Dest: TBitmap; X: Integer; Y: Integer; Index: Integer);
var
New_X: Integer;
New_Y: Integer;
New_Width: Integer;
New_Height: Integer;
New_Left: Integer;
New_Top: Integer;
i,j: Integer;
pDest: PByteArray;
Color: Word;
Alpha: Byte;
begin
if not fInitialized then
raise Exception.Create ( if (Index<0) or (Index> (Info.SpriteCount * Info.FrameCount)) then
raise Exception.Create ( // 裁减
New_X := X - Data[Index].Info.Key_X;
New_Y := Y - Data[Index].Info.Key_Y;
New_Width := Data[Index].Info.Width;
New_Height := Data[Index].Info.Height;
New_Left := 0;
New_Top := 0;
if (New_X > Dest.Width) or (New_Y > Dest.Height) then
exit;
if (New_X + New_Width) > Dest.Width then
New_Width := Dest.Width - New_X;
if (New_Y + New_Height) > Dest.Height then
New_Height := Dest.Height - New_Y;
if New_X < 0 then
begin
New_Left := -New_X;
New_X := 0;
end;
if New_Y < 0 then
begin
New_Top := -New_Y;
New_Y := 0
end;
for i:=New_Top to New_Height-1 do
begin
pDest := Dest.ScanLine[New_Y+i-New_Top];
Inc (LongWord (pDest), 3*New_X);
for j:=New_Left to New_Width-1 do
begin
Color := CurrentPal[Data[Index].Data[i*Data[Index].Info.Width+j].Index];
Alpha := Data[Index].Data[i*Data[Index].Info.Width+j].Alpha;
pDest[0] := ( ( (Color and $001F) shl 3) - pDest[0]) * Alpha div 32 + pDest[0];
pDest[1] := ( ( (Color and $07E0) shr 3) - pDest[1]) * Alpha div 32 + pDest[1];
pDest[2] := ( ( (Color and $F800) shr 8) - pDest[2]) * Alpha div 32 + pDest[2];
Inc (LongWord (pDest), 3);
end;
end;
end;
procedure TWasFile.Draw (Dest: TBitmap; X: Integer; Y: Integer; Index: Integer);
begin
DrawByKey (Dest, X+Data[Index].Info.Key_X, Y+Data[Index].Info.Key_Y, Index);
end;
procedure TWasFile.SaveToFile (const FileName: string);
var
Stream: TStream;
begin
Stream := TFileStream.Create (FileName, fmCreate);
try
Stream.Write (Info, SizeOf (Info));
Stream.Write (HeadData[0], HeadDataSize);
Stream.Write (CurrentPal[0], $200);
Stream.Write (FData[0], FSize);
finally
Stream.Free;
end;
end;
function TWasFile.GetSpriteCount (): Integer;
begin
Result := Info.SpriteCount;
end;
function TWasFile.GetFrameCount (): Integer;
begin
Result := Info.FrameCount;
end;
function TWasFile.GetWidth (): Integer;
begin
Result := Info.MaxWidth;
end;
function TWasFile.GetHeight (): Integer;
begin
Result := Info.MaxHeight;
end;
function TWasFile.GetFileName (): String;
begin
Result := FName;
end;
function TWasFile.GetIsInit (): Boolean;
begin
Result := fInitialized;
end;
procedure TWasFile.GetCurrentPal (var aHighPal: HighPal);
var
i: Integer;
begin
for i:=0 to 255 do
begin
aHighPal[i] := CurrentPal[i];
end;
end;
procedure TWasFile.GetOriginalPal (var aHighPal: HighPal);
var
i: Integer;
begin
for i:=0 to 255 do
begin
aHighPal[i] := OriginalPal[i];
end;
end;
procedure TWasFile.SetCurrentPal (aHighPal: HighPal);
var
i: Integer;
begin
for i:=0 to 255 do
begin
CurrentPal[i] := aHighPal[i];
end;
end;
procedure TWasFile.ResetCurrentPal ();
var
i: Integer;
begin
for i:=0 to 255 do
begin
CurrentPal[i] := OriginalPal[i];
end;
end;
procedure TWasFile.SaveToTGA ();
var
fs1: TStream;
i,l,x,y: Integer;
path: widestring;
FileName: widestring;
tmp: Byte;
bufbmp: TBitmap;
pDest: PByteArray;
begin
FileName := FName;
i := PosLast ( path := LeftStr (FileName, i-1);
bufbmp := TBitmap.Create;
bufbmp.PixelFormat := pf32bit;
bufbmp.Width := Info.MaxWidth;
bufbmp.Height := Info.MaxHeight;
for i:=Low (Data) to High (Data) do
begin
FileName := path + Format ( fs1 := TFileStream.Create (FileName, fmCreate);
l := $00020000;
fs1.Write (l, 4);
l := $0;
fs1.Write (l, 4);
fs1.Write (l, 4);
fs1.Write (Info.MaxWidth, 2);
fs1.Write (Info.MaxHeight, 2);
tmp := $20;
fs1.Write (tmp, 1);
tmp := $08; // 原点在左下用$08,原点在左上用$28;
fs1.Write (tmp, 1);
// 清空bufbmp
for y:=0 to bufbmp.Height-1 do
begin
pDest := bufbmp.ScanLine[y];
for x:=0 to bufbmp.Width-1 do
begin
pDest[0]:=0;
pDest[1]:=0;
pDest[2]:=0;
pDest[3]:=0;
Inc (LongWord (pDest), 4);
end;
end;
DrawToTGA (bufbmp, Info.Key_X, Info.Key_Y, i);
for y:=bufbmp.Height-1 downto 0 do
begin
pDest := bufbmp.ScanLine[y];
fs1.Write (pDest[0], 4*bufbmp.Width);
end;
fs1.Free;
end;
bufbmp := nil;
end;
function PosLast (substr1,str1: widestring):Integer;
var
str2: widestring;
i,l: Integer;
sum: Integer;
begin
sum := 0;
str2 := str1;
// i := Pos ( i := Pos (substr1, str2);
while i<>0 do
begin
l := Length (str2);
sum := sum + i;
str2 := RightStr (str2, l-i);
i := Pos (substr1, str2);
end;
Result := sum;
end;
// 仅为TGA服务
procedure TWasFile.DrawToTGA (Dest: TBitmap; X: Integer; Y: Integer; Index: Integer);
var
New_X: Integer;
New_Y: Integer;
New_Width: Integer;
New_Height: Integer;
New_Left: Integer;
New_Top: Integer;
i,j: Integer;
pDest: PByteArray;
Color: Word;
Alpha: Byte;
begin
if not fInitialized then
raise Exception.Create ( if (Index<0) or (Index> (Info.SpriteCount * Info.FrameCount)) then
raise Exception.Create ( // 裁减
New_X := X - Data[Index].Info.Key_X;
New_Y := Y - Data[Index].Info.Key_Y;
New_Width := Data[Index].Info.Width;
New_Height := Data[Index].Info.Height;
New_Left := 0;
New_Top := 0;
if (New_X > Dest.Width) or (New_Y > Dest.Height) then
exit;
if (New_X + New_Width) > Dest.Width then
New_Width := Dest.Width - New_X;
if (New_Y + New_Height) > Dest.Height then
New_Height := Dest.Height - New_Y;
if New_X < 0 then
begin
New_Left := -New_X;
New_X := 0;
end;
if New_Y < 0 then
begin
New_Top := -New_Y;
New_Y := 0
end;
for i:=New_Top to New_Height-1 do
begin
pDest := Dest.ScanLine[New_Y+i-New_Top];
Inc (LongWord (pDest), 4*New_X);
for j:=New_Left to New_Width-1 do
begin
Color := CurrentPal[Data[Index].Data[i*Data[Index].Info.Width+j].Index];
Alpha := Data[Index].Data[i*Data[Index].Info.Width+j].Alpha;
if Alpha=0 then
begin
// 0号调色板不一定是黑色,为了photoshop里显示的效果
// 强制写入黑色
pDest[0] := 0;
pDest[1] := 0;
pDest[2] := 0;
pDest[3] := 0;
end
else if Alpha=32 then
begin
pDest[0] := ( (Color and $001F) shl 3) and $F8;
pDest[0] := pDest[0] or ( (pDest[0] shr 3) and 7);
pDest[1] := ( (Color and $07E0) shr 3) and $FC;
pDest[1] := pDest[1] or ( (pDest[1] shr 2) and 3);
pDest[2] := ( (Color and $F800) shr 8) and $F8;
pDest[2] := pDest[2] or ( (pDest[2] shr 3) and 7);
pDest[3] := 255;
end
else
begin
// alpha 像素导出处
// 貌似wascompress是先合并再导出,而不是直接导出,所以发黑
{ pDest[0] := ( ( (Color and $001F) shl 3) * Alpha div 32) and $F8;
pDest[0] := pDest[0] or ( (pDest[0] shr 3) and 7);
pDest[1] := ( ( (Color and $07E0) shr 3) * Alpha div 32) and $FC;
pDest[1] := pDest[1] or ( (pDest[1] shr 2) and 3);
pDest[2] := ( ( (Color and $F800) shr 8) * Alpha div 32) and $F8;
pDest[2] := pDest[2] or ( (pDest[2] shr 3) and 7);}
// 正常的TGA导出
pDest[0] := ( (Color and $001F) shl 3) and $F8;
pDest[0] := pDest[0] or ( (pDest[0] shr 3) and 7);
pDest[1] := ( (Color and $07E0) shr 3) and $FC;
pDest[1] := pDest[1] or ( (pDest[1] shr 2) and 3);
pDest[2] := ( (Color and $F800) shr 8) and $F8;
pDest[2] := pDest[2] or ( (pDest[2] shr 3) and 7);
pDest[3]:=Alpha*8;
end;
Inc (LongWord (pDest), 4);
end;
end;
end;
end.