Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
5203ce801This file is pinned to the selected release. Download the original to open it in your design tool.
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
namespace QuickFont
{
class KerningCalculator
{
private struct XLimits
{
public int Min;
public int Max;
}
private static int Kerning(QFontGlyph g1, QFontGlyph g2, XLimits[] lim1, XLimits[] lim2, QFontKerningConfiguration config)
{
int yOffset1 = g1.yOffset;
int yOffset2 = g2.yOffset;
int startY = Math.Max(yOffset1, yOffset2);
int endY = Math.Min(g1.rect.Height + yOffset1, g2.rect.Height + yOffset2);
int w1 = g1.rect.Width;
int worstCase = w1;
//TODO - offset startY, endY by yOffset1 so that lim1[j-yOffset1] can be written as lim1[j], will need another var for yOffset2
for (int j = startY; j < endY; j++)
worstCase = Math.Min(worstCase, w1 - lim1[j-yOffset1].Max + lim2[j-yOffset2].Min);
worstCase = Math.Min(worstCase, g1.rect.Width);
worstCase = Math.Min(worstCase, g2.rect.Width);
//modify by character kerning rules
CharacterKerningRule kerningRule = config.GetOverridingCharacterKerningRuleForPair(""+g1.character + g2.character);
if (kerningRule == CharacterKerningRule.Zero)
{
return 0;
}
else if (kerningRule == CharacterKerningRule.NotMoreThanHalf)
{
return (int)Math.Min(Math.Min(g1.rect.Width,g2.rect.Width)*0.5f, worstCase);
}
return worstCase;
}
public static Dictionary<String, int> CalculateKerning(char[] charSet, QFontGlyph[] glyphs, List<QBitmap> bitmapPages, QFontKerningConfiguration config)
{
var kerningPairs = new Dictionary<String, int>();
//we start by computing the index of the first and last non-empty pixel in each row of each glyph
XLimits[][] limits = new XLimits[charSet.Length][];
int maxHeight = 0;
for (int n = 0; n < charSet.Length; n++)
{
var rect = glyphs[n].rect;
var page = bitmapPages[glyphs[n].page];
limits[n] = new XLimits[rect.Height];
maxHeight = Math.Max(rect.Height, maxHeight);
int yStart = rect.Y;
int yEnd = rect.Y + rect.Height;
int xStart = rect.X;
int xEnd = rect.X + rect.Width;
for (int j = yStart; j < yEnd; j++)
{
int last = xStart;
bool yetToFindFirst = true;
for (int i = xStart; i < xEnd; i++)
{
if (!QBitmap.EmptyAlphaPixel(page.bitmapData, i, j,config.alphaEmptyPixelTolerance))
{
if (yetToFindFirst)
{
limits[n][j - yStart].Min = i - xStart;
yetToFindFirst = false;
}
last = i;
}
}
limits[n][j - yStart].Max = last - xStart;
if (yetToFindFirst)
limits[n][j - yStart].Min = xEnd - 1;
}
}
//we now bring up each row to the max (or min) of it's two adjacent rows, this is to stop glyphs sliding together too closely
var tmp = new XLimits[maxHeight];
for (int n = 0; n < charSet.Length; n++)
{
//clear tmp
for (int j = 0; j < limits[n].Length; j++)
tmp[j] = limits[n][j];
for (int j = 0; j < limits[n].Length; j++)
{
if(j != 0){
tmp[j].Min = Math.Min(limits[n][j - 1].Min, tmp[j].Min);
tmp[j].Max = Math.Max(limits[n][j - 1].Max, tmp[j].Max);
}
if (j != limits[n].Length - 1)
{
tmp[j].Min = Math.Min(limits[n][j + 1].Min, tmp[j].Min);
tmp[j].Max = Math.Max(limits[n][j + 1].Max, tmp[j].Max);
}
}
for (int j = 0; j < limits[n].Length; j++)
limits[n][j] = tmp[j];
}
for (int i = 0; i < charSet.Length; i++)
for (int j = 0; j < charSet.Length; j++)
kerningPairs.Add("" + charSet[i] + charSet[j], 1-Kerning(glyphs[i], glyphs[j], limits[i], limits[j],config));
return kerningPairs;
}
}
}