BufferedImage 与像素级渲染

常有人说 Java 图形渲染很慢?嗯,相对 C/C++ 而言, Java2D 固有的图像处理能力确实有待提高。

 

但是,这也仅仅局限于对比 C/C++ 应用而言。

 

如果您是以其它什么东西与之比较,却得出 Java 渲染很慢的结论。那么,或者并不是出自 Java 本身的原因,而在于您并没能搞清楚该怎样正确的使用 Java 绘图。

 

况且,即便是相对于 C/C++ 而谈, Java 也并非相差到难以望其项背的地步。相对于某些行将就木的技术,至少我们除了异常积极的自行修改 JRE ,或者极端消极的等待 JRE 官方更新以外,还有使用 OpenGL 或者像素级优化这两条道路可走。

 

在本节当中,我们就先谈点基础的,来说说 Java 渲染的像素级优化吧。

 

像素与 RGB

 

像素是什么?简单的讲,像素就是色彩,像素是系统能够在计算机屏幕上显示的最小染色点。越高位的像素,其拥有的色板也就越丰富,越能表达颜色的真实感。

 

众所周知,图像是像素的复合,看似绚丽的形象,也无外是一个个肉眼难以分辨的细微颗粒集合罢了。

 

比如,在一些常见的 Java 图像处理中,我们经常会用到所谓的 RGB24 模式( 24 位三原色模式,在 Java2D 中以 TYPE_INT_RGB 表示),将 Red Green Blue 三种色彩加以混合,创造出唯一的色彩点并绘制到计算机之上。而这个色彩点,也就是所谓的像素。因为在 RGB24 Red Green Blue 三者都被分配有一个 0~255 的强度值,所以该 RGB 模式的极限机能就是 256*256*256 ,即至多可以显示出 16777216 种颜色。

 

PS :关于 16 位的 RGB565 Java2D 中表示为 TYPE_USHORT_565_RGB )以及 RGB555 Java2D 中表示为 TYPE_USHORT_555_RGB )会在以后章节中涉及,大家此刻只要知道,使用 24 位以下的图形处理模式,在显示速度上虽然会有提高,视觉效果上却必然会有损失就可以了。

 

也许有网友会感叹。哇! 16777216 种颜色,这么多?难道都能用上吗?!

 

没错, 16777216 种颜色确实很多;事实上,这已非常接近于人类肉眼所能观察到的颜色数目极限 , 所以我们又将它称之为真彩色。然而,人类的欲求却是无止境的,即便能够展现出 16777216 种颜色的 RGB 真彩模式,依旧有人嫌弃它的效果太差。

 

否则,在您计算机“颜色质量”一栏中,或许就不会再有 32 位这种“多余”的选择了。

 

正是因为人类天性的贪婪,当今 2D 3D 图形渲染中最为常见的 ARGB 模式,也就是 32 位真彩模式才会应运而生。

 

ARGB 模式:

 

您问什么是 ARGB ?其实,它就是个穿了 Alpha 通道马甲的 RGB


00


 

00


 

 

例如,白色 ( 全强度 ) 用十六进制记数法表示为 : 0xFFFFFFFF 。而黑色正好相反;它在红色、绿色和蓝色中的任何一个通道中都无颜色,结果就成了 : 0xFF000000 。请注意 , Alpha 通道中的全强度意味着没有 Alpha (FF) ,也就是不透明 , 而无强度 (00) ,则意味着全透明。


00


 

public static int getARGB( int r, int g, int b, int alpha) {

        return (alpha << 24) | (r << 16) | (g << 8) | b;

}


关于 BufferedImage

 

当我们需要使用像素级操作,当我们需要设定针对不同图像的不同色彩模式时,最直接有效的方法,就是使用 BufferedImage

 

事实上,就像深入优化 Flash 渲染必须利用 BitmapData 一样,没有对 BufferedImage 的相关了解,提高 Java2D 性能根本无从谈起,甚至不能说你会用 Java2D

 

当您想要创建 BufferedImage ,并对其中像素进行直接操作时,大体上有三种方式可选:

 

1 、直接创建 BufferedImage ,导出 DataBufferInt 对象获取像素集合。

 

// 创建一个 640x480 BufferedImage ,设定渲染模式为 ARGB

BufferedImage image = new BufferedImage (640, 480,

              BufferedImage . TYPE_INT_ARGB );

// 获得当前 BufferedImage 的图像数据存储器,并转为 DataBufferInt

DataBufferInt dataBuffer = ((DataBufferInt) image.getRaster()

              .getDataBuffer());

// 获得对应 BufferedImage 的像素数组

int [] pixels = dataBuffer.getData();  


2 、以 int[] 生成 WritableRaster ,以 WritableRaster 产生 BufferedImage


// 设定 BufferedImage 的宽与高

int width = 640, height = 480;

int size = width * height;

// 创建数组,用以保存对应 BufferedImage 的像素集合

int [] pixels = new int [size];

// 以指定数组创建出指定大小的 DataBuffer

DataBuffer dataBuffer = new DataBufferInt(pixels, size);

// 创建一个 WritableRaster 对象,用以管理光栅

WritableRaster raster = Raster.createPackedRaster (dataBuffer, width, height,width, new int [] { 0xFF0000, 0xFF00, 0xFF }, null );

// 创建一个 24 位的 RGB 色彩模型,并填充相应的 R G B 掩码

DirectColorModel directColorModel = new DirectColorModel(24, 0xFF0000, 0xFF00, 0xFF);

// 以下为 32 RGB 色彩模型

// DirectColorModel directColorModel = new DirectColorModel(32, 0xFF000000, 0xFF0000, 0xFF00, 0xFF);

// 生成 BufferedImage, 预设 Alpha ,无配置

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 构建像素包

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 );

 

实际上,虽然表面上有所不同,但无论您采用以上何种方式获得 BufferedImage 及其对应的像素集合( PS: 此处并非一定要获得像素的 int[] 形式,如 short[] byte[] 等各式亦可,请根据实际需求决定), pixels 对您而言都将成为一块保存有图像数据的内存区域,针对此 pixels 进行的任何修改,都将被直接反馈于 BufferedImage 之上。

 

得到了像素集合,我们又该如何将其应用到 Java2D 中呢?下面,我将介绍两个像素级 Java 渲染组件给大家参考。下面我们所使用到的一切操作,也都将围绕 pixels 这个以 int[] 形式出现的数组展开。


一、 古董级的 Processing

 

项目地址: http://processing.org/

 

这是一套完整的,开源的,兼顾 2D 3D 方面的 Java 渲染组件。事实上, Processing 在针对 Java2D 性能优化上的意义并不太大,因为它本来就不是为了解决性能问题而出现的。

 

Processing 所做的,更多的是一种效果优化,一种对 Java 语言的延伸。它希望人们能利用它对 Java 的扩充,以简单高效的方式实现绚丽夺目的图形效果。应该说, Processing Java 的语法简化并将其运算结果 感官化 ,让使用者能很快享有声光兼备的交互式多媒体作品。

 

由于 Processing 运行于 PApplet 之上,而 PApplet 继承自 Applet 。也就是说原本的 Processing 也是一种小程序,如果我们要将它应用在网页环境之外,要们就将 PApplet 插入到 Frame/JFrame 当中,要么就将其改写。

 

为了未来的演示更加方便,笔者选择了改写的道路,将其 PGraphics 渲染层直接封装。以下,是一个已经替换为 Processing 渲染的 LGame 示例:

 

  1. public   class  ProcessingBall  extends  Screen {  
  2.    
  3.     class  Ball {  
  4.    
  5.        float  x;  
  6.    
  7.        float  y;  
  8.    
  9.        float  speed;  
  10.    
  11.        float  gravity;  
  12.    
  13.        float  w;  
  14.    
  15.        float  life =  255 ;  
  16.    
  17.        Ball(float  tempX,  float  tempY,  float  tempW) {  
  18.            x = tempX;  
  19.            y = tempY;  
  20.            w = tempW;  
  21.            speed = 0 ;  
  22.            gravity = 0 .1f;  
  23.        }  
  24.    
  25.        void  move() {  
  26.            speed = speed + gravity;  
  27.            y = y + speed;  
  28.            if  (y > getHeight()) {  
  29.               speed = speed * -0 .8f;  
  30.               y = getHeight();  
  31.            }  
  32.        }  
  33.    
  34.        boolean  finished() {  
  35.            life--;  
  36.            if  (life <  0 ) {  
  37.               return   true ;  
  38.            } else  {  
  39.               return   false ;  
  40.            }  
  41.        }  
  42.    
  43.        void  display(LPGraphics g) {  
  44.            g.fill(0 , life);  
  45.            g.ellipse(x, y, w, w);  
  46.        }  
  47.     }  
  48.    
  49.     private  ArrayList balls;  
  50.    
  51.     private   int  ballWidth =  48 ;  
  52.    
  53.     PImage image=Utils.loadImage("system/image/logo.png" );  
  54.      
  55.     public  ProcessingBall() {  
  56.        balls = new  ArrayList();  
  57.        balls.add(new  Ball(getWidth() /  2 0 , ballWidth));  
  58.     }  
  59.    
  60.     public   void  draw(LPGraphics g) {  
  61.        g.background(255 );  
  62.        for  ( int  i = balls.size() -  1 ; i >=  0 ; i--) {  
  63.            Ball ball = (Ball) balls.get(i);  
  64.            ball.move();  
  65.            ball.display(g);  
  66.            if  (ball.finished()) {  
  67.               balls.remove(i);  
  68.            }  
  69.        }  
  70.     }  
  71.    
  72.     public   void  leftClick(MouseEvent e) {  
  73.        balls.add(new  Ball(getMouseX(), getMouseY(), ballWidth));  
  74.     }  
  75.    
  76.     public   void  middleClick(MouseEvent e) {  
  77.    
  78.     }  
  79.    
  80.     public   void  rightClick(MouseEvent e) {  
  81.    
  82.     }  
  83.    
  84.     public   void  onKey(KeyEvent e) {  
  85.    
  86.     }  
  87.    
  88.     public   void  onKeyUp(KeyEvent e) {  
  89.    
  90.     }  
  91.    
  92.     public   static   void  main(String[] args) {  
  93.        GameScene frame = new  GameScene( "球体下落" 480 360 );  
  94.        Deploy deploy = frame.getDeploy();  
  95.        deploy.setScreen(new  ProcessingBall());  
  96.        deploy.setShowFPS(true );  
  97.        deploy.setFPS(100 );  
  98.        deploy.mainLoop();  
  99.        frame.showFrame();  
  100.     }  
  101.    
  102. }  
  1. public class ProcessingBall extends Screen {   
  2.     
  3.     class Ball {   
  4.     
  5.        float x;   
  6.     
  7.        float y;   
  8.     
  9.        float speed;   
  10.     
  11.        float gravity;   
  12.     
  13.        float w;   
  14.     
  15.        float life = 255;   
  16.     
  17.        Ball(float tempX, float tempY, float tempW) {   
  18.            x = tempX;   
  19.            y = tempY;   
  20.            w = tempW;   
  21.            speed = 0;   
  22.            gravity = 0.1f;   
  23.        }   
  24.     
  25.        void move() {   
  26.            speed = speed + gravity;   
  27.            y = y + speed;   
  28.            if (y > getHeight()) {   
  29.               speed = speed * -0.8f;   
  30.               y = getHeight();   
  31.            }   
  32.        }   
  33.     
  34.        boolean finished() {   
  35.            life--;   
  36.            if (life < 0) {   
  37.               return true;   
  38.            } else {   
  39.               return false;   
  40.            }   
  41.        }   
  42.     
  43.        void display(LPGraphics g) {   
  44.            g.fill(0, life);   
  45.            g.ellipse(x, y, w, w);   
  46.        }   
  47.     }   
  48.     
  49.     private ArrayList balls;   
  50.     
  51.     private int ballWidth = 48;   
  52.     
  53.     PImage image=Utils.loadImage("system/image/logo.png");   
  54.       
  55.     public ProcessingBall() {   
  56.        balls = new ArrayList();   
  57.        balls.add(new Ball(getWidth() / 20, ballWidth));   
  58.     }   
  59.     
  60.     public void draw(LPGraphics g) {   
  61.        g.background(255);   
  62.        for (int i = balls.size() - 1; i >= 0; i--) {   
  63.            Ball ball = (Ball) balls.get(i);   
  64.            ball.move();   
  65.            ball.display(g);   
  66.            if (ball.finished()) {   
  67.               balls.remove(i);   
  68.            }   
  69.        }   
  70.     }   
  71.     
  72.     public void leftClick(MouseEvent e) {   
  73.        balls.add(new Ball(getMouseX(), getMouseY(), ballWidth));   
  74.     }   
  75.     
  76.     public void middleClick(MouseEven
分享到:
评论

你可能感兴趣的:(C++,c,C#,Flash,360)