2019年4月29日 星期一

用canvas2d繪製font texture的注意事項

用canvas2d繪製font texture的注意事項

前言

  用canvas2d繪製font可以輕鬆地找到範例,但繪製時有些需要注意的事,在此把過程做個紀錄。

內容

  由於WebGL本身不提供字型,所以需要產生font texture,產生的流程如下圖
產生font texture的流程

繪製字型是用canvas2d繪製後輸出到image,再來將imgae的資料輸出到WebGL的texture,之後WebGL再利用該texture來繪製font,整個流程不是很簡潔,但是我目前找到的方法中最好的。

  基本的繪製範例如下
let canvas=document.getElementById('myCanvas');
let ctx=canvas.getContext('2d');
canvas.width=300;
canvas.height=300;
let fontSize=36;
let str='|_jx/|';
ctx.fillStyle='#ff0000';
ctx.font=fontSize+'px sans-serif';
ctx.textBaseline='top';
for(let i=0,curOffset=0;i<str.length;i++){
  let width=ctx.measureText(str[i]).width;
  ctx.fillStyle='#ff0000ff';
  ctx.fillText(str[i],curOffset,0);

  curOffset+=width;
}

繪製的結果如下
範例繪製的結果

繪製的結果看起來沒問題,但事實上有問題,請看"_"與"j"連接的部分會連在一起,這對font texture而言可不是好事,如果WebGL在繪製"_"時會看到與"j"的部分,再想想如果將"_"改成"A",在繪製"A"時下方會多出"j"的一部分,這樣是無法讓人接受的。為什麼會發生這個問題呢?看看範例是如何知道該字的寬,"ctx.measureText()"就這個method,這是唯一可以得到字寬的方法,字高呢?font size有多高該字就多高,像"_"與"A"的高明顯不同,但只能把字型高的空間整個拿去畫。

   前面提到透過"ctx.measureText()"來取得字寬,但"j"這個字很特別,它的左側會會超出繪製的字寬,如下圖
"j"的字寬很特別

第一個"j"的左側會被截掉,是因為超出canvas的左側,第二個"j"就可以看到該字的左側會超出邊框,這就是問題了。這問題怎麼解決呢?目前我想到的是針對"j"來做特例處理,讓"j"的左側會多出一些空間來包住"j",具體的範例如下
let canvas=document.getElementById('myCanvas');
let ctx=canvas.getContext('2d');
canvas.width=300;
canvas.height=300;
let fontSize=36;
let str='jj|_jx/|';
let modWidth=10;
ctx.fillStyle='#ff0000';
ctx.font=fontSize+'px sans-serif';
ctx.textBaseline='top';
for(let i=0,curOffset=0;i<str.length;i++){
  let width=ctx.measureText(str[i]).width;
  if(str[i] === 'j')
    modWidth=5/72 *fontSize;
  else
    modWidth=0;
  ctx.fillStyle='#ff0000ff';
  ctx.fillText(str[i],curOffset+modWidth,0);
  ctx.strokeRect(curOffset+modWidth,0,width,fontSize);

  curOffset+=width+modWidth;
}

這個範例會對"j"這個字的左側增加"5/72"的寬度,這個寬度是我實驗的寬度,可以依據需要自己改,並且會考慮新增的寬度繪製新的邊框(邊框的寬度還是原本的寬度,但字跟字不會連在一起了),繪製的結果如下
修正"j"的寬度的繪製結果

參考資料

Drawing text

沒有留言:

張貼留言