public void DownScaleThis (int newWidth, int newHeight, QBitmapData dest, PixelFormat format)
		{
			var srcWidth = mBitmapData.Width;
			var srcHeight = mBitmapData.Height;

			if (format != PixelFormat.Format32bppArgb)
				throw new Exception ("DownsScale32 only works on 32 bit images");
			float xscale = (float)srcWidth / newWidth;
			float yscale = (float)srcHeight / newHeight;
			byte r = 0, g = 0, b = 0, a = 0;
			float summedR = 0f;
			float summedG = 0f;
			float summedB = 0f;
			float summedA = 0f;
			int left, right, top, bottom;
			//the area of old pixels covered by the new bitmap
			float targetStartX, targetEndX;
			float targetStartY, targetEndY;
			float leftF, rightF, topF, bottomF;
			//edges of new pixel in old pixel coords
			float weight;
			float weightScale = xscale * yscale;
			float totalColourWeight = 0f;
			for (int m = 0; m < newHeight; m++)
			{
				for (int n = 0; n < newWidth; n++)
				{
					leftF = n * xscale;
					rightF = (n + 1) * xscale;
					topF = m * yscale;
					bottomF = (m + 1) * yscale;
					left = (int)leftF;
					right = (int)rightF;
					top = (int)topF;
					bottom = (int)bottomF;
					if (left < 0)
						left = 0;
					if (top < 0)
						top = 0;
					if (right >= srcWidth)
						right = srcWidth - 1;
					if (bottom >= srcHeight)
						bottom = srcHeight - 1;
					summedR = 0f;
					summedG = 0f;
					summedB = 0f;
					summedA = 0f;
					totalColourWeight = 0f;
					for (int j = top; j <= bottom; j++)
					{
						for (int i = left; i <= right; i++)
						{
							targetStartX = Math.Max (leftF, i);
							targetEndX = Math.Min (rightF, i + 1);
							targetStartY = Math.Max (topF, j);
							targetEndY = Math.Min (bottomF, j + 1);
							weight = (targetEndX - targetStartX) * (targetEndY - targetStartY);
							this.GetPixel32 (i, j, ref r, ref g, ref b, ref a);
							summedA += weight * a;
							if (a != 0)
							{
								summedR += weight * r;
								summedG += weight * g;
								summedB += weight * b;
								totalColourWeight += weight;
							}
						}
					}
					summedR /= totalColourWeight;
					summedG /= totalColourWeight;
					summedB /= totalColourWeight;
					summedA /= weightScale;
					if (summedR < 0)
						summedR = 0f;
					if (summedG < 0)
						summedG = 0f;
					if (summedB < 0)
						summedB = 0f;
					if (summedA < 0)
						summedA = 0f;
					if (summedR >= 256)
						summedR = 255;
					if (summedG >= 256)
						summedG = 255;
					if (summedB >= 256)
						summedB = 255;
					if (summedA >= 256)
						summedA = 255;
					dest.PutPixel32 (n, m, (byte)summedR, (byte)summedG, (byte)summedB, (byte)summedA);
				}
			}
		}
		public void BlurAlpha (int radius, int passes, QBitmapData tmp, int width, int height)
		{
			byte a = 0;
			int summedA;
			int weight = 0;
			int xpos, ypos, x, y, kx, ky;
			for (int pass = 0; pass < passes; pass++)
			{
				//horizontal pass
				for (y = 0; y < height; y++)
				{
					for (x = 0; x < width; x++)
					{
						summedA = weight = 0;
						for (kx = -radius; kx <= radius; kx++)
						{
							xpos = x + kx;
							if (xpos >= 0 && xpos < width)
							{
								this.GetAlpha32 (xpos, y, ref a);
								summedA += a;
								weight++;
							}
						}
						summedA /= weight;
						tmp.PutAlpha32 (x, y, (byte)summedA);
					}
				}
				//vertical pass
				for (x = 0; x < width; ++x)
				{
					for (y = 0; y < height; ++y)
					{
						summedA = weight = 0;
						for (ky = -radius; ky <= radius; ky++)
						{
							ypos = y + ky;
							if (ypos >= 0 && ypos < height)
							{
								tmp.GetAlpha32 (x, ypos, ref a);
								summedA += a;
								weight++;
							}
						}
						summedA /= weight;
						this.PutAlpha32 (x, y, (byte)summedA);
					}
				}
			}
		}
		public NxFont Load(string filePath, float height, float downSampleFactor, NxFontLoaderConfiguration loaderConfig)
		{
			if (loaderConfig == null)
			{
				throw new ArgumentNullException ("loaderConfig");
			}

			float fontScale;
			TransformViewport? transToVp = NxFont.SetupTransformViewport (height, loaderConfig.TransformToCurrentOrthogProjection, loaderConfig.Transform, out fontScale);

			var qfont = new NxFont();
			var internalConfig = new QFontLoaderConfiguration();		
			var fontData = new QFontData ();
			qfont.SetData (fontData);

			QFontDataInformation fontInfo = null;
			using (var fs = File.OpenRead (filePath))
			{
				fontInfo = fontData.LoadFromStream (fs);
			}
			var bitmapFiles = fontInfo.GenerateBitmapPageNames (filePath);

			var bitmapPages = new List<NxBitmap> ();
			foreach (var bitmapFileName in bitmapFiles)
			{
				// TODO : STREAM BASED REPLACEMENT 
				// https://support.microsoft.com/en-us/kb/814675
				// GDI+ require the bitmap files to be locked as indexed image
				// during the lifetime i.e. maybe reloaded from disk				
				using (var fs = File.OpenRead (bitmapFileName))	
				{
					var parent = new Bitmap (fs);
					var data = parent.LockBits (
							new Rectangle(0,0, parent.Width, parent.Height)
							,System.Drawing.Imaging.ImageLockMode.ReadWrite
							,parent.PixelFormat);
					var target = new QBitmapData (data);
					var qb = new NxBitmap (parent, target);
					bitmapPages.Add (qb);
				}
			}
			var glyphList = fontData.InitialiseQFontData (fontInfo, ref bitmapPages, downSampleFactor, internalConfig);

			if (loaderConfig.ShadowConfig != null)
			{
				qfont.DropShadow = Helper.BuildDropShadow<NxFont, NxBitmap> (
					bitmapPages,
					glyphList.ToArray (),
					loaderConfig.ShadowConfig,
					Helper.ToArray (fontInfo.CharSet),
					internalConfig.KerningConfig.alphaEmptyPixelTolerance);
			}
			fontData.InitialiseKerningPairs (fontInfo, bitmapPages, glyphList, internalConfig);

			if (loaderConfig.ShadowConfig != null)
				qfont.Options.DropShadowActive = true;
			if (transToVp != null)
				qfont.Options.TransformToViewport = transToVp;

			qfont.InitialiseGlyphRenderer(loaderConfig.CharacterOutput, loaderConfig.FontGlyphRenderer, loaderConfig.DropShadowRenderer);

			return qfont;
		}