`
lelong
  • 浏览: 554796 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

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
    分享到:
    评论

相关推荐

    Java Image转BufferedImage

    否则,创建一个新的`BufferedImage`实例,其宽度和高度与原始`Image`相同,并根据`Image`的色彩模型和透明度选择适当的`BufferedImage`类型。然后,使用`Graphics2D`将`Image`绘制到`BufferedImage`上,完成转换过程...

    IplImage 与BufferedImage互相转换

    public static BufferedImage iplToBufImgData(IplImage ipl,BufferedImage image ) public static IplImage BufImgToIplData(IplImage ipl,BufferedImage bi)

    java像素级图像处理与识别方法

    Java中的像素级图像处理与识别涉及到了多个关键概念和技术,主要使用了`BufferedImage`和`ImageIO`这两个核心类。下面将详细解释这些概念及其使用方法。 1. **ImageIO类**: `ImageIO`是Java标准库中处理图像输入...

    \java像素级图像处理

    进行像素级图像处理时,主要会用到`BufferedImage`和`ImageIO`这两个类。 - **`ImageIO`**:提供了图像读写接口,可以方便地从URL、输入流等地方获取图像信息。例如: ```java BufferedImage img = ImageIO.read...

    将BufferedImage保存为BMP格式的文件

    下面,我们将深入分析与BMP文件格式相关的知识点,以及如何在Java中实现`BufferedImage`到BMP文件的转换。 ### BMP文件结构 BMP文件由多个部分组成,主要包括文件头(File Header)和信息头(Info Header),以及...

    java像素级图像处理.pdf

    总之,Java提供了强大的图像处理能力,结合`ImageIO`和`BufferedImage`,开发者可以实现从基本的像素级操作到复杂的图像识别算法。在实际项目中,可以根据需求选择合适的方法,实现图像的读取、编辑、保存以及各种...

    通过BufferedImage实现将几张图片合成一张图片,图片效果类型为依次排列图片

    在Java编程语言中,`BufferedImage`类是用于创建和操作图像的重要工具。这个类提供了丰富的功能,包括读取、写入、绘制和修改图像。在这个特定的场景中,我们利用`BufferedImage`来实现一个有趣的功能:将四张图片...

    java编程简单获取图片像素的方法

    然而,获取单个像素的颜色值,我们可以使用`BufferedImage`的`getRGB(int x, int y)`方法,它会返回指定位置(x, y)像素的ARGB值。例如,要获取图像左上角第一个像素的颜色,我们可以这样做: ```java int pixel =...

    java opencv BufferedImage与Mat互相转换.rar_OpenCV_Java_

    总结来说,理解和掌握`Mat`与`BufferedImage`之间的转换对于在Java环境中有效地使用OpenCV至关重要。无论是进行图像显示、保存还是进一步的处理,这种转换都是不可或缺的步骤。熟练运用这些转换方法,可以极大地扩展...

    解析:android 如何从JPEG生成BufferedImage

    完成这些步骤后,你就成功地从JPEG文件生成了BufferedImage,并提取了图像的像素数据。这样的处理可以用于各种图像操作,如缩放、旋转、颜色转换或滤波。然而,由于Android不直接支持Java AWT,你可能需要使用...

    java中ImageReader和BufferedImage获取图片尺寸实例

    BufferedImage 是 Java 中用于表示图像的类,它可以存储图像的像素数据和其他相关信息。使用 BufferedImage 获取图片尺寸可以通过以下步骤实现: 1. 首先,创建一个 BufferedImage 对象,并将其设置为读取指定图片...

    java图像像素点的处理

    1. **BufferedImage类**: `BufferedImage`是`Image`类的子类,它提供了对图像像素的直接访问。你可以通过创建一个`BufferedImage`对象并指定其类型(如RGB、ARGB等)来加载或创建图像。例如: ```java ...

    图片像素调整jar包,可实现自定义像素,自定义转换类型

    6. **图像处理API**:在Java中,可以使用Java AWT(Abstract Window Toolkit)和Swing中的BufferedImage类进行基本的图像处理,或者利用更高级的Java Advanced Imaging (JAI) 库进行复杂的操作。这个jar包可能封装了...

    BufferedImage使用—-生成一张验证码图片

    2. **创建 BufferedImage 对象**:然后,我们需要创建一个 BufferedImage 对象,指定其宽度(WIDTH)、高度(HEIGHT)以及类型(BufferedImage.TYPE_INT_RGB,即 RGB 像素格式)。这样我们就得到了一个可以在内存中...

    像素鸟小游戏源代码

    另一个是渲染线程,负责将游戏状态更新到屏幕上,确保游戏画面的连续滚动。通过合理调度这两个线程,可以保证游戏的流畅运行,避免因为CPU资源的争夺导致画面卡顿。 其次,IO流是数据在内存与外部设备之间传输的...

    java中如何使用BufferedImage判断图像通道顺序并转RGB/BGR

    Java中使用BufferedImage判断图像通道顺序并转RGB/BGR 在Java中,使用BufferedImage处理图像时,需要判断图像的通道顺序,判断图像是否为RGB或BGR格式,并对其进行相应的转换。本文将详细介绍如何使用BufferedImage...

    tile-joiner:将地图服务图块渲染为 java 中的 BufferedImage,然后渲染为 PNG

    瓷砖木工将谷歌地图、ArcGIS、OpenStreetMaps 中的底图图块渲染为 java 中的 ,然后再渲染为 PNG。 状态: pre-alpha 与 Jenkins 的持续集成: Maven 站点报告在包括 。特征计算最佳缩放级别处理 180/-180 经度边界...

    java图片旋转

    - 使用`createCompatibleImage()`方法创建一个新的`BufferedImage`对象,确保颜色模型与原始图像兼容,这样在绘制时能提高效率。 - 考虑性能:对于大量或大尺寸的图片,直接操作原始图像可能会很慢。可以先缩小...

    查看图片的像素.rar

    了解图片的像素信息对很多场合都很关键,比如网页设计时需要考虑加载速度与清晰度的平衡,摄影后期处理时调整尺寸,甚至在进行图像分析和处理的算法开发时,都需要明确图像的像素大小。在日常工作中,掌握这些方法...

    对图片的像素进行管理

    2. **计算比例**:确定新尺寸与原尺寸的比例,以保持宽高比不变。如果新的宽度或高度小于原始尺寸,可以计算出缩放比例。 3. **创建新图像**:创建一个新的`BufferedImage`对象,尺寸为新宽度和高度。 4. **像素复制...

Global site tag (gtag.js) - Google Analytics