// ButtonStencil.h #ifndef ButtonStencilh #define ButtonStencilh #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #include "PixelBlock.h" #include "../Primitives/gColor.h" #include "../Primitives/gFilters.h" #include "../Noise/Noise.h" class CButtonStencil { virtual void SetPixel(WORD x, WORD y, DWORD Color) =0; public: enum FinishType {Normal,Clean,Marbled}; void Button(WORD Left, WORD Top, WORD Width, WORD Height, const gColorSpline& Colors, bool Down=false, bool Highlight=false, WORD BorderWidth=8, FinishType Finish=Normal) { ASSERT(Width >=BorderWidth); ASSERT(Height>=BorderWidth); WORD Middle=Height/2; WORD ReflectionBottom=Middle+(Down ? BorderWidth/2 : BorderWidth); double DblBorderWidth=2*BorderWidth; for(WORD x=Width; x--; ) { double Sides=gFilters::HermiteStep(x, 0, 2*BorderWidth) // Start by making a rounded border on the sides: *(1-gFilters::HermiteStep(x, Width-2*BorderWidth, Width)); for(WORD y=Height; y--;) { // Blurry Button: double vTop=gFilters::HermiteStep(y, 0, DblBorderWidth); // Another border, this time for top double Arch=vTop*Sides; // Mix the Sides and vTop to make an arch (which will be needed again later)... double Border=Arch*(1-gFilters::HermiteStep(y, Height-DblBorderWidth, Height)); // ...and mix in the bottom. // Button: double Outer=1-0.2*gFilters::Ramp(Border, 0, 0.5); // Keeps only the dark half and spreads it to full range. double vGradient=gFilters::Ramp(y, BorderWidth, Height-BorderWidth); // Vertical Gradient Black at the top if(Outer==0.8) Outer=0.5*(1-vGradient); // Inner Gradient double Inner=gFilters::Ramp(Border, 0.5, 1); // Thresholding the Inner Blur double Button=(Inner>0 ? ((Down ? 1.5 : 2)-Inner)*Outer : Outer); // Use the Inner Blur for inner glow or to remove the inner gradient depending on 'Down' // Reflection: double Reflection=Arch*(1-gFilters::Ramp(y, Middle-BorderWidth, ReflectionBottom)); // Mix the Reflection Bottom with the arch to make a blurry shape Reflection=gFilters::Ramp(Reflection, 0.3, 0.5); // Threshold Reflection Reflection*=1-0.75*gFilters::Ramp(y, BorderWidth, ReflectionBottom); // Reflection Gradient starting at 25% white if(!Down) Reflection=gFilters::HermiteCurve(Reflection); // Noise: //Reflection*=1-fabs(Noise::InterpolatedNoise(Left+x/10.0, Top+y/4.0)); // For debugging the Noise switch(Finish) { case Clean: Button=gColor::Screen(Reflection,0.9, Button,0.1); break; // For debugging (or not having) Noise case Marbled: Button=gColor::Difference(1-fabs(Noise::InterpolatedNoise((Left+x)/20.0, (Top+y)/4.0)), Button, Reflection, 1-Button); break; // Marble Button and surface default: Button=gColor::Overlay (1-fabs(Noise::InterpolatedNoise((Left+x)/20.0, (Top+y)/4.0)), Reflection, Button, 1-Reflection); break; // Bright Button Top } // Highlight: double Line=gFilters::Ramp(Border, 0.1, 0.3) // Antialiased line around button. *(1-gFilters::Ramp(Border, 0.3, 0.4))/2; double Mask=gFilters::Ramp(Border, 0.2, 0.5) // Antialiased line around button used to highlight the button. *(1-gFilters::Ramp(Border, 0.5, 0.7)); Button=gColor::Add(1-(Mask+Line), Button, Line/2+(Highlight ? Mask : Mask*(1-vGradient)/2)); // Work on the Button Parameter masking out the Line, then adding a little of the line back in. // Render: //BYTE B=BYTE(Round(255*gFilters::Ramp(Border, 0, 0.1))); // Colorless version for Debugging //SetPixel(Left+x, Top+y, RGB(B,B,B)); // Turn the parameter into a Greyscale Level. gColor Color(Colors.Get(Button)); Color.SetA(Finish==Marbled ? 0 : 1-gFilters::Ramp(Border, 0, 0.1)); // Border Prior to Line ramp start is the button background: negative of the Alpha-channel SetPixel(Left+x, Top+y, Color.GetRGBA()); // Turn the parameter into a Greyscale Level. } } } }; struct CButtonPainter : public CPixelPainter, public CButtonStencil { // Plug ButtonStencil into PixelPainter: void SetPixel (WORD x, WORD y, DWORD Color) {CPixelPainter::SetPixel(x,y,Color);} CButtonPainter() {} CButtonPainter(WORD Width, WORD Height, BYTE Level=0) : CPixelPainter(Width,Height,Level) {} virtual ~CButtonPainter() {} }; #endif // ndef ButtonStencilh