Exemplo n.º 1
0
void UniFont::CacheGlyphDefault(const unsigned int* str, int index, int len, int cellCols, int cellRows, Texture* tex, bool* outIsDirty) {
	int codePoint = str[index];
	int charSize = fontScale + 2;
	if (GetInfoForCodepoint(codePoint) == nullptr) {
		stbtt_fontinfo* font = nullptr;
		unsigned char* cBmp = nullptr;
		int cW = 0, cH = 0;
		for (int j = 0; j < fontInfos.count; j++) {
			if (fontInfos.data[j].low <= codePoint && codePoint <= fontInfos.data[j].high) {
				float pixelScale = stbtt_ScaleForPixelHeight(&fontInfos.data[j].info, fontScale);
				int glyphIdx = stbtt_FindGlyphIndex(&fontInfos.data[j].info, codePoint);
				cBmp = stbtt_GetCodepointBitmap(&fontInfos.data[j].info, 0, pixelScale, codePoint, &cW, &cH, 0, 0);
				if (cBmp) {
					font = &fontInfos.data[j].info;
					break;
				}
			}
		}

		if (font != nullptr) {
			*outIsDirty = true;
			int ascent, descent, lineGap;
			stbtt_GetFontVMetrics(font, &ascent, &descent, &lineGap);

			float pixelScale = stbtt_ScaleForPixelHeight(font, fontScale);

			int cellY = cacheCursor / cellCols;
			int cellX = cacheCursor % cellCols;

			int startX = cellX * charSize;
			int startY = cellY * charSize;

			GlyphBlit(tex->texMem, tex->width, tex->height, startX, startY, cBmp, cW, cH);

			free(cBmp);

			int advanceWidth = 0, leftSideBearing = 0;
			stbtt_GetCodepointHMetrics(font, codePoint, &advanceWidth, &leftSideBearing);

			int x0, y0, x1, y1;
			stbtt_GetCodepointBitmapBox(font, codePoint, pixelScale, pixelScale, &x0, &y0, &x1, &y1);
			CodepointInfo pointInfo = {};
			pointInfo.codepoint = codePoint;
			pointInfo.x = startX;
			pointInfo.y = startY;
			pointInfo.w = cW;
			pointInfo.h = cH;
			pointInfo.xOffset = x0;
			pointInfo.yOffset = y0;
			pointInfo.xAdvance = pixelScale * advanceWidth;

			codepointListing.EnsureCapacity(cacheCursor + 1);
			codepointListing.count = BNS_MAX(codepointListing.count, cacheCursor + 1);
			codepointListing.data[cacheCursor] = pointInfo;

			cacheCursor = (cacheCursor + 1) % (cellCols * cellRows);
		}
	}
}
Exemplo n.º 2
0
s32 cellFontRenderCharGlyphImage(vm::ptr<CellFont> font, u32 code, vm::ptr<CellFontRenderSurface> surface, float x, float y, vm::ptr<CellFontGlyphMetrics> metrics, vm::ptr<CellFontImageTransInfo> transInfo)
{
	cellFont.notice("cellFontRenderCharGlyphImage(font=*0x%x, code=0x%x, surface=*0x%x, x=%f, y=%f, metrics=*0x%x, trans=*0x%x)", font, code, surface, x, y, metrics, transInfo);

	if (!font->renderer_addr)
	{
		return CELL_FONT_ERROR_RENDERER_UNBIND;
	}

	// Render the character
	s32 width, height, xoff, yoff;
	float scale = stbtt_ScaleForPixelHeight(font->stbfont, font->scale_y);
	unsigned char* box = stbtt_GetCodepointBitmap(font->stbfont, scale, scale, code, &width, &height, &xoff, &yoff);

	if (!box)
	{
		return CELL_OK;
	}

	// Get the baseLineY value
	s32 baseLineY;
	s32 ascent, descent, lineGap;
	stbtt_GetFontVMetrics(font->stbfont, &ascent, &descent, &lineGap);
	baseLineY = (int)((float)ascent * scale); // ???

	// Move the rendered character to the surface
	unsigned char* buffer = vm::_ptr<unsigned char>(surface->buffer.addr());
	for (u32 ypos = 0; ypos < (u32)height; ypos++)
	{
		if ((u32)y + ypos + yoff + baseLineY >= (u32)surface->height)
			break;

		for (u32 xpos = 0; xpos < (u32)width; xpos++)
		{
			if ((u32)x + xpos >= (u32)surface->width)
				break;

			// TODO: There are some oddities in the position of the character in the final buffer
			buffer[((s32)y + ypos + yoff + baseLineY)*surface->width + (s32)x + xpos] = box[ypos * width + xpos];
		}
	}
	stbtt_FreeBitmap(box, 0);
	return CELL_OK;
}
Exemplo n.º 3
0
Purity::Font::Font(const std::string& fontFileName)
    : mFontFileName(fontFileName)
{
    loadFont();

    if (!stbtt_InitFont(&mFontInfo, mFontData.data(), 0))
    {
        std::cerr << "Failed to initialize font" << std::endl;
    }

    int w, h;
    auto bitmap = stbtt_GetCodepointBitmap(&mFontInfo, 0, stbtt_ScaleForPixelHeight(&mFontInfo, 30),
                                           'D', &w, &h, 0, 0);
    for (int j = 0; j < h; ++j)
    {
        for (int i = 0; i < w; ++i)
            putchar(" .:ioVM@"[bitmap[j * w + i] >> 5]);
        putchar('\n');
    }
}
Exemplo n.º 4
0
int cellFontRenderCharGlyphImage(mem_ptr_t<CellFont> font, u32 code, mem_ptr_t<CellFontRenderSurface> surface, float x, float y, mem_ptr_t<CellFontGlyphMetrics> metrics, mem_ptr_t<CellFontImageTransInfo> transInfo)
{
	x = GetCurrentPPUThread().FPR[1]; // TODO: Something is wrong with the float arguments
	y = GetCurrentPPUThread().FPR[2]; // TODO: Something is wrong with the float arguments
	cellFont->Log("cellFontRenderCharGlyphImage(font_addr=0x%x, code=0x%x, surface_addr=0x%x, x=%f, y=%f, metrics_addr=0x%x, trans_addr=0x%x)",
		font.GetAddr(), code, surface.GetAddr(), x, y, metrics.GetAddr(), transInfo.GetAddr());

	if (!font.IsGood() || !surface.IsGood() || !metrics.IsGood() || !transInfo.IsGood())
		return CELL_FONT_ERROR_INVALID_PARAMETER;
	if (!font->renderer_addr)
		return CELL_FONT_ERROR_RENDERER_UNBIND;

	// Render the character
	int width, height, xoff, yoff;
	float scale = stbtt_ScaleForPixelHeight(&(font->stbfont), font->scale_y);
	unsigned char* box = stbtt_GetCodepointBitmap(&(font->stbfont), scale, scale, code, &width, &height, &xoff, &yoff);
	if (!box) return CELL_OK;

	// Get the baseLineY value
	int baseLineY;
	int ascent, descent, lineGap;
	stbtt_GetFontVMetrics(&(font->stbfont), &ascent, &descent, &lineGap);
	baseLineY = ascent * scale;

	// Move the rendered character to the surface
	unsigned char* buffer = (unsigned char*)Memory.VirtualToRealAddr(surface->buffer_addr);
	for (u32 ypos = 0; ypos < (u32)height; ypos++){
		if ((u32)y + ypos + yoff + baseLineY >= surface->height)
			break;

		for (u32 xpos = 0; xpos < (u32)width; xpos++){
			if ((u32)x + xpos >= surface->width)
				break;

			// TODO: There are some oddities in the position of the character in the final buffer
			buffer[((int)y + ypos + yoff + baseLineY)*surface->width + (int)x+xpos] = box[ypos*width + xpos];
		}
	}
	stbtt_FreeBitmap(box, 0);
	return CELL_FONT_OK;
}
Exemplo n.º 5
0
void DynamicFontAtSize::_update_char(CharType p_char) {

	if (char_map.has(p_char))
		return;

	font->lock();


	int w,h,xofs,yofs;
	unsigned char * cpbitmap = stbtt_GetCodepointBitmap(&font->info, scale, scale, p_char, &w, &h, &xofs, &yofs );

	if (!cpbitmap) {
		//no glyph

		int advance;
		stbtt_GetCodepointHMetrics(&font->info, p_char, &advance, 0);
		//print_line("char has no bitmap: "+itos(p_char)+" but advance is "+itos(advance*scale));
		Character ch;
		ch.texture_idx=-1;
		ch.advance=advance*scale;
		ch.h_align=0;
		ch.v_align=0;

		char_map[p_char]=ch;

		font->unlock();

		return;
	}

	int mw=w+rect_margin*2;
	int mh=h+rect_margin*2;

	if (mw>4096 || mh>4096) {

		stbtt_FreeBitmap(cpbitmap,NULL);
		font->unlock();
		ERR_FAIL_COND(mw>4096);
		ERR_FAIL_COND(mh>4096);
	}

	//find a texture to fit this...

	int tex_index=-1;
	int tex_x=0;
	int tex_y=0;

	for(int i=0;i<textures.size();i++) {

		CharTexture &ct=textures[i];

		if (mw > ct.texture_size || mh > ct.texture_size) //too big for this texture
			continue;

		tex_y=0x7FFFFFFF;
		tex_x=0;

		for(int j=0;j<ct.texture_size-mw;j++) {

			int max_y=0;

			for(int k=j;k<j+mw;k++) {

				int y = ct.offsets[k];
				if (y>max_y)
					max_y=y;
			}

			if (max_y<tex_y) {
				tex_y=max_y;
				tex_x=j;
			}
		}

		if (tex_y==0x7FFFFFFF || tex_y+mh > ct.texture_size)
			continue; //fail, could not fit it here

		tex_index=i;
		break;
	}

//	print_line("CHAR: "+String::chr(p_char)+" TEX INDEX: "+itos(tex_index)+" X: "+itos(tex_x)+" Y: "+itos(tex_y));

	if (tex_index==-1) {
		//could not find texture to fit, create one

		int texsize = MAX(size*8,256);
		if (mw>texsize)
			texsize=mw; //special case, adapt to it?
		if (mh>texsize)
			texsize=mh; //special case, adapt to it?

		texsize=nearest_power_of_2(texsize);

		texsize=MIN(texsize,4096);


		CharTexture tex;
		tex.texture_size=texsize;
		tex.imgdata.resize(texsize*texsize*2); //grayscale alpha

		{
			//zero texture
			DVector<uint8_t>::Write w = tex.imgdata.write();
			for(int i=0;i<texsize*texsize*2;i++) {
				w[i]=0;
			}
		}
		tex.offsets.resize(texsize);
		for(int i=0;i<texsize;i++) //zero offsets
			tex.offsets[i]=0;

		textures.push_back(tex);
		tex_index=textures.size()-1;

	}


	//fit character in char texture

	CharTexture &tex=textures[tex_index];

	{
		DVector<uint8_t>::Write wr = tex.imgdata.write();

		for(int i=0;i<h;i++) {
			for(int j=0;j<w;j++) {

				int ofs = ( (i+tex_y+rect_margin)*tex.texture_size+j+tex_x+rect_margin)*2;
				wr[ofs+0]=255; //grayscale as 1
				wr[ofs+1]=cpbitmap[i*w+j]; //alpha as 0
			}
		}
	}

	//blit to image and texture
	{

		Image img(tex.texture_size,tex.texture_size,0,Image::FORMAT_GRAYSCALE_ALPHA,tex.imgdata);

		if (tex.texture.is_null()) {
			tex.texture.instance();
			tex.texture->create_from_image(img,Texture::FLAG_FILTER);
		} else {
			tex.texture->set_data(img); //update
		}

	}


	// update height array

	for(int k=tex_x;k<tex_x+mw;k++) {

		tex.offsets[k]=tex_y+mh;
	}

	int advance;
	stbtt_GetCodepointHMetrics(&font->info, p_char, &advance, 0);

	Character chr;
	chr.h_align=xofs;
	chr.v_align=yofs + get_ascent();
	chr.advance=advance*scale;
	chr.texture_idx=tex_index;


	chr.rect=Rect2(tex_x+rect_margin,tex_y+rect_margin,w,h);

	//print_line("CHAR: "+String::chr(p_char)+" TEX INDEX: "+itos(tex_index)+" RECT: "+chr.rect+" X OFS: "+itos(xofs)+" Y OFS: "+itos(yofs));

	char_map[p_char]=chr;

	stbtt_FreeBitmap(cpbitmap,NULL);

	font->unlock();

}
Exemplo n.º 6
0
int main(int argc, char **argv)
{
   stbtt_fontinfo font;
   unsigned char *bitmap;
   int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 34807), s = (argc > 2 ? atoi(argv[2]) : 32);

   //debug();

   // @TODO: why is minglui.ttc failing? 
   fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/mingliu.ttc", "rb"));

   //fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/x/DroidSansMono.ttf", "rb"));
   {
      static stbtt_pack_context pc;
      static stbtt_packedchar cd[256];
      static unsigned char atlas[1024*1024];

      stbtt_PackBegin(&pc, atlas, 1024,1024,1024,1,NULL);
      stbtt_PackFontRange(&pc, ttf_buffer, 0, 32.0, 0, 256, cd);
      stbtt_PackEnd(&pc);
   }

#if 0
   stbtt_BakeFontBitmap(ttf_buffer,stbtt_GetFontOffsetForIndex(ttf_buffer,0), 40.0, temp_bitmap[0],BITMAP_W,BITMAP_H, 32,96, cdata); // no guarantee this fits!
   stbi_write_png("fonttest1.png", BITMAP_W, BITMAP_H, 1, temp_bitmap, 0);

   {
      stbtt_pack_context pc;
      stbtt_PackBegin(&pc, temp_bitmap[0], BITMAP_W, BITMAP_H, 0, 1, NULL);
      stbtt_PackFontRange(&pc, ttf_buffer, 0, 20.0, 32, 95, pdata);
      stbtt_PackFontRange(&pc, ttf_buffer, 0, 20.0, 0xa0, 0x100-0xa0, pdata);
      stbtt_PackEnd(&pc);
      stbi_write_png("fonttest2.png", BITMAP_W, BITMAP_H, 1, temp_bitmap, 0);
   }

   {
      stbtt_pack_context pc;
      stbtt_pack_range pr[2];
      stbtt_PackBegin(&pc, temp_bitmap[0], BITMAP_W, BITMAP_H, 0, 1, NULL);

      pr[0].chardata_for_range = pdata;
      pr[0].first_unicode_char_in_range = 32;
      pr[0].num_chars_in_range = 95;
      pr[0].font_size = 20.0f;
      pr[1].chardata_for_range = pdata+256;
      pr[1].first_unicode_char_in_range = 0xa0;
      pr[1].num_chars_in_range = 0x100 - 0xa0;
      pr[1].font_size = 20.0f;

      stbtt_PackSetOversampling(&pc, 2, 2);
      stbtt_PackFontRanges(&pc, ttf_buffer, 0, pr, 2);
      stbtt_PackEnd(&pc);
      stbi_write_png("fonttest3.png", BITMAP_W, BITMAP_H, 1, temp_bitmap, 0);
   }
   return 0;
#endif

   stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0));
   bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, (float)s), c, &w, &h, 0,0);

   for (j=0; j < h; ++j) {
      for (i=0; i < w; ++i)
         putchar(" .:ioVM@"[bitmap[j*w+i]>>5]);
      putchar('\n');
   }
   return 0;
}
Exemplo n.º 7
0
	fg::Image FontRenderer::renderGlyph(const fm::Uint32 &letter,Glyph::Style style,fm::vec2 *leftDown) const
	{
		if (!m_fileContent) 
			return fg::Image();
		
		if (letter == ' ')
		{
			float scl = stbtt_ScaleForMappingEmToPixels((stbtt_fontinfo*)m_stbFontInfo, m_currentSize);
			
			int ix0,ix1,iy0,iy1;
			
			stbtt_GetCodepointBitmapBox((stbtt_fontinfo*)m_stbFontInfo, 'w', scl, scl, &ix0, &iy0, &ix1, &iy1);
			
			return fg::Image(fm::vec2s(ix1-ix0,iy1-iy0),fg::Color(255,255,255,0));
		}
		
		unsigned char *bitmap;
		fm::vec2i size,offset;

		bitmap = stbtt_GetCodepointBitmap((stbtt_fontinfo*)m_stbFontInfo, 0,stbtt_ScaleForMappingEmToPixels((stbtt_fontinfo*)m_stbFontInfo, m_currentSize), letter, &size.w, &size.h, &offset.x, &offset.y);
		
		if (leftDown)
			*leftDown = offset;
		
		fg::Image img;
		img.create(size);
		
		Cv(size)
			img.set(p,fg::Color(255,bitmap[p.x + p.y*size.w]));
		
		stbtt_FreeBitmap(bitmap,0);
		
		return img;
		
		(void)style;
/*
		// if rendering upper/lower index decrease size
		unsigned int originalSize = m_currentSize;
		if ((style & Glyph::Subscript) xor (style & Glyph::Superscript ))
			setCharacterSize(m_currentSize*.7);

		// get glyph index
		FT_UInt glyphIndex = FT_Get_Char_Index(*((FT_Face*)m_ftFaceData),letter);

		// load glyph data
		int error = FT_Load_Glyph(*((FT_Face*)m_ftFaceData),glyphIndex,FT_LOAD_FORCE_AUTOHINT | FT_LOAD_DEFAULT);

		if (error)
			return fg::Image();

		FT_Face face = (*((FT_Face*)m_ftFaceData));

		// embolden and italicise
		if (style & fg::Glyph::Bold)
			FT_GlyphSlot_Embolden(face->glyph);

		if (style & fg::Glyph::Italic)
			FT_GlyphSlot_Oblique(face->glyph);

		// render glyph image
		error = FT_Render_Glyph( face->glyph,FT_RENDER_MODE_NORMAL);

		if (error)
			return fg::Image();

		// get bitmap details
		fm::Size width  = face->glyph->bitmap.width;
		fm::Size height = face->glyph->bitmap.rows;

		std::vector<fm::Uint8> bitmap(face->glyph->bitmap.buffer,face->glyph->bitmap.buffer+width*height);

		float offsetx = (face->glyph->metrics.horiBearingX>>6);
		float offsety = (face->glyph->metrics.horiBearingY>>6)-float(height);

		// manually create upper index if requested
		if (style & fg::Glyph::Superscript && !(style & fg::Glyph::Subscript))
		{
			fm::Size deltaH = height*0.4f;
			bitmap.resize(width*(height+deltaH),0);
			offsety+=deltaH;
			height+=deltaH;
		}

		// manually create lower index if requested
		if (style & fg::Glyph::Subscript && !(style & fg::Glyph::Superscript))
		{
			fm::Size deltaH = height*0.4f;
			bitmap.resize(width*(height+deltaH),0);
			offsety-=deltaH;
			Cxy(width,height)
			{
				fm::Uint8 tmp = bitmap[x+int(height+deltaH-y-1)*width];
				bitmap[x+int(height+deltaH-y-1)*width] = bitmap[x+int(height-y-1)*width];
				bitmap[x+int(height-y-1)*width] = tmp;
			}
			height+=height/2.f;
		}

		// reset the size back if needed
		if ((style & fg::Glyph::Subscript) xor (style & fg::Glyph::Superscript))
			setCharacterSize(originalSize);

		// manually strike through and/or underline if requested
		for (int i=0;i<bool(style & fg::Glyph::Crossed)+bool(style & fg::Glyph::Underlined);i++)
		{
			bool crossed = ((style & fg::Glyph::Crossed) && i==0);

			int ymax = offsety+height;
			float &ymin = offsety;

			// if the line would be out of the image (take account the y offset like in '~' as the leeter might not "sit" on the baseline)
			int lineW = int(m_metrics.maxH/15.f);
			int lineP = int(m_metrics.maxH*(crossed ? 0.2 : 0.0) );
			if (ymin > lineP)
			{
				bitmap.resize(width*(height+int(ymin-lineP)),0);

				ymin = lineP;
				height = ymax-ymin;
			}
			else if (ymax < lineW+lineP)
			{
				bitmap.resize(width*(height+(lineW+lineP-ymax)),0);

				ymax = lineW+lineP;
				height = ymax-ymin;
			}

			// simply set the value to one for every pixel in the line
			Cx(width)
				Cy((unsigned int)(lineW))
					bitmap[x+(height-1-y+int(ymin)-lineP)*width] = 255;
		}

		// manually create outline if requested
		if (style & fg::Glyph::Outline)
		{
			std::vector<fm::Uint8> oldData = bitmap;

			bitmap.resize((width+2)*(height+2));

			// get a bigger bitmap
			Cxy(width+2,height+2)
			{
				int db=0,sum=0;
				int ax=int(x)-1,ay=int(y)-1;
				int curVal = fm::rect2i(0,0,width-1,height-1).contains(fm::vec2i(ax,ay)) ? oldData[ax+ay*width] : 0;

				// this algorithm uses the difference between the neighbour pixels (the bigger the difference the bigger the output will be)
				for(int x1=-1;x1<=1;x1++) for(int y1=-1;y1<=1;y1++)
				{
					if (fm::vec2(x1,y1).LENGTH()>=1)
					{
						int deltaVal=(fm::rect2i(0,0,width-1,height-1).contains(fm::vec2i(ax+x1,ay+y1))) ? oldData[(ax+x1)+(ay+y1)*width]-curVal : -curVal;
						deltaVal*=(deltaVal > 0 ? .9 : 1.4);
						sum+=(deltaVal < 0 ? -deltaVal : deltaVal),
						db++;
					}
				}

				// do some scaling on the value and clamp it to [0;255]
				bitmap[x+y*(width+2)] = fm::math::min(255.0,db ? sum/db*1.6 : 0.0);
			}

			// update glyph image details
			width+=2,height+=2;offsetx--,offsety--;
		}

		Cxy(width,height)
		{
			float f = bitmap[y*width + x] / 255.0;
			bitmap[y*width + x] = fm::math::sqrt3(f) * 255.0;
		}

		if (leftDown)
			leftDown->x = offsetx,
			leftDown->y = offsety;

		// convert the bitmap to fg::Image
		fg::Image img;
		img.create(width,height);

		Cxy(width,height)
			img.setPixel(x,y,fg::Color(255,255,255,bitmap[y*width + x]));
*/
		return img;
	}
Exemplo n.º 8
0
void
MakeBitmapCodepoint(char Letter, platform_read_file *ReadFile, game_state *GameState)
{
	read_file_result FontFile = PlatformReadFile("../Assets/Gotham-Medium.ttf");
	Assert(FontFile.Contents > 0);

	int Width, Height, XOffset, YOffset;
	stbtt_fontinfo FontInfo;
	stbtt_InitFont(&FontInfo, (uint8 *)FontFile.Contents, stbtt_GetFontOffsetForIndex((uint8 *)FontFile.Contents, 0));
	uint8 *MonoBitmap = stbtt_GetCodepointBitmap(&FontInfo, 0, stbtt_ScaleForPixelHeight(&FontInfo, (float)GlobalFontRenderSize),
	                    Letter, &Width, &Height, &XOffset, &YOffset);

	font_codepoint *NextCodepoint = &GameState->AlphabetBitmaps[GameState->AlphabetBitmapsCount];
	GameState->AlphabetBitmapsCount++;

	NextCodepoint->BaselineFactor = YOffset;
	NextCodepoint->Bitmap.Width = Width;
	NextCodepoint->Bitmap.Height = Height;
	NextCodepoint->Bitmap.GLTexture = (GLuint)VirtualAlloc(0, sizeof(*MonoBitmap), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

	SIZE_T BitmapSize = sizeof(uint32) * Width * Height;
	void *ConvertedBitmap = malloc(BitmapSize);

	uint32 Pitch = Width * sizeof(uint32);
	uint8 *Source = (uint8 *)MonoBitmap;
	uint8 *DestRow = (uint8 *)ConvertedBitmap + ((Height - 1) * Pitch);
	for (uint32 Y = 0;
	     Y < (uint32)Height;
	     ++Y)
	{
		uint32 *Dest = (uint32 *)DestRow;
		for (uint32 X = 0;
		     X < (uint32)Width;
		     ++X)
		{
			uint8 MonoAlpha = *Source++;

			uint8 Bit2 = MonoAlpha; // A
			uint8 Bit3 = MonoAlpha; // R
			uint8 Bit0 = MonoAlpha; // G
			uint8 Bit1 = MonoAlpha; // B

			*Dest++ = ((Bit0 << 24) | (Bit1 << 16) | (Bit2 << 8) | (Bit3 << 0));
		}

		DestRow -= Pitch;
	}

	//TODO pull this out into a separate GL function to keep gl code separate from game code.
	glGenTextures(1, &NextCodepoint->Bitmap.GLTexture);
	glBindTexture(GL_TEXTURE_2D, NextCodepoint->Bitmap.GLTexture);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height,
	             0, GL_RGBA, GL_UNSIGNED_BYTE, ConvertedBitmap);

	VirtualFree(ConvertedBitmap, BitmapSize, MEM_RELEASE);
	stbtt_FreeBitmap(MonoBitmap, 0);
}