Published by Derek Moore on 27 Jul 2009 at 12:03 am
I am now fully underway in the rewrite of 7stacks v1.5. This new 7stacks, unlike v1.2, eschews Windows controls of any kind. Now, for reasons of both speed and appearance, I will be drawing everything myself.
The primary tool I’m using to do this is something I’ve used a lot lately .. the Graphics32 library. It’s a Delphi library that allows you to draw fully alpha-blended 32-bit graphics at speeds much faster than a standard canvas. I used Graphics32 in 7stacks v1.0 to draw text and preview graphics. One of the tricks I’ve learned when doing this is how to make Graphics32 combine with Aero Glass to make awesome effects like 7stack’s text possible.
So, to take use of this, I’d like to retrieve 256×256 full-sized icons. Using the backend library that I’m using now, the MPCommonLibrary from MustangPeak (which itself is a translation of that part of the Windows API into Delphi), this is simple to do. Using 2 lines of code (using the function SHGetImageList with the parameter SHIL_JUMBO), I can retreieve a big ol’ icon, and put it into a Graphics32 bitmap, ready for use. But wait, that’s too easy!
As it turns out, not all icons are 256×256. And I can completely understand that. But, certainly in cases where there is no full-sized icon, the API could inform you about this, and let you make alternate plans (say, retrieving the 48×48 icon, instead)? Perhaps telling you how big the real icon is? Or just returning no icon at all? But no. It turns out that the Microsoft API team apparently gets its jollies from toying w/ developers, rather than helping them. In these cases, where no big icon is available, it simply slaps a 48×48 (or sometimes 32×32 or 16×16) icon onto a mostly blank 256×256 icon. AND, it doesn’t tell you how big the icon actually is! Hooray! So, imaine the fun I had of looking at this tiny little icon on this HUGE space, and trying to ask a computer to look at how big it really is.
Fortunately, Graphics32 is just too awesome, and led me to what we’ll call a “workaround”. I created the following 2 functions. The first function, TrueIconSize, scans the icon’s alpha channel, starting from the bottom-right to the top-left. If it finds anything, it says “aha! There’s the icon!”. The 2nd function, RoundToIntInList, simply rounds up to the nearest available icon size.
function TrueIconSize(Bitmap : TBitmap32) : integer; const TestPoints = 20; var PointsBlank : integer; X,Y, I : integer; IconSizes : TIntegerArray; begin Randomize; SetLength(IconSizes,5); IconSizes := 16; IconSizes := 32; IconSizes := 48; IconSizes := 128; IconSizes := 256; Result := Bitmap.Width; repeat Dec(Result); PointsBlank := 0; for I := 1 to TestPoints do begin X := RandomRange(0, Result); Y := RandomRange(0, Result); if (AlphaComponent(Bitmap.PixelS[X,Result]) = 0) and (AlphaComponent(Bitmap.PixelS[Result,Y]) = 0) then Inc(PointsBlank); end; until (Result <= 16) or (PointsBlank <= TestPoints-1); Result := RoundToIntInList(Result, IconSizes); end; function RoundToIntInList(Amount : integer; IntArray : TIntegerArray) : integer; var I : integer; HiIndex, LoIndex : integer; IsFound : boolean; begin I := 0; IsFound := FALSE; while (not IsFound) and (I <= Length(IntArray)-2) do begin IsFound := (Amount > IntArray[I]) and (Amount < IntArray[I+1]); Inc(I); end; LoIndex := I-1; HiIndex := I; if Frac(Amount / (IntArray[HiIndex] - IntArray[LoIndex])) > 0.5 then Result := IntArray[HiIndex] else Result := IntArray[LoIndex]; end;
There may be approx. 4 people who ever need these functions, but there’s no reason to reinvent the wheel.
And just remember the moral of our story … don’t let your son or daughter grow up and say, “I want to be a Windows programmer!”