Windows ICOファイルを変換する(Delphi)
Windows ICOファイルを読み込む事のできるDelphiのソースコードです。クラスになっていますのでそのまま使用できます。
ソースコード
10年以上前に作成した古いコードで私すら覚えていないのですが、何かのお役に立てると嬉しいです。
//Windows Icon(Cursor) R/W Class βバージョン // //Load 1/4/8/15(16)/24/32bit //Save none unit ICOPack; interface uses Windows,Messages,SysUtils,Classes,Graphics; type // 6Byte pIconFileHeader = ^TIconFileHeader; TIconFileHeader = packed record Reserved : Word; // 予約 wType : Word; // リソースタイプ [1 アイコン] [2カーソル] Count : Word; // アイコンの枚数 end; // 16Byte pIconInfoHeader = ^TIconInfoHeader; TIconInfoHeader = packed record Width : Byte; // アイコンの幅 Height : Byte; // アイコンの高さ Colors : Byte; // カラー数 Reserved : Byte; // 予約値 Reserved1 : Word; // アイコンの場合 : 常に1 カーソルの場合 : X ホットスポット Reserved2 : Word; // アイコンの場合 : ビット数 カーソルの場合 : Y ホットスポット DIBSize : DWord; // BitmapInfoHeaderとピクセルデータのサイズ DIBOffset : DWord; // ファイルの先頭からBItmapInfoHeaderまでのオフセット end; type pIconData =^TIconData; TIconData =packed record IsIcon : Word; // リソースタイプ Width : Byte; // 幅 Height : Byte; // 高さ Colors : Byte; // カラー数 BitCount : Byte; // ビット深度 HotSpot : TPoint; // カーソルの場合のみ有効 hBMP : HBitmap; // ビットマップのハンドル FileMem : Pointer; // ファイルアクセス用ポインタ FileSize : Dword; // ファイルのサイズ end; type TRGBA=packed record B,G,R,A : BYTE; end; TRGB=packed record B,G,R : BYTE; end; //24bit用ポインタ pRGB24 = ^TRGB24; TRGB24 = array[0..0] of TRGB; //32bit用ポインタ pRGB32 =^TRGB32; TRGB32 = array[0..0] of TRGBA; type TPaletteData =packed record Count : Word; RGBs : array [0..256] of TRGB; end; { [アイコンファイルフォーマット概要] ----------------------------- IconFileHeader (6Byte) ----------------------------- IconInfoHeader (16Byte) ----------------------------- BitmapInfoHeader (40Byte) ----------------------------- カラーパレット (可変) ----------------------------- カラーデータ (可変) ----------------------------- マスクデータ (可変) 1bitデータ ビットがセットされていると透明になる ----------------------------- 透明色 ... 透明フラグがある場合はその部分が透明になる 反転色 ... 透明フラグがあり色が[白]の場合は反転色になる。 1/4/8bit ... カラーパレットあり 16bit ... カラーパレットなし 32x32x32 RGB 24bit ... カラーパレットなし 256x256x256 RGB 32bit ... 24bitに8bitアルファを付加したもの 複数枚の画像がある場合はこのようになる。 ----------------------------- IconFileHeader (6Byte) ----------------------------- IconInfoHeader (16Byte) 1枚目 ----------------------------- IconInfoHeader (16Byte) 2枚目 ----------------------------- IconInfoHeader (16Byte) ・・・ ----------------------------- IconInfoHeader (16Byte) N 枚目 ----------------------------- この後にBitmapInfoHeaderは続くのでファイルポインタをSeekする必要がある。 } { TIconImageEx } Type TIconImageEx = class(TBitmap) private PaletteData : TPaletteData; FUsesAlphlaBlend : Boolean; FLoad24bitBitmap : Boolean; FBackColor : Dword; FHostSpot : TPoint; FIsIcon : Boolean; FNotColor : Boolean; FFileCount : Word; function SeachColorIndex(R,G,B : Byte):Byte; procedure CreateBitmapPalette(Stream : TStream; PalSize : WORD; BMP :TBitmap); function CreateXorBitmap(Stream : TStream; BitsSize : Dword;BI:TBitmapInfoHeader):HBitmap; function CreateAndBitmap(Stream: TStream; MaskSize : Dword;BI:TBitmapInfoHeader):HBitmap; function AlphlaBlend32(hXor,hAnd: HBitmap; R,G,B : Byte):HBitmap; public constructor Create; override; destructor Destroy; override; procedure LoadFromStream(Stream: TStream); override; procedure SaveToStream(Stream: TStream); override; published property HostSpot : TPoint read FHostSpot; // ホットスポット property IsIcon : Boolean read FIsIcon; // アイコンイメージ? property FileCount : Word read FFileCount; // ファイル数 property NotColor : Boolean read FNotColor ; // 反転色があるかどうか?(Load24bitBitmapがTrueになっている場合のみ有効) property UsesAlphlaBlend : Boolean read FUsesAlphlaBlend write FUsesAlphlaBlend; property BackColor : Dword read FBackColor write FBackColor; property Load24bitBitmap : Boolean read FLoad24bitBitmap write FLoad24bitBitmap; end; type TIconEx = class(TComponent) private FStretchMode :Boolean; procedure WriteIcon(Stream: TStream; Icon: HICON; WriteLength: Boolean); function BytesPerScanline(PixelsPerScanline, BitsPerPixel,Alignment: Integer): Longint; procedure CheckBool(Result: Bool); procedure InitializeBitmapInfoHeader(Bitmap: HBITMAP; var BI: TBitmapInfoHeader; Colors: Integer); function InternalGetDIB(Bitmap: HBITMAP; Palette: HPALETTE; var BitmapInfo; var Bits; Colors: Integer): Boolean; procedure InternalGetDIBSizesA(Bitmap: HBITMAP; var InfoHeaderSize,ImageSize: DWORD; Colors: Integer); procedure InvalidBitmap; procedure WinError; protected public function CreateIcon(ABitmap:TBitmap):TIcon; procedure SaveToFileIcon16(AIcon:Ticon;Afile:string); procedure SaveToFileIcon256(AIcon:Ticon;Afile:string); property StretchMode : Boolean read FStretchMode write FStretchMode; published end; implementation //////// function Set255(Value :Integer) : Byte; begin if Value>255 then begin Result:=255; Exit; end else if Value<0 then begin Result:=0; exit; end; Result:=Value; end; ///////// 1bitマスクを24bit化して透過フラグを取得 procedure GetMask24Bits(hBMP : HBitmap; Var Source : PByteArray); Var BMP : TBitmap; Dest24 : pRGB24; X,Y,BitsCnt : Dword; begin BMP :=TBitmap.Create; Try BMP.handle:=hBMP; BitsCnt:=0; BMP.pixelformat:=pf24bit; for Y := 0 to BMP.Height-1 do begin Dest24 :=BMP.ScanLine[Y]; for X := 0 to bmp.Width - 1 do begin Source[BitsCnt]:=Dest24[x].B and $1; inc(BitsCnt); end; end; finally BMP.free; end; end; //////// 4の倍数(?)対策(ビットマップの幅を取得) function GetLineWidth(BitCount,Width : WORD ):DWORD; Var Dummy :DWORD; begin Dummy :=0; case BitCount of 1 : Dummy :=((Width + 7) div 8) ; 4 : Dummy :=((Width + 7) div 8) shl 2; 8 : Dummy :=Width; end; if(Dummy and $003)<>0 then Dummy := (Dummy or 3) + 1; Result :=Dummy; end; // 以下16bitファイルのビット操作 //function GetRValueW(Value : WORD):Byte; begin Result:=(Value AND $001F);end; //function GetGValueW(Value : WORD):Byte; begin Result:=(Value AND $03E0) shr 5;end; //function GetBValueW(Value : WORD):Byte; begin Result:=(Value AND $7C00) shr 10;end; function ColorSpace32(R,G,B :Byte):Word;begin Result:=(((B) shr 3) shl 10) or (((G) shr 3) shl 5) or ((R) shr 3);end; { ---------- } { TIconImageEx } { ---------- } constructor TIconImageEx.Create; begin inherited Create; ZeroMemory(@PaletteData,sizeof(TPaletteData)); FLoad24bitBitmap :=False; FUsesAlphlaBlend :=False; FIsIcon :=False; FNotColor :=False; FFileCount :=0; FHostSpot.x :=0; FHostSpot.y :=0; FBackColor :=RGB(255,255,255); end; destructor TIconImageEx.Destroy; begin inherited Destroy; end; function TIconImageEx.SeachColorIndex(R,G,B : Byte):Byte; Var i :Integer; begin // パレットから指定されたカラーのインデックス値を検索 Result :=0; for i:= 0 to PaletteData.Count -1 do begin if (PaletteData.RGBs[i].B =B) and (PaletteData.RGBs[i].G =G) and (PaletteData.RGBs[i].R =R) then begin Result :=i; Exit; end; end; end; procedure TIconImageEx.CreateBitmapPalette(Stream : TStream; PalSize : WORD; BMP :TBitmap); var i: Integer; LogPalette: TMaxLogPalette; RGBQUA :array [0..256] of TRGBQUAD; begin //パレットの読み込み&作成 FillChar(LogPalette, SizeOf(LogPalette), 0); LogPalette.palVersion := $300; ZeroMemory(@RGBQUA ,sizeof(TRGBQUAD)*256); Stream.Read(RGBQUA,PalSize*4); LogPalette.palNumEntries := PalSize; // キャッシュに格納 PaletteData.Count := PalSize; for i := 0 to PalSize-1 do begin LogPalette.palPalEntry[i].peBlue := RGBQUA[i].rgbBlue; LogPalette.palPalEntry[i].peGreen := RGBQUA[i].rgbGreen; LogPalette.palPalEntry[i].peRed := RGBQUA[i].rgbRed; // キャッシュに格納 PaletteData.RGBs[i].B:= RGBQUA[i].rgbBlue; PaletteData.RGBs[i].G:= RGBQUA[i].rgbGreen; PaletteData.RGBs[i].R:= RGBQUA[i].rgbRed; end; BMP.Palette := CreatePalette(PLogPalette(@LogPalette)^); end; function TIconImageEx.CreateXorBitmap(Stream : TStream; BitsSize : Dword;BI:TBitmapInfoHeader):HBitmap; Var x,y :Integer; SrcRow,DestRow : pByte; Dest16 : pWord; Dest24 : pRGB24; Dest32 : pRGB32; SrcMem : Pointer; BMP : TBitmap; MemoryStream : TMemoryStream; LineWidth : DWord; begin // XorBitmap作成 BMP :=TBitmap.Create; // ビット深度等の設定 case BI.biBitCount of 1 : BMP.PixelFormat := pf1Bit; 4 : BMP.PixelFormat := pf4Bit; 8 : BMP.PixelFormat := pf8Bit; 16 : BMP.PixelFormat := pf15Bit; 24 : BMP.PixelFormat := pf24Bit; 32 : BMP.PixelFormat := pf32Bit; end; BMP.Width := BI.biWidth; BMP.Height := BI.biHeight; // パレットの構築 if BI.biClrUsed<>0 then CreateBitmapPalette(Stream,BI.biClrUsed,BMP); //SrcRow MemoryStream:=TMemoryStream.create; Try MemoryStream.CopyFrom(Stream,BitsSize); MemoryStream.Position:=0; GetMem(SrcMem,BitsSize); Move(MemoryStream.Memory^,SrcMem^,BitsSize); finally MemoryStream.Free; end; try SrcRow:=PByte(SrcMem); if BI.biBitCount in [1,4,8] then begin // 横幅のサイズ(Byte単位)を算出 LineWidth := GetLineWidth(BI.biBitCount,BI.biWidth); for Y := BMP.Height-1 downto 0 do begin DestRow :=BMP.ScanLine[Y]; Move(SrcRow^,DestRow^, LineWidth); inc(SrcRow,LineWidth); end; end else if BI.biBitCount=16 then begin for Y := BMP.Height-1 downto 0 do begin Dest16 :=BMP.ScanLine[Y]; Move(SrcRow^,Dest16^,BMP.Width*2); inc(SrcRow,BMP.Width*2); end; end else if BI.biBitCount=24 then begin for Y := BMP.Height-1 downto 0 do begin Dest24 :=BMP.ScanLine[Y]; for X := 0 to BMP.width-1 do begin Dest24[x].B :=SrcRow^; inc(SrcRow); Dest24[x].G :=SrcRow^; inc(SrcRow); Dest24[x].R :=SrcRow^; inc(SrcRow); end; end; end else if BI.biBitCount=32 then begin for Y := BMP.Height-1 downto 0 do begin Dest32 :=BMP.ScanLine[Y]; for X := 0 to BMP.width-1 do begin Dest32[x].B :=SrcRow^ ;inc(SrcRow); Dest32[x].G :=SrcRow^ ;inc(SrcRow); Dest32[x].R :=SrcRow^ ;inc(SrcRow); // αチャンネル(0 は完全な透明、255 は完全な不透明) Dest32[x].A :=SrcRow^;inc(SrcRow); end; end; end; Result:=BMP.ReleaseHandle; finally BMP.Free; FreeMem(SrcMem); end; end; ///////// マスクビットマップの作成 function TIconImageEx.CreateAndBitmap(Stream: TStream; MaskSize : Dword;BI:TBitmapInfoHeader):HBitmap; Var y :Integer; MaskRow,DestRow : pByte; MaskMem : Pointer; BMP :TBitmap; MemoryStream : TMemoryStream; LineWidth : DWord; LogPalette: TMaxLogPalette; begin //MaskRow MemoryStream:=TMemoryStream.create; MemoryStream.CopyFrom(Stream,MaskSize); MemoryStream.Position:=0; GetMem(MaskMem,MaskSize); Move(MemoryStream.Memory^,MaskMem^,MaskSize); MaskRow:=PByte(MaskMem); MemoryStream.Free; BMP :=TBitmap.Create; BMP.PixelFormat := pf1Bit; BMP.Width := BI.biWidth; BMP.Height := BI.biHeight; // パレットを構築 ZeroMemory(@LogPalette, SizeOf(LogPalette)); LogPalette.palVersion := $300; LogPalette.palNumEntries := 2; LogPalette.palPalEntry[1].peBlue := 255; LogPalette.palPalEntry[1].peGreen := 255; LogPalette.palPalEntry[1].peRed := 255; BMP.Palette := CreatePalette(PLogPalette(@LogPalette)^); try // 横幅のサイズ(Byte単位)を算出 LineWidth := GetLineWidth(1,BI.biWidth); // データを格納 for Y := BMP.Height-1 downto 0 do begin DestRow :=BMP.ScanLine[Y]; Move(MaskRow^,DestRow^, LineWidth); inc(MaskRow,LineWidth); end; Result:=BMP.ReleaseHandle finally BMP.Free; FreeMem(MaskMem); end; end; ///////// function TIconImageEx.AlphlaBlend32(hXor,hAnd: HBitmap; R,G,B : Byte):HBitmap; Var XorBitmap,AndBitmap:TBitmap; MaskMem : Pointer; MaskRow : PByteArray; Dest8 : PByteArray; Dest16 : PWordArray; Dest24 : pRGB24; Dest32 : pRGB32; x,y,i : Dword; PalNo,Bytes :Byte; Sub,MaskCnt : Dword; amount : Extended; begin // ブレンド XorBitmap :=TBitmap.Create; AndBitmap :=TBitmap.Create; XorBitmap.Handle:= hXor; AndBitmap.Handle:= hAnd; GetMem(MaskMem,XorBitmap.Width*XorBitmap.Height); MaskRow :=PByteArray(Maskmem); Try // マスクビットを取得 GetMask24Bits(AndBitmap.ReleaseHandle,MaskRow); MaskCnt:=0; if XorBitmap.PixelFormat = pf1bit then begin // 1bitの場合はパレットは黒->白の順になっているはずなので「xor」だけで可能なはず。 for Y := 0 to XorBitmap.Height-1 do begin Dest8 :=XorBitmap.ScanLine[Y]; for X := 0 to (XorBitmap.width div 8)-1 do begin Bytes :=0; for i:= 1 to 8 do begin Bytes :=Bytes or (MaskRow[MaskCnt] shl (8-i)); inc(MaskCnt); end; Dest8[x] :=Dest8[x] xor Bytes; end; // 端数が出る場合があるので修正する。 Sub:= XorBitmap.width - ((XorBitmap.width div 8) * 8) ; if ( Sub<>0 )then begin Bytes :=0; // 端数が出る場合は1-7までしかでない。 for i:= 1 to sub do begin Bytes :=Bytes or (MaskRow[MaskCnt] shl (8-i)); inc(MaskCnt); end; Dest8[(XorBitmap.width div 8)] :=Dest8[(XorBitmap.width div 8)] xor Bytes; end; end; end else if XorBitmap.PixelFormat = pf4bit then begin // パレット番号 palNo:=SeachColorIndex(R,G,B); for Y := 0 to XorBitmap.Height-1 do begin Dest8 :=XorBitmap.ScanLine[Y]; for x := 0 to (XorBitmap.width div 2)-1 do begin // 上位4bit if MaskRow[MaskCnt]<>0 then Dest8[x] := ((palNo shl 4) and $F0) or (Dest8[x] and $0F); inc(MaskCnt); // 下位4bit if MaskRow[MaskCnt]<>0 then Dest8[x] := ((palNo) and $0F) or (Dest8[x] and $F0); inc(MaskCnt); end; // 端数が出る場合があるので修正する。 Sub:= XorBitmap.width - ((XorBitmap.width div 2) * 2) ; if ( Sub<>0 )then begin if MaskRow[MaskCnt]<>0 then begin // 端数が出る場合は必ず上位4bitしかでない。 Dest8[(XorBitmap.width div 2)] := ((palNo shl 4) and $F0) or (Dest8[(XorBitmap.width div 2)] and $00); end; inc(MaskCnt); end; end; end else if XorBitmap.PixelFormat = pf8bit then begin // パレット番号 palNo:=SeachColorIndex(R,G,B); for Y := 0 to XorBitmap.Height-1 do begin Dest8 :=XorBitmap.ScanLine[Y]; for X := 0 to XorBitmap.width-1 do begin if MaskRow[MaskCnt]<>0 then begin Dest8[x] :=palNo; end; inc(MaskCnt); end; end; end else if XorBitmap.PixelFormat = pf15bit then begin for Y := 0 to XorBitmap.Height-1 do begin Dest16 :=XorBitmap.ScanLine[Y]; for X := 0 to XorBitmap.width-1 do begin if MaskRow[MaskCnt]<>0 then Dest16[x] :=ColorSpace32(R,G,B); inc(MaskCnt); end; end; end else if XorBitmap.PixelFormat = pf24bit then begin for Y := 0 to XorBitmap.Height-1 do begin Dest24 :=XorBitmap.ScanLine[Y]; for X := 0 to XorBitmap.width-1 do begin if MaskRow[MaskCnt]<>0 then begin // 透明フラグがあり色が[白]の場合は反転色になる。 if ((Dest24[x].B=255) and (Dest24[x].G=255) and (Dest24[x].R=255)) then FNotColor :=True; Dest24[x].B := B; Dest24[x].G := G; Dest24[x].R := R; end; inc(MaskCnt); end; end; end else if XorBitmap.PixelFormat = pf32bit then begin if FUsesAlphlaBlend then begin for Y := 0 to XorBitmap.Height-1 do begin Dest32 :=XorBitmap.ScanLine[Y]; for X := 0 to XorBitmap.width-1 do begin // アルファブレンド(イメージの合成方法が若干、違うかも) amount := Dest32[x].A / 255; Dest32[x].R :=set255(Round ( ((1-amount)*R)+(amount*Dest32[x].R))); Dest32[x].G :=set255(Round ( ((1-amount)*G)+(amount*Dest32[x].G))); Dest32[x].B :=set255(Round ( ((1-amount)*B)+(amount*Dest32[x].B))); // 背景色の設定 if MaskRow[MaskCnt]<>0 then begin Dest32[x].R :=R; Dest32[x].G :=G; Dest32[x].B :=B; end; inc(MaskCnt); end; end; end else begin for Y := 0 to XorBitmap.Height-1 do begin Dest32 :=XorBitmap.ScanLine[Y]; for X := 0 to XorBitmap.width-1 do begin // 透明な色は透明にするだけ if Dest32[x].A=0 then begin Dest32[x].R :=R; Dest32[x].G :=G; Dest32[x].B :=B; end; // 背景色の設定 if MaskRow[MaskCnt]<>0 then begin Dest32[x].R :=R; Dest32[x].G :=G; Dest32[x].B :=B; end; inc(MaskCnt); end; end; end; end; Result:=XorBitmap.ReleaseHandle; finally FreeMem(MaskMem) ; XorBitmap.Free; AndBitmap.Free; end; end; /////// procedure TIconImageEx.LoadFromStream(Stream: TStream); Var IconFileHeader : TIconFileHeader; IconInfoHeader : TIconInfoHeader; BI : TBitmapInfoHeader; XorBitmap,AndBitmap :TBitmap; MskBitsSize,BitsSize :DWord; begin // 初期化 ZeroMemory(@PaletteData,sizeof(TPaletteData)); ZeroMemory(@IconFileHeader,sizeof(TIconFileHeader)) ; ZeroMemory(@IconInfoHeader,sizeof(TIconInfoHeader)) ; ZeroMemory(@BI,sizeof(TBitmapInfoHeader)) ; FNotColor :=False; FHostSpot.x :=0; FHostSpot.y :=0; // ファイルヘッダーの取得 Stream.Read(IconFileHeader,sizeof(TIconFileHeader)); Stream.Read(IconInfoHeader,sizeof(TIconInfoHeader)); // アイコンかカーソル if IconFileHeader.wType =1 then FIsIcon :=True else if IconFileHeader.wType =2 then begin FIsIcon :=False; FHostSpot.x :=IconInfoHeader.Reserved1; FHostSpot.y :=IconInfoHeader.Reserved2; end else raise Exception.Create('このファイルはアイコン/カーソルではありません。'); FFileCount :=IconFileHeader.Count; // 複数毎のイメージが格納されている可能性があるので注意が必要。 Stream.Position:=IconInfoHeader.DIBOffset; Stream.Read(BI,sizeof(TBitmapInfoHeader)); // BitmapInfoHeader.biHeightにはマスク分のデータも入っているで変更 BI.biWidth := IconInfoHeader.Width; BI.biHeight := IconInfoHeader.Height; // マスクデータと画像データのサイズを計算 MskBitsSize := Cardinal(((1 * BI.biWidth + 31) div 32) * 4 * abs(BI.biHeight)) ; BitsSize := Cardinal(((BI.biBitCount * BI.biWidth + 31) div 32) * 4 * abs(BI.biHeight)) ; // パレットのサイズが違うので修正する。 Case BI.biBitCount of 1 : BI.biClrUsed:= 2; 4 : BI.biClrUsed:= 16; 8 : BI.biClrUsed:= 256; end; XorBitmap := TBitmap.Create; AndBitmap := TBitmap.Create; Try // Xor Bitmap取得 XorBitmap.Handle:= CreateXorBitmap(Stream,BitsSize,BI); // AND Bitmap取得 AndBitmap.Handle:= CreateAndBitmap(Stream,MskBitsSize,BI); // Xor BitmapとAND Bitmapを合成 if FLoad24bitBitmap then if XorBitmap.PixelFormat <>pf32bit then XorBitmap.PixelFormat :=pf24bit; Self.handle := AlphlaBlend32(XorBitmap.ReleaseHandle,AndBitmap.ReleaseHandle, GetRValue(FBackColor),GetGValue(FBackColor), GetBValue(FBackColor)); finally XorBitmap.free; AndBitmap.Free; end; end; ///////// procedure TIconImageEx.SaveToStream(Stream: TStream); begin // none end; //////////////////////////////////////////////////////////////////////////////// { TIconEx } //////// procedure TIconEx.SaveToFileIcon16(AIcon:Ticon;Afile:string); begin AIcon.SaveToFile (Afile); end; ///////// procedure TIconEx.SaveToFileIcon256(AIcon:Ticon;Afile:string); var fs:TFilestream; begin fs:=Tfilestream.Create (afile, fmcreate or fmopenwrite); writeIcon(fs,AIcon.handle,false); fs.Free; end; //////// function TIconEx.CreateIcon(ABitmap:TBitmap):TIcon; var IconSizeX : integer; IconSizeY : integer; XOrMask : TBitmap; MonoMask:TBitmap; BlackMask:TBitmap; IconInfo : TIconInfo; R:trect; transcolor:Tcolor; begin {Get the icon size} IconSizeX := GetSystemMetrics(SM_CXICON); IconSizeY := GetSystemMetrics(SM_CYICON); R:=Rect(0, 0, IconSizeX, IconSizeY); {Create the "XOr" mask} XOrMask := TBitmap.Create; XOrMask.Width := IconSizeX; XOrMask.Height := IconSizeY; {stretchdraw mypaint} XorMask.canvas.draw(0,0,Abitmap); //透明色の設定 transcolor:=XorMask.Canvas.Pixels [0,IconSizeY-1]; {Create the Monochrome mask} MonoMask := TBitmap.Create; MonoMask.Width := IconSizeX; MonoMask.Height := IconSizeY; MonoMask.Canvas.Brush.Color := Clwhite; MonoMask.Canvas.FillRect(R); {Create the Black mask} BlackMask := TBitmap.Create; BlackMask.Width := IconSizeX; BlackMask.Height := IconSizeY; {if black is not the transcolor we must replace black with a temporary color} if transcolor<>clblack then begin BlackMask.Canvas.Brush.Color := $F8F9FA; BlackMask.Canvas.FillRect(R); BlackMask.canvas.BrushCopy(R,XorMask,R,clblack); XorMask.Assign (BlackMask); end; {now make the black mask} BlackMask.Canvas.Brush.Color := Clblack; BlackMask.Canvas.FillRect(R); {draw the XorMask with brushcopy} BlackMask.canvas.BrushCopy(R,XorMask,R,transcolor); XorMask.Assign (BlackMask); {Assign and draw the mono mask} XorMask.Transparent:=true; // XorMask.TransparentColor :=transcolor; XorMask.TransparentColor :=clblack; MonoMask.Canvas.draw(0,0,XorMask); MonoMask.canvas.copymode:=cmsrcinvert; MonoMask.canvas.CopyRect (R,XorMask.canvas,R); MonoMask.monochrome:=true; // XorMask.transparent:=false; {restore the black color in the image} BlackMask.Canvas.Brush.Color := Clblack; BlackMask.Canvas.FillRect(R); BlackMask.canvas.BrushCopy(R,XorMask,R,$F8F9FA); XorMask.Assign (BlackMask); {Create a icon} result := TIcon.Create; IconInfo.fIcon := true; IconInfo.xHotspot := 0; IconInfo.yHotspot := 0; IconInfo.hbmMask := MonoMask.Handle; IconInfo.hbmColor := XOrMask.Handle; result.Handle := CreateIconIndirect(IconInfo); {Destroy the temporary bitmaps} XOrMask.Free; MonoMask.free; BlackMask.free; end; ///////// procedure TIconEx.WinError; begin end; //////// procedure TIconEx.CheckBool(Result: Bool); begin if not Result then WinError; end; //////// function TIconEx.BytesPerScanline(PixelsPerScanline, BitsPerPixel, Alignment: Longint): Longint; begin Dec(Alignment); Result := ((PixelsPerScanline * BitsPerPixel) + Alignment) and not Alignment; Result := Result div 8; end; //////// procedure TIconEx.InvalidBitmap; begin raise Exception.Create('失敗しちゃった!!'); end; //////// procedure TIconEx.InitializeBitmapInfoHeader(Bitmap: HBITMAP; var BI: TBitmapInfoHeader; Colors: Integer); var DS: TDIBSection; Bytes: Integer; begin DS.dsbmih.biSize := 0; Bytes := GetObject(Bitmap, SizeOf(DS), @DS); if Bytes = 0 then InvalidBitmap else if (Bytes >= (sizeof(DS.dsbm) + sizeof(DS.dsbmih))) and (DS.dsbmih.biSize >= DWORD(sizeof(DS.dsbmih))) then BI := DS.dsbmih else begin FillChar(BI, sizeof(BI), 0); with BI, DS.dsbm do begin biSize := SizeOf(BI); biWidth := bmWidth; biHeight := bmHeight; end; end; if Colors <> 0 then case Colors of 2: BI.biBitCount := 1; 16: BI.biBitCount := 4; 256: BI.biBitCount := 8; end else BI.biBitCount := DS.dsbm.bmBitsPixel * DS.dsbm.bmPlanes; BI.biPlanes := 1; if BI.biSizeImage = 0 then BI.biSizeImage := BytesPerScanLine(BI.biWidth, BI.biBitCount, 32) * Abs(BI.biHeight); end; ///////// procedure TIconEx.InternalGetDIBSizesA(Bitmap: HBITMAP; var InfoHeaderSize: DWORD; var ImageSize: DWORD; Colors: Integer); var BI: TBitmapInfoHeader; begin InitializeBitmapInfoHeader(Bitmap, BI, Colors); if BI.biBitCount > 8 then begin InfoHeaderSize := SizeOf(TBitmapInfoHeader); if (BI.biCompression and BI_BITFIELDS) <> 0 then Inc(InfoHeaderSize, 12); end else InfoHeaderSize := SizeOf(TBitmapInfoHeader) + SizeOf(TRGBQuad) * (1 shl BI.biBitCount); ImageSize := BI.biSizeImage; end; //////// function TIconEx.InternalGetDIB(Bitmap: HBITMAP; Palette: HPALETTE; var BitmapInfo; var Bits; Colors: Integer): Boolean; var OldPal: HPALETTE; DC: HDC; begin InitializeBitmapInfoHeader(Bitmap, TBitmapInfoHeader(BitmapInfo), Colors); OldPal := 0; DC := CreateCompatibleDC(0); try if Palette <> 0 then begin OldPal := SelectPalette(DC, Palette, False); RealizePalette(DC); end; Result := GetDIBits(DC, Bitmap, 0, TBitmapInfoHeader(BitmapInfo).biHeight, @Bits, TBitmapInfo(BitmapInfo), DIB_RGB_COLORS) <> 0; finally if OldPal <> 0 then SelectPalette(DC, OldPal, False); DeleteDC(DC); end; end; ///////// procedure TIconEx.WriteIcon(Stream: TStream; Icon: HICON; WriteLength: Boolean); type TCursorOrIcon = packed record Reserved: Word; wType: Word; Count: Word; end; PIconRec = ^TIconRec; TIconRec = packed record Width: Byte; Height: Byte; Colors: Word; Reserved1: Word; Reserved2: Word; DIBSize: Longint; DIBOffset: Longint; end; var IconInfo: TIconInfo; MonoInfoSize, ColorInfoSize: DWORD; MonoBitsSize, ColorBitsSize: DWORD; MonoInfo, MonoBits, ColorInfo, ColorBits: Pointer; CI: TCursorOrIcon; List: TIconRec; Length: Longint; begin FillChar(CI, SizeOf(CI), 0); FillChar(List, SizeOf(List), 0); CheckBool(GetIconInfo(Icon, IconInfo)); try InternalGetDIBSizesA(IconInfo.hbmMask, MonoInfoSize, MonoBitsSize, 2); InternalGetDIBSizesA(IconInfo.hbmColor, ColorInfoSize, ColorBitsSize, 256); MonoInfo := nil; MonoBits := nil; ColorInfo := nil; ColorBits := nil; try MonoInfo := AllocMem(MonoInfoSize); MonoBits := AllocMem(MonoBitsSize); ColorInfo := AllocMem(ColorInfoSize); ColorBits := AllocMem(ColorBitsSize); InternalGetDIB(IconInfo.hbmMask, 0, MonoInfo^, MonoBits^, 2); InternalGetDIB(IconInfo.hbmColor, 0, ColorInfo^, ColorBits^, 256); if WriteLength then begin Length := SizeOf(CI) + SizeOf(List) + ColorInfoSize + ColorBitsSize + MonoBitsSize; Stream.Write(Length, SizeOf(Length)); end; with CI do begin CI.wType := 1; CI.Count := 1; end; Stream.Write(CI, SizeOf(CI)); with List, PBitmapInfoHeader(ColorInfo)^ do begin Width := biWidth; Height := biHeight; if (biBitCount = 1) then begin Colors := 2; end else if (biBitCount = 4) then begin Colors := 16; end else begin Colors := 0; end; Reserved1 := 1; Reserved2 := biBitCount; DIBSize := ColorInfoSize + ColorBitsSize + MonoBitsSize; DIBOffset := SizeOf(CI) + SizeOf(List); end; Stream.Write(List, SizeOf(List)); with PBitmapInfoHeader(ColorInfo)^ do Inc(biHeight, biHeight); { color height includes mono bits } Stream.Write(ColorInfo^, ColorInfoSize); Stream.Write(ColorBits^, ColorBitsSize); ZeroMEmory(MonoBits,MonoBitsSize) ; Stream.Write(MonoBits^, MonoBitsSize); finally FreeMem(ColorInfo, ColorInfoSize); FreeMem(ColorBits, ColorBitsSize); FreeMem(MonoInfo, MonoInfoSize); FreeMem(MonoBits, MonoBitsSize); end; finally DeleteObject(IconInfo.hbmColor); DeleteObject(IconInfo.hbmMask); end; end; end.
2016年1月26日追記
現在、JavaScriptでアイコンファイルを作成 & 編集するクラスを作成していますが、このDelphiのソースは汚すぎるので完成したらJavascriptバージョンに差し替える予定です。(他のコードも雑ですが・・・。)
スポンサーリンク
関連記事
公開日:2015年01月07日 最終更新日:2016年01月26日
記事NO:00087