{***********************************************************} { PngTBXOfficeXPTheme } { A PNG-aware version of the TBX OfficeXP theme } { } { Copyright (c) 2004 Martijn Saly } {***********************************************************} unit PngTBXOfficeXPTheme; interface uses Windows, Graphics, ImgList, TB2Item, TBXUtils, TBXThemes, TBXOfficeXPTheme, PngImageList, PngFunctions, pngimage; type TPngTBXOfficeXPTheme = class(TTBXOfficeXPTheme) public procedure PaintImage(Canvas: TCanvas; ARect: TRect; const ItemInfo: TTBXItemInfo; ImageList: TCustomImageList; ImageIndex: Integer); override; end; implementation const ThemeName = 'OfficeXP'; { Globals } procedure HighlightTBXIcon(Canvas: TCanvas; const R: TRect; ImageList: TCustomImageList; ImageIndex: Integer; HighlightColor: TColor; Amount: Byte); var ImageWidth, ImageHeight, X, Y: Integer; DestPng, Png: TPNGObject; W1, W2: Byte; CBRB, CBG, S, C: Cardinal; Assigner: TBitmap; TransparencyColor: TColor; Line: PByteArray; begin //If the imagelist is not the PngImageList, then invoke the default call. if not (ImageList is TPngImageList) then begin TBXUtils.HighlightTBXIcon(Canvas, R, ImageList, ImageIndex, HighlightColor, Amount); Exit; end; //Get the size of the image ImageWidth := R.Right - R.Left; ImageHeight := R.Bottom - R.Top; with ImageList do begin if Width < ImageWidth then ImageWidth := Width; if Height < ImageHeight then ImageHeight := Height; end; //Recieve a copy of the image in the imagelist Png := TPngImageList(ImageList).PngImages[ImageIndex].PngImage; DestPng := TPNGObject.Create; try //Create a new PNG by assigning a TBitmap Assigner := TBitmap.Create; try Assigner.Width := Png.Width; Assigner.Height := Png.Height; DestPng.Assign(Assigner); finally Assigner.Free; end; //Copy alpha channel, if available if Png.Header.ColorType in [COLOR_GRAYSCALEALPHA, COLOR_RGBALPHA] then begin DestPng.CreateAlpha; for Y := 0 to Png.Height - 1 do CopyMemory(DestPng.AlphaScanline[Y], Png.AlphaScanline[Y], Png.Width); end else if Png.TransparencyMode = ptmBit then begin TransparencyColor := Png.TransparentColor; DestPng.CreateAlpha; for Y := 0 to Png.Height - 1 do begin Line := DestPng.AlphaScanline[Y]; for X := 0 to Png.Width - 1 do if Png.Pixels[X, Y] = TransparencyColor then Line^[X] := 0 else Line^[X] := 255; end; end; //Initialize variables that help generate the lighted icon W2 := Amount; W1 := 255 - W2; HighlightColor := GetBGR(ColorToRGB(HighlightColor)); CBRB := (Cardinal(HighlightColor) and $00FF00FF) * W1; CBG := (Cardinal(HighlightColor) and $0000FF00) * W1; //Loop through every pixel for Y := 0 to ImageHeight - 1 do for X := 0 to ImageWidth - 1 do begin //Lighten a pixel, basically the same way as in the original OfficeXP theme, //only this function preserves the alpha channel S := Png.Pixels[X, Y]; C := ((S and $FF00FF) * W2 + CBRB) and $FF00FF00 + ((S and $00FF00) * W2 + CBG) and $00FF0000; DestPng.Pixels[X, Y] := C shr 8; end; DestPng.Draw(Canvas, R); finally DestPng.Free; end; end; procedure DrawTBXIconFullShadow(Canvas: TCanvas; const R: TRect; ImageList: TCustomImageList; ImageIndex: Integer; ShadowColor: TColor); function Perform_DSPDxax(const Source, Dest, Brush: Byte): Byte; begin //It was neccesary to translate the ternary raster operation ROP_DSPDxax into //normal code, because it's not using a Blt function. //See also: http://msdn.microsoft.com/library/en-us/gdi/pantdraw_6n77.asp Result := Dest xor Brush and Source xor Dest; end; var Assigner: TBitmap; ImageWidth, ImageHeight, X, Y: Integer; DestPng, Png: TPNGObject; DestColor, TransparencyColor: TColor; BrushR, BrushG, BrushB, DestR, DestG, DestB: Byte; Line: PByteArray; begin //If the imagelist is not the PngImageList, then invoke the default call. if not (ImageList is TPngImageList) then begin TBXUtils.DrawTBXIconFullShadow(Canvas, R, ImageList, ImageIndex, ShadowColor); Exit; end; //Get the size of the image ImageWidth := R.Right - R.Left; ImageHeight := R.Bottom - R.Top; with ImageList do begin if Width < ImageWidth then ImageWidth := Width; if Height < ImageHeight then ImageHeight := Height; end; //Recieve a copy of the image in the imagelist Png := TPngImageList(ImageList).PngImages[ImageIndex].PngImage; DestPng := TPNGObject.Create; try //Create a new PNG by assigning a TBitmap Assigner := TBitmap.Create; try Assigner.Width := Png.Width; Assigner.Height := Png.Height; DestPng.Assign(Assigner); finally Assigner.Free; end; //Copy alpha channel, if available if Png.Header.ColorType in [COLOR_GRAYSCALEALPHA, COLOR_RGBALPHA] then begin DestPng.CreateAlpha; for Y := 0 to Png.Height - 1 do CopyMemory(DestPng.AlphaScanline[Y], Png.AlphaScanline[Y], Png.Width); end else if Png.TransparencyMode = ptmBit then begin TransparencyColor := Png.TransparentColor; DestPng.CreateAlpha; for Y := 0 to Png.Height - 1 do begin Line := DestPng.AlphaScanline[Y]; for X := 0 to Png.Width - 1 do if Png.Pixels[X, Y] = TransparencyColor then Line^[X] := 0 else Line^[X] := 255; end; end; //BrushX values for the raster opration BrushR := ShadowColor and $FF; BrushG := ShadowColor shr 8 and $FF; BrushB := ShadowColor shr 16 and $FF; //Loop through every pixel to generate a shadow for Y := 0 to ImageHeight - 1 do for X := 0 to ImageWidth - 1 do begin //These call the raster operation that generate a shadow image. The raster //operation "DSPDxax" is basically the same as BitBlt with ROP_DSPDxax as the //last parameter, but since we're modifying the PNG image itself, the operation //needed to be translated to normal code. DestColor := GetPixel(Canvas.Handle, R.Left + X, R.Top + Y); DestR := Perform_DSPDxax(255, DestColor and $FF, BrushR); DestG := Perform_DSPDxax(255, DestColor shr 8 and $FF, BrushG); DestB := Perform_DSPDxax(255, DestColor shr 16 and $FF, BrushB); DestPng.Pixels[X, Y] := RGB(DestR, DestG, DestB); end; DestPng.Draw(Canvas, R); finally DestPng.Free; end; end; procedure DrawTBXIconFlatShadow(Canvas: TCanvas; const R: TRect; ImageList: TCustomImageList; ImageIndex: Integer; ShadowColor: TColor); var Png: TPNGObject; begin //If the imagelist is not the PngImageList, then invoke the default call. if not (ImageList is TPngImageList) then begin TBXUtils.DrawTBXIconFullShadow(Canvas, R, ImageList, ImageIndex, ShadowColor); Exit; end; //Recieve a copy of the image in the imagelist, if available Png := TPngImageList(ImageList).PngImages[ImageIndex].Duplicate; try //Now draw the PNG with the appropriate options, to make it appear //disabled DrawPNG(Png, Canvas, R, TPngImageList(ImageList).PngOptions); finally Png.Free; end; end; { TPngTBXOfficeXPTheme } procedure TPngTBXOfficeXPTheme.PaintImage(Canvas: TCanvas; ARect: TRect; const ItemInfo: TTBXItemInfo; ImageList: TCustomImageList; ImageIndex: Integer); var HiContrast: Boolean; begin //This method is the same as in the original OfficeXP theme, for the most part. with ItemInfo do begin if ImageList is TTBCustomImageList then begin TTBCustomImageList(ImageList).DrawState(Canvas, ARect.Left, ARect.Top, ImageIndex, Enabled, (HoverKind <> hkNone), Selected); Exit; end; HiContrast := IsDarkColor(GetItemImageBackground(ItemInfo), 64); if not Enabled then begin DrawTBXIconFlatShadow(Canvas, ARect, ImageList, ImageIndex, BtnItemColors[bisDisabled, ipText]); end else if Selected or Pushed or (HoverKind <> hkNone) then begin if not (Selected or Pushed and not IsPopupParent) then begin OffsetRect(ARect, 1, 1); DrawTBXIconFullShadow(Canvas, ARect, ImageList, ImageIndex, IconShadowColor); OffsetRect(ARect, -2, -2); end; DrawTBXIcon(Canvas, ARect, ImageList, ImageIndex, HiContrast); end else if HiContrast or TBXHiContrast or TBXLoColor then DrawTBXIcon(Canvas, ARect, ImageList, ImageIndex, HiContrast) else HighlightTBXIcon(Canvas, ARect, ImageList, ImageIndex, clWindow, 178); end; end; initialization //Unregister the original OfficeXP theme and replace it with the PNG-aware //version. It is neccesary either to not include TBXOfficeXPTheme in your uses //clause, or to make sure this unit gets initialized *after* TBXOfficeXPTheme. if IsTBXThemeAvailable(ThemeName) then UnregisterTBXTheme(ThemeName); RegisterTBXTheme(ThemeName, TPngTBXOfficeXPTheme); end.