寫(xiě)在前面的話:
JAVA 應(yīng)用結(jié)構(gòu)簡(jiǎn)單,易于編寫(xiě),能夠輕易完成高強(qiáng)度的復(fù)雜交互,并且安全性高,穩(wěn)定性強(qiáng),免費(fèi)資源豐富,網(wǎng)絡(luò)功能強(qiáng)大,擁有近乎完美的多線程機(jī)制。有必要的前提下, Java 程序員甚至可以使用 JNI 直接與本地環(huán)境溝通,從而繞過(guò)虛擬機(jī)的性能制約。
而 JAVA 應(yīng)用的跨平臺(tái)特性,更(理論上)讓其可以運(yùn)行于任何系統(tǒng)和平臺(tái)之上,最大限度的增加了程序的通用可能。
從本質(zhì)上講,無(wú)論你以 Java 開(kāi)發(fā)桌面應(yīng)用也好,網(wǎng)頁(yè)應(yīng)用也罷,其實(shí)并沒(méi)有明顯的界線存在。究其根本,無(wú)非是使用 Applet/JApplet/JavaFX 當(dāng)做容器,抑或 AWT/Swing/SWT 當(dāng)作容器的區(qū)別罷了。
快捷、靈活、通用、穩(wěn)定,以上這些優(yōu)勢(shì),原本足以讓 JAVA 將成為未來(lái)網(wǎng)頁(yè)游戲乃至中小型桌面游戲開(kāi)發(fā)的主流語(yǔ)言之一。
然而, Java 的運(yùn)行效率問(wèn)題,似乎卻成了這一些美好前景的絆腳石。更直接的說(shuō),有一些人武斷的認(rèn)為, Java “緩慢”的運(yùn)行速度,讓它根本不適合作為游戲客戶端之用。
即便自 JDK1.6 起 Java 的圖形渲染能力已經(jīng)有了顯著提升,即便國(guó)外像 RuneScape 之類的 Java3D 網(wǎng)頁(yè)游戲已經(jīng)上線盈利很多年( PS :順便鄙視下 Jagex 最近對(duì) RuneScape 作的人物屬性調(diào)整……),即便連 NetBeans 的運(yùn)行速度都已經(jīng)變得能同普通桌面程序不遑多讓。但是,某些自 2004 年后或許從未接觸過(guò)新技術(shù)的家伙, 依舊樂(lè)此不疲的散布著有關(guān) Java 性能的流言蜚語(yǔ)。
在某些落伍人士眼里, Java 如同洪水猛獸,又好像是他們天生的對(duì)頭。他們甚至寧愿選擇某些行將就木的技術(shù),他們甚至寧愿將某些只適合做低成本動(dòng)畫(huà)的東西視為命根,他們甚至寧愿花大力氣去處理那些因?yàn)椴恢С謱?shí)際多線程、 CPU 占用過(guò)高、硬件加速不到位、資源回收異常等等問(wèn)題而引發(fā)的致命 BUG ,也不愿意去多了解一下 Java 。他們將一種原本可以帶來(lái)巨大商業(yè)利益的語(yǔ)言視若等閑,他們寧愿讓自己的雇主花費(fèi)數(shù)倍的精力與財(cái)力去打造垃圾,也不愿意讓雇主和公司擁有接觸到更為優(yōu)秀技術(shù)的機(jī)會(huì)。
不得不說(shuō),這即是 Java 的遺憾,更是某些落伍人士雇主及其公司,乃至整個(gè)游戲產(chǎn)業(yè)的遺憾。
當(dāng)然,一味的指責(zé)他人,就成了抱怨,勢(shì)必會(huì)犯“有嘴說(shuō)別人,沒(méi)嘴說(shuō)自己”的民族通病。事實(shí)上,人們對(duì)于 Java 性能方面之所以會(huì)產(chǎn)生誤解,除了旁人的傲慢與偏見(jiàn)外,自然也同 Java 自身的發(fā)展歷程密不可分(具體原因我在其它的博文中已經(jīng)闡述過(guò)很多次,此處不再贅述)。
但總體上講,除了原 Sun 公司本身的不作為,以及 Java 偏向企業(yè)級(jí)開(kāi)發(fā),偏向服務(wù)器端開(kāi)發(fā)的大環(huán)境影響外。 Java 進(jìn)行游戲開(kāi)發(fā),或者說(shuō)桌面開(kāi)發(fā)的最大缺陷,就在于其圖形開(kāi)發(fā)方面,特別是有關(guān)于渲染優(yōu)化方面,乃至整個(gè) Java 游戲開(kāi)發(fā)領(lǐng)域的書(shū)籍資料都嚴(yán)重匱乏。沒(méi)錯(cuò),相比浩如煙海的 Java 服務(wù)器端技術(shù)資料而言, Java 游戲開(kāi)發(fā)方面的資源鳳毛麟角。在某個(gè)黑暗時(shí)期中,甚至連 Java 版的貪食蛇、俄羅斯方塊、超級(jí)馬里奧之類的資源都會(huì)被人視為經(jīng)典。
不客氣地說(shuō),如果憑那些東西就想讓人樹(shù)立對(duì)于 Java 游戲開(kāi)發(fā)的信心,就算不等于癡人說(shuō)夢(mèng),至少也是難于登天了。
而如果想要解決這個(gè)問(wèn)題,那么更多的示例,以及更多的技術(shù)文章將必不可少。為此,筆者才會(huì)產(chǎn)生創(chuàng)作此系列博文的意愿,唯恨椽筆拙文,權(quán)作引玉之磚。
正文: BufferedImage 與像素級(jí)渲染
常有人說(shuō) Java 圖形渲染很慢?嗯,相對(duì) C/C++ 而言, Java2D 固有的圖像處理能力確實(shí)有待提高。
但是,這也僅僅局限于對(duì)比 C/C++ 應(yīng)用而言。
如果您是以其它什么東西與之比較,卻得出 Java 渲染很慢的結(jié)論。那么,或者并不是出自 Java 本身的原因,而在于您并沒(méi)能搞清楚該怎樣正確的使用 Java 繪圖。
況且,即便是相對(duì)于 C/C++ 而談, Java 也并非相差到難以望其項(xiàng)背的地步。相對(duì)于某些行將就木的技術(shù),至少我們除了異常積極的自行修改 JRE ,或者極端消極的等待 JRE 官方更新以外,還有使用 OpenGL 或者像素級(jí)優(yōu)化這兩條道路可走。
在本節(jié)當(dāng)中,我們就先談點(diǎn)基礎(chǔ)的,來(lái)說(shuō)說(shuō) Java 渲染的像素級(jí)優(yōu)化吧。
像素與 RGB :
像素是什么?簡(jiǎn)單的講,像素就是色彩,像素是系統(tǒng)能夠在計(jì)算機(jī)屏幕上顯示的最小染色點(diǎn)。越高位的像素,其擁有的色板也就越豐富,越能表達(dá)顏色的真實(shí)感。
眾所周知,圖像是像素的復(fù)合,看似絢麗的形象,也無(wú)外是一個(gè)個(gè)肉眼難以分辨的細(xì)微顆粒集合罷了。
比如,在一些常見(jiàn)的 Java 圖像處理中,我們經(jīng)常會(huì)用到所謂的 RGB24 模式( 24 位三原色模式,在 Java2D 中以 TYPE_INT_RGB 表示),將 Red , Green , Blue 三種色彩加以混合,創(chuàng)造出唯一的色彩點(diǎn)并繪制到計(jì)算機(jī)之上。而這個(gè)色彩點(diǎn),也就是所謂的像素。因?yàn)樵?/span> RGB24 中 Red , Green , Blue 三者都被分配有一個(gè) 0~255 的強(qiáng)度值,所以該 RGB 模式的極限機(jī)能就是 256*256*256 ,即至多可以顯示出 16777216 種顏色。
PS :關(guān)于 16 位的 RGB565 ( Java2D 中表示為 TYPE_USHORT_565_RGB )以及 RGB555 ( Java2D 中表示為 TYPE_USHORT_555_RGB )會(huì)在以后章節(jié)中涉及,大家此刻只要知道,使用 24 位以下的圖形處理模式,在顯示速度上雖然會(huì)有提高,視覺(jué)效果上卻必然會(huì)有損失就可以了。
也許有網(wǎng)友會(huì)感嘆。哇! 16777216 種顏色,這么多?難道都能用上嗎?!
沒(méi)錯(cuò), 16777216 種顏色確實(shí)很多;事實(shí)上,這已非常接近于人類肉眼所能觀察到的顏色數(shù)目極限 , 所以我們又將它稱之為真彩色。然而,人類的欲求卻是無(wú)止境的,即便能夠展現(xiàn)出 16777216 種顏色的 RGB 真彩模式,依舊有人嫌棄它的效果太差。
否則,在您計(jì)算機(jī)“顏色質(zhì)量”一欄中,或許就不會(huì)再有 32 位這種“多余”的選擇了。
正是因?yàn)槿祟愄煨缘呢澙罚?dāng)今 2D 、 3D 圖形渲染中最為常見(jiàn)的 ARGB 模式,也就是 32 位真彩模式才會(huì)應(yīng)運(yùn)而生。
ARGB 模式:
您問(wèn)什么是 ARGB ?其實(shí),它就是個(gè)穿了 Alpha 通道馬甲的 RGB 。
事實(shí)上,較之最初的 RGB 模式, ARGB 僅僅增加了一個(gè)名為 Alpha 的色彩通道。這是一個(gè) 8 位的灰度通道,用 256 級(jí)灰度來(lái)記錄圖像中的透明度信息,定義透明、不透明和半透明區(qū)域。通俗的說(shuō),你的 ARGB 圖像是否透明,與底層圖像的遮擋關(guān)系如何,都將由 Alpha 這個(gè)參數(shù)所決定。
在 Java2D 中, TYPE_INT_ARGB 象征著 32 位十六進(jìn)制數(shù)的 ARGB 色彩模式。
將“ 32 位十六進(jìn)制數(shù)”的概念具象化后,也就是四對(duì)十六進(jìn)制數(shù)字的序列。每個(gè)十六進(jìn)制對(duì)定義四個(gè)顏色通道,即 Red 、 Green 、 Blue 和 Alpha 中每個(gè)顏色通道的強(qiáng)度,全以范圍介于 0 到 255 之間的十進(jìn)制數(shù)的十六進(jìn)制表示法。(在 16 進(jìn)制表示中, FF 是指全強(qiáng)度 ,最高的 255 。 00 是指通道中無(wú)顏色,最低為 0 )
正如大家都知道的那樣 , 由于顏色值長(zhǎng)度需要兩位數(shù)字 , 因此您需要填充一個(gè)通道 , 例如用 01 代替 1 ,這樣才可確保十六進(jìn)制數(shù)中始終具有八個(gè)數(shù)字。還應(yīng)確保指定十六進(jìn)制數(shù)前綴 0x ,這樣才能被 Java 識(shí)別為 16 進(jìn)制。
例如,白色 ( 全強(qiáng)度 ) 用十六進(jìn)制記數(shù)法表示為 : 0xFFFFFFFF 。而黑色正好相反;它在紅色、綠色和藍(lán)色中的任何一個(gè)通道中都 無(wú)顏色,結(jié)果就成了 : 0xFF000000 。請(qǐng)注意 , Alpha 通道中的全強(qiáng)度意味著沒(méi)有 Alpha (FF) ,也就是不透明 , 而無(wú)強(qiáng)度 (00) ,則意味著全透明。
利用 ARGB 模式,我們可以輕易的創(chuàng)建出一些 RGB 所無(wú)法實(shí)現(xiàn)的艷麗圖像,完成一些 RGB 所無(wú)法企及的繽紛效果。應(yīng)該說(shuō),如果您只是想制作一個(gè)讓人可以入目的畫(huà)面,那么普通的 RGB 模式已然游刃有余,但如果您想百尺竿頭更進(jìn)一步,制作出一些讓人心曠神怡的視覺(jué)盛宴,那就非 ARGB 不可。而一旦您開(kāi)始使用 ARGB ,就與 Alpha 、 Red 、 Green 、 Blue 這四層色彩通道留下了不解之緣。
在 Java 中獲得 ARGB 像素的方法如下:
public static int getARGB( int r, int g, int b, int alpha) {
return (alpha << 24) | (r << 16) | (g << 8) | b;
}
關(guān)于 BufferedImage :
當(dāng)我們需要使用像素級(jí)操作,當(dāng)我們需要設(shè)定針對(duì)不同圖像的不同色彩模式時(shí),最直接有效的方法,就是使用 BufferedImage 。
事實(shí)上,就像深入優(yōu)化 Flash 渲染必須利用 BitmapData 一樣,沒(méi)有對(duì) BufferedImage 的相關(guān)了解,提高 Java2D 性能根本無(wú)從談起,甚至不能說(shuō)你會(huì)用 Java2D 。
當(dāng)您想要?jiǎng)?chuàng)建 BufferedImage ,并對(duì)其中像素進(jìn)行直接操作時(shí),大體上有三種方式可選:
1 、直接創(chuàng)建 BufferedImage ,導(dǎo)出 DataBufferInt 對(duì)象獲取像素集合。
// 創(chuàng)建一個(gè) 640x480 的 BufferedImage ,設(shè)定渲染模式為 ARGB
BufferedImage image = new BufferedImage (640, 480,
BufferedImage . TYPE_INT_ARGB );
// 獲得當(dāng)前 BufferedImage 的圖像數(shù)據(jù) 存儲(chǔ)器,并轉(zhuǎn)為 DataBufferInt
DataBufferInt dataBuffer = ((DataBufferInt) image.getRaster()
.getDataBuffer());
// 獲得對(duì)應(yīng) BufferedImage 的像素?cái)?shù)組
int [] pixels = dataBuffer.getData();
2 、以 int[] 生成 WritableRaster ,以 WritableRaster 產(chǎn)生 BufferedImage 。
// 設(shè)定 BufferedImage 的寬與高
int width = 640, height = 480;
int size = width * height;
// 創(chuàng)建數(shù)組,用以保存對(duì)應(yīng) BufferedImage 的像素集合
int [] pixels = new int [size];
// 以指定數(shù)組創(chuàng)建出指定大小的 DataBuffer
DataBuffer dataBuffer = new DataBufferInt(pixels, size);
// 創(chuàng)建一個(gè) WritableRaster 對(duì)象,用以 管理光柵
WritableRaster raster = Raster.createPackedRaster (dataBuffer, width, height,width, new int [] { 0xFF0000, 0xFF00, 0xFF }, null );
// 創(chuàng)建一個(gè) 24 位的 RGB 色彩模型,并填充相應(yīng)的 R 、 G 、 B 掩碼
DirectColorModel directColorModel = new DirectColorModel(24, 0xFF0000, 0xFF00, 0xFF);
// 以下為 32 位 RGB 色彩模型
// DirectColorModel directColorModel = new DirectColorModel(32, 0xFF000000, 0xFF0000, 0xFF00, 0xFF);
// 生成 BufferedImage, 預(yù)設(shè) Alpha ,無(wú)配置
BufferedImage image = new BufferedImage(directColorModel, raster, true , null );
3 、與方法 2 基本相同,唯一差別在于使用了 SampleModel
int width = 640, height = 480;
int size = width * height;
int [] pixels = new int [size];
// 24 位色彩模型
DirectColorModel directColorModel = new DirectColorModel(24, 0xFF0000,
0xFF00, 0xFF);
// 以 SinglePixelPackedSampleModel 構(gòu)建像素包
SampleModel sample = new SinglePixelPackedSampleModel(
DataBuffer . TYPE_INT , width, height, new int [] { 0xFF0000,
0xFF00, 0xFF });
// 生成 DataBuffer
DataBuffer dataBuffer = new DataBufferInt(pixels, size);
// 以 SampleModel 及 DataBuffer 生成 WritableRaster
WritableRaster raster = Raster.createWritableRaster (sample, dataBuffer,
new Point(0, 0));
// 生成 BufferedImage
BufferedImage image = new BufferedImage(directColorModel, raster, true , null );
實(shí)際上,雖然表面上有所不同,但無(wú)論您采用以上何種方式獲得 BufferedImage 及其對(duì)應(yīng)的像素集合( PS: 此處并非一定要獲得像素的 int[] 形式,如 short[] 、 byte[] 等各式亦可,請(qǐng)根據(jù)實(shí)際需求決定), pixels 對(duì)您而言都將成為一塊保存有圖像數(shù)據(jù)的內(nèi)存區(qū)域,針對(duì)此 pixels 進(jìn)行的任何修改,都將被直接反饋于 BufferedImage 之上。
得到了像素集合,我們又該如何將其應(yīng)用到 Java2D 中呢?下面,我將介紹兩個(gè)像素級(jí) Java 渲染組件給大家參考。下面我們所使用到的一切操作,也都將圍繞 pixels 這個(gè)以 int[] 形式出現(xiàn)的數(shù)組展開(kāi)。
一、 古董級(jí)的 Processing
項(xiàng)目地址: http://processing.org/
這是一套完整的,開(kāi)源的,兼顧 2D 與 3D 方面的 Java 渲染組件。事實(shí)上, Processing 在針對(duì) Java2D 性能優(yōu)化上的意義并不太大,因?yàn)樗緛?lái)就不是為了解決性能問(wèn)題而出現(xiàn)的。
Processing 所做的,更多的是一種效果優(yōu)化,一種對(duì) Java 語(yǔ)言的延伸。它希望人們能利用它對(duì) Java 的擴(kuò)充,以簡(jiǎn)單高效的方式實(shí)現(xiàn)絢麗奪目的圖形效果。應(yīng)該說(shuō), Processing 將 Java 的語(yǔ)法簡(jiǎn)化并將其運(yùn)算結(jié)果 “ 感官化 ” ,讓使用者能很快享有聲光兼?zhèn)涞慕换ナ蕉嗝襟w作品。
由于 Processing 運(yùn)行于 PApplet 之上,而 PApplet 繼承自 Applet 。也就是說(shuō)原本的 Processing 也是一種小程序,如果我們要將它應(yīng)用在網(wǎng)頁(yè)環(huán)境之外,要們就將 PApplet 插入到 Frame/JFrame 當(dāng)中,要么就將其改寫(xiě)。
為了未來(lái)的演示更加方便,筆者選擇了改寫(xiě)的道路,將其 PGraphics 渲染層直接封裝。以下,是一個(gè)已經(jīng)替換為 Processing 渲染的 LGame 示例:
二、 新生代的 PulpCore
項(xiàng)目地址: http://www.interactivepulp.com/pulpcore/
事實(shí)上, PulpCore 在國(guó)外的 Java 圈中也算頗有名氣,甚至連某位 JavaFX 開(kāi)發(fā)者都曾以它和自己的項(xiàng)目作過(guò)比較。如果有朋友泡過(guò) http://www.javagaming.org/ ,想必應(yīng)該知道,如果你在該論壇中尋求 Java 游戲框架,那么 3D 方面的優(yōu)先推薦必然是 JME , 2D 方面的優(yōu)先推薦絕對(duì)是 Slick2D ,至于網(wǎng)頁(yè)游戲開(kāi)發(fā)方面,則必屬 PulpCore 無(wú)疑。
在以 OpenGL 為絕對(duì)主流的 javagaming 上,一款以標(biāo)準(zhǔn) Java2D 開(kāi)發(fā)的框架,居然會(huì)受到如此推崇, PulpCore 的技術(shù)價(jià)值我們可想而知。
下圖為 PulpCore 提供的應(yīng)用示例:
PS :雖然 PulpCore 所提供的示例多為小游戲,但該作者曾反復(fù)強(qiáng)調(diào), PulpCore 是一個(gè)開(kāi)源的 2D 渲染和動(dòng)畫(huà)處理框架。
與 Processing 一樣,啟動(dòng) PulpCore 的 CoreApplet 繼承自 Applet ,所以 PulpCore 依舊屬于 Applet 實(shí)現(xiàn),也就是默認(rèn)情況下只能運(yùn)行于網(wǎng)頁(yè)之上。但相對(duì)于標(biāo)準(zhǔn) Applet 應(yīng)用, PulpCore 卻做了更多的優(yōu)化,尤其注重用戶體驗(yàn)與動(dòng)畫(huà)效果。應(yīng)該說(shuō), Pulpcore 是目前為止筆者所見(jiàn)過(guò)的,在不損失圖像色彩的情況 下最高效的 Java2D 解決方案。
關(guān)于圖像渲染部分, PulpCore 中有對(duì)應(yīng)于標(biāo)準(zhǔn) Java2D 的 Graphics 類,名為 CoreGraphics 。其中對(duì)像素級(jí)操作進(jìn)行了必要的封裝,也基本參照標(biāo)準(zhǔn) Java2D API 命名。( PS :具體留待下節(jié)講解,目前請(qǐng)自行參考其源碼)不過(guò),或許是方便模塊化管理的緣故, CoreGraphics 默認(rèn)情況下并不對(duì)外開(kāi)放,而被統(tǒng)一封裝在 PulpCore 所提供的各種精靈類里。
如果您想要獲得 CoreGraphics 進(jìn)行修改,要么請(qǐng)重載 Sprite 的 draw( 需要 super.draw 一下,否則會(huì)覆蓋到基礎(chǔ)操作 ) ,要么請(qǐng)?jiān)?/span> Scene2D 中重載 drawScene (需要 super.drawScene 一下,否則會(huì)覆蓋到基礎(chǔ)操作), PulpCore 并沒(méi)有直接提供給您。對(duì)于僅想進(jìn)行簡(jiǎn)單圖形繪制的用戶而言,這不得不說(shuō)是一個(gè)小小的不足。
另外,雖然 PulpCore 也有對(duì)應(yīng)于 Font 的 CoreFont 類,但相比于 Processing 的字體繪制方案,它明顯寒酸了很多。
實(shí)際上, PulpCore 中的 CoreFont 只是一個(gè)分圖管理器,由用戶導(dǎo)入一張由英文字母及各種符號(hào)組成的圖像,而 CoreFont 負(fù)責(zé)分配不同的圖像對(duì)應(yīng)不同的字母繪制。這意味著,如果您不自行擴(kuò)充其 CoreFont 部分,那么 PulpCore 將絕對(duì)無(wú)法支持中文輸入及顯示。( PS :目前來(lái)說(shuō),最偷懶的方法就是將 Processing 中的 PFont 和 text 部分直接“移植”到 CoreGraphics 中使用。畢竟兩者都是操作像素繪制圖像,很好 copy ……)
針對(duì) PulpCore 的 CoreGraphics ,筆者也提供了一個(gè) LGame 的替代封裝,以方便后文講解分析其渲染方式之用。
示例源碼:
另外筆者還要補(bǔ)充一點(diǎn),那就是 PulpCore 雖然提供了較為完善的“臟繪”機(jī)制,卻必須和 Sprite 一起使用才能看到效果(被封裝到了 Sprite 的 draw 函數(shù)里,所以單就渲染速度而言,在 PulpCore 中使用精靈繪圖反而比不用更快)。
從下文開(kāi)始,筆者將以 PulpCore 為基礎(chǔ),逐步講解 Java 像素級(jí)渲染框架的設(shè)計(jì)與實(shí)現(xiàn)。
以下為以PulpCore與Processing進(jìn)行渲染的LGame實(shí)驗(yàn)工程:
http://loon-simple.googlecode.com/files/Pixels-LGame.7z
——————————————————————
心理學(xué)上有一個(gè)名詞叫做 The Halo Effect ,也就是俗稱的暈輪效應(yīng)或者說(shuō)“刻板印象”。在這種心理現(xiàn)象影響下,很多人往往會(huì)將某種事物的“第一印象”當(dāng)作終身的準(zhǔn)則,而無(wú)視其實(shí)際究竟是怎樣的。
其實(shí)對(duì)于 Java 桌面或網(wǎng)頁(yè)應(yīng)用而言,性能上的問(wèn)題早就已經(jīng)算不得什么問(wèn)題。只要人們稍微留心一下,就會(huì)發(fā)現(xiàn) Java 在游戲開(kāi)發(fā)方面,至少在網(wǎng)頁(yè)游戲方面完全可以比某些東西作的更好,更強(qiáng),更復(fù)雜,更快捷,也更穩(wěn)定。真正關(guān)鍵的,反倒是那些聽(tīng)信了流言蜚語(yǔ)的人們對(duì)于 Java 性能上的誤解,以及食古不化的偏見(jiàn),才是真正制約 Java 發(fā)展的攔路虎。
所謂積重難返,要想扭轉(zhuǎn)這種頑固偏見(jiàn),只憑小弟一人是絕對(duì)不足夠的,還要靠各位 Java 同仁的努力。
都說(shuō)“荒田無(wú)人耕,耕開(kāi)有人爭(zhēng)”,可都等著別人耕田,畢竟太慢,始終沒(méi)有自己動(dòng)手那么快捷。畢竟只有將 Java 做大做強(qiáng),各位同仁才能有更多的出路,更好的待遇,以及更多的 Money 好賺……
浙公網(wǎng)安備 33010602011771號(hào)