最近在使用Libgdx進行遊戲大廳開發,遇到這種需求:爲個別文本控件(Label)設置純色透明的圓角矩形背景。java
Libgdx中的Label是提供背景設置的:對Label的Style的background屬性進行設置便可,這個background是個Drawable,可使用圖片做爲Label的背景,很好很強大,但我這個項目中的Label背景只須要一種透明顏色而已,用圖片來實現的話我以爲並非一種很好的方式(有種殺雞用牛刀的感受)。想來想去,認爲Libgdx中的Pixmap能夠幫助我實現這種需求,由於Pixmap是能夠被用來繪製一個簡單圖形的,以後將pixmap轉換成drawable賦值給background就行了:wordpress
Drawable bg = new TextureRegionDrawable(new TextureRegion(new Texture(pixmap)));
label.getStyle().background = bg;
複製代碼
然而,pixmap只提供了以下幾種繪製圖形的方法:google
pixmap.drawLine() // 畫線
pixmap.drawRectangle(); // 畫矩形
pixmap.drawCircle(); // 畫環
pixmap.fillTriangle(); // 填充三角形
pixmap.fillRectangle(); // 填充矩形
pixmap.fillCircle(); // 填充圓形
複製代碼
我要的圓角矩形正好沒有(畢竟圓角矩形不是簡單圖形是吧。。。),因而,通過google大法及本人的"縝密"思考以後,純色透明圓角矩形實現出來了,本篇將記錄兩種實現圓角矩形的方案,下面開始進入正題。spa
這個方案借鑑了一個歪果人的博文,本文爲我以後的方案二作了啓發,這裏就先把地址貼出來,方便從此再翻出來欣賞:翻譯
下面就開始強行「翻譯」一下。code
繪製出一個圓角矩形,實際上,能夠經過使用填充了相同的顏色的2個矩形和4個圓圈來實現,這幾個圖形的擺放以下圖所示。orm
經過上圖,能夠很清晰的明白原做者的實現思想,下面就開始碼代碼(copy):cdn
public Pixmap getRoundedRectangle(int width, int height, int radius, int color) {
Pixmap pixmap = new Pixmap(width, height, Pixmap.Format.RGBA8888);
pixmap.setColor(color);
// Pink rectangle
pixmap.fillRectangle(0, radius, pixmap.getWidth(), pixmap.getHeight() - 2 * radius);
// Green rectangle
pixmap.fillRectangle(radius, 0, pixmap.getWidth() - 2 * radius, pixmap.getHeight());
// Bottom-left circle
pixmap.fillCircle(radius, radius, radius);
// Top-left circle
pixmap.fillCircle(radius, pixmap.getHeight() - radius, radius);
// Bottom-right circle
pixmap.fillCircle(pixmap.getWidth() - radius, radius, radius);
// Top-right circle
pixmap.fillCircle(pixmap.getWidth() - radius, pixmap.getHeight() - radius, radius);
return pixmap;
}
複製代碼
爲了直觀的看出效果,我把Demo的舞臺背景渲染爲黑色,圓角矩形設置爲白色,下面列出demo中的部分代碼:blog
Texture roundedRectangle = new Texture(getRoundedRectangle(color, width, height, radius));
Image image = new Image(roundedRectangle);
image.setPosition(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2, Align.center);
addActor(image);
複製代碼
效果很棒,不得不說,歪果人的想法仍是挺好的,可是,當我把圓角矩形的顏色設置爲白色透明時,這效果就噁心了,這裏貼出白透明色的設置代碼:
Color color = new Color(1, 1, 1, 0.5f);
複製代碼
爲何會這樣,仔細想一想就能明白,這是由於pixmap在繪製這幾個圖形時,它們的重合部分透明度疊加了。
既然知道了緣由,那有什麼解決辦法呢?這裏列出我能想到的2個辦法:
第一個方法我以爲是比較好的,感受實現上比較簡單可靠,然而我始終沒有找到能夠對pixmap設置總體透明度的方法,因而我這裏採用了第二個方法來實現:
public Pixmap getRoundedRectangle(Color color, int width, int height, int radius) {
Pixmap pixmap = new Pixmap(width, height, Pixmap.Format.RGBA8888);
// 一、保存原先的透明度
float alpha = color.a;
// 二、將透明度設置爲1以後開始繪製圓角矩形
color.set(color.r, color.g, color.b, 1);
pixmap.setColor(color);
// Pink rectangle
pixmap.fillRectangle(0, radius, pixmap.getWidth(), pixmap.getHeight() - 2 * radius);
// Green rectangle
pixmap.fillRectangle(radius, 0, pixmap.getWidth() - 2 * radius, pixmap.getHeight());
// Bottom-left circle
pixmap.fillCircle(radius, radius, radius);
// Top-left circle
pixmap.fillCircle(radius, pixmap.getHeight() - radius, radius);
// Bottom-right circle
pixmap.fillCircle(pixmap.getWidth() - radius, radius, radius);
// Top-right circle
pixmap.fillCircle(pixmap.getWidth() - radius, pixmap.getHeight() - radius, radius);
// 三、若是原來的背景色存在透明度,則須要對圖形總體重繪一次
if (alpha != 1) {
Pixmap newPixmap = new Pixmap(pixmap.getWidth(), pixmap.getHeight(), pixmap.getFormat());
int r = ((int) (255 * color.r) << 16);
int g = ((int) (255 * color.g) << 8);
int b = ((int) (255 * color.b));
int a = ((int) (255 * alpha) << 24);
int argb8888 = new Color(r | g | b | a).toIntBits();
for (int y = 0; y < pixmap.getHeight(); y++) {
for (int x = 0; x < pixmap.getWidth(); x++) {
int pixel = pixmap.getPixel(x, y);
if ((pixel & color.toIntBits()) == color.toIntBits()) {
newPixmap.drawPixel(x, y, argb8888);
}
}
}
pixmap.dispose();
pixmap = newPixmap;
}
return pixmap;
}
複製代碼
來看下效果,嗯,還能夠吧。
雖然用2個pixmap的方式能夠"完美"地繪製出純色透明圓角矩形,可是,每建立出1個透明圓角矩形都必須建立出2個pixmap來爲之輔助,儘管最後會對舊的pixmap進行dispose,但總感受這種方案不是並最優方式。
經過一番思考以後,我得出了這樣一個結論:
既然最後在使用到第2個pixmap的時候須要遍歷全部像素點來從新繪製一遍,那我乾脆直接進行第2步(第1步繪製不透明矩形的步驟不要了),在遍歷全部像素的時候把須要繪製到pixmap的像素點繪製出來不就行了嗎?這樣作還能夠省掉一個pixmap的開銷。
那麼如今的問題就是,我怎麼知道哪些像素應該被繪製,哪些像素不要被繪製呢?其實能夠把圓角矩形當作是一個不完整的有缺角的矩形,而這些缺角正好就是不用被繪製的那些像素點。
經過觀察,能夠知道,四個缺角中的像素都有以下相同點:
根據結論,代碼實現以下:
public Pixmap getRoundedRectangle(Color color, int width, int height, int radius) {
Pixmap pixmap = new Pixmap(width, height, Pixmap.Format.RGBA8888);
pixmap.setColor(color);
for (int y = 0; y < pixmap.getHeight(); y++) {
for (int x = 0; x < pixmap.getWidth(); x++) {
if ((x >= 0 && x <= radius) && (y >= 0 && y <= radius)) { // bottom-left
if (Math.sqrt((radius - x) * (radius - x) + (radius - y) * (radius - y)) > radius) {
continue;
}
} else if ((x >= 0 && x <= radius) && (y >= (height - radius) && y <= height)) { // top-left
if (Math.sqrt((radius - x) * (radius - x) + ((height - radius) - y) * ((height - radius) - y)) > radius) {
continue;
}
} else if ((x >= (width - radius) && x <= width) && (y >= 0 && y <= radius)) {// bottom-right
if (Math.sqrt(((width - radius) - x) * ((width - radius) - x) + (radius - y) * (radius - y)) > radius) {
continue;
}
} else if ((x >= (width - radius) && x <= width) && (y >= (height - radius) && y <= height)) {// top-right
if (Math.sqrt(((width - radius) - x) * ((width - radius) - x) + ((height - radius) - y) * ((height - radius) - y)) > radius) {
continue;
}
}
pixmap.drawPixel(x, y);
}
}
return pixmap;
}
複製代碼
爲了方便理解,下面列出各個缺角的圓心與小矩形x與y的取值範圍:
// bottom-left
// ------------圓心:(radius, radius)
// ------------矩形:([0,radius], [0,radius])
// top-left
// ------------圓心:(radius, height-radius)
// ------------矩形:([0,radius], [height-radius,height])
// bottom-right
// ------------圓心:(width-radius,radius)
// ------------矩形:([width-radius,width], [0,radius])
// top-right
// ------------圓心:(width-radius,height-radius)
// ------------矩形:([width-radius,width], [height-radius,height])
複製代碼
結果是OK的,與方案一繪製出來的透明圓角矩形一致,而且少了一個pixmap的開銷。
最後,想多說兩句,Libgdx做爲一款優秀的Android端遊戲開發引擎,網上的資料卻至關的少,不少東西就算Google了也不必定能找到答案,本人也是最近纔對其進行了解並上手使用,對於本文中所說的需求或許並非最好的解決方式,若是您有什麼好的解決方案或建議,請不吝賜教,thx。