- 浏览: 19478 次
- 性别:
最新评论
实现画图板重绘的两种方法
1.抽象Shape类 每次画的时候 都保存在ArrayList中 当窗体改变 自动调用paint方法时 取出ArrayList中保存的Shape对象
public void paint(Graphics g) { super.paint(g);// 将面板本身绘制在屏幕上 // 当面板发生改变的时候, //将ArrayList中保存的形状对象取出来,重新绘制 for(int i=0;i<DrawListener.list.size();i++){ Shape shape = DrawListener.list.get(i); shape.draw(g); } }
2.截屏 每次释放的时候 用二维数组保存屏幕上的点
// 释放一次,就重新保存一次 // 截屏 Point point= drawJPanel.getLocationOnScreen(); Dimension dim=drawJPanel.getPreferredSize(); Rectangle screenrect=new Rectangle(point,dim); BufferedImage bufferedImg=robot.createScreenCapture(screenrect); // 根据面板大小调整数组大小 array=new int[dim.height][dim.width]; for(int i=0;i<array.length;i++){ for(int j=0;j<array[i].length;j++){ array[i][j]=bufferedImg.getRGB(j, i); } }
先介绍一下有关BMP的基础知识:
BMP 是一种与硬件设备无关的图像文件格式,使用非常广。它采用位映射存储格式,除了图 像深度可选以外,不采用其他任何压缩,因此, BMP 文件所占用的空间很大。 BMP 文件的图 像深度可选 lbit 、 4bit 、 8bit 及 24bit 。 BMP 文件存储数据时,图像的扫描方式是按从左 到右、从下到上的顺序。由于 BMP 文件格式是 Windows 环境中交换与图有关的数据的一种标 准,因此在 Windows 环境中运行的图形图像软件都支持 BMP 图像格式。
要解析文件,就必须知道他的文件结构:
BMP 文件结构
典型的 BMP 图像文件由四部分组成:
1 . 位图文件 头数据结构 ,它包含 BMP 图像文件的类型、显示内容等信息;
2 .位图信息数据结构 ,它包含有 BMP 图像的宽、高、压缩方法,以及定义颜色等信息;
3. 调色板 ,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24 位 的 BMP )就不需要调色板;
4. 位图数据 ,这部分的内容根据 BMP 位图使用的位数不同而不同,在 24 位图中直 接使用 RGB ,而其他的小于 24 位的使用调色板中颜色索引值。
对应的数据结构
① BMP 文件头 (14 字节 )
BMP 文件头数据结构含有 BMP 文件的类型、文件大小和位图起始位置等信息。其结构定 义如下:
int bfType; // 位图文件的类型,必须为 ' B '' M '两个字母 (0-1字节 )
int bfSize; // 位图文件的大小,以字节为单位 (2-5 字节 )
int usignedshort bfReserved1; // 位图文件保留字,必须为 0(6-7 字节 )
int usignedshort bfReserved2; // 位图文件保留字,必须为 0(8-9 字节 )
int bfOffBits; // 位图数据的起始位置,以相对于位图 (10-13 字节 )
int bfOffBits;// 文件头的偏移量表示,以字节为单位
② 位图信息头(40 字节 )
BMP 位图信息头数据用于说明位图的尺寸等信息。
int Size; // 本结构所占用字节数 (14-17 字节 )
int image_width; // 位图的宽度,以像素为单位 (18-21 字节 )
int image_heigh; // 位图的高度,以像素为单位 (22-25 字节 )
int Planes; // 目标设备的级别,必须为 1(26-27 字节 )
int biBitCount;// 每个像素所需的位数,必须是 1(双色),(28-29 字节) 4(16
色 ) , 8(256 色 ) 或 24(// 真彩色 ) 之一
int biCompression; // 位图压缩类型,必须是 0( 不压缩 ),(30-33 字节 ) 1(BI_RLE8 压缩类型 ) 或// 2(BI_RLE4 压缩类型 ) 之一
int SizeImage; // 位图的大小,以字节为单位 (34-37 字节 )
int biXPelsPerMeter; // 位图水平分辨率,每米像素数 (38-41 字节 ) int biYPelsPerMeter; // 位图垂直分辨率,每米像素数 (42-45 字节 ) int biClrUsed;// 位图实际使用的颜色表中的颜色数 (46-49 字节 )
int biClrImportant;// 位图显示过程中重要的颜色数 (50-53 字节 )
③ 颜色表
颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个 RGBQUAD 类型的结 构,定义一种颜色。
class RGBQUAD {
byte rgbBlue;// 蓝色的亮度 ( 值范围为 0-255)
byte rgbGreen; // 绿色的亮度 ( 值范围为 0-255)
byte rgbRed; // 红色的亮度 ( 值范围为 0-255)
byte rgbReserved;// 保留,必须为 0
}
颜色表中 RGBQUAD 结构数据的个数有 biBitCount 来确定。当 biBitCount=1,4,8 时,分别有 2,16,256 个表项 ; 当 biBitCount=24 时,没有颜色表项。 位图信息头和颜色表组成位图信息,BITMAPINFO 结构定义如下 :
class BITMAPINFO {
BITMAPINFOHEADER bmiHeader; // 位图信息头
RGBQUAD bmiColors[1]; // 颜色表
}
④ 位图数据
位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从 下到上。位图的一个像素值所占的字节数:
当 biBitCount=1 时, 8 个像素占 1 个字节 ; 当 biBitCount=4 时, 2 个像素占 1 个字节 ; 当 biBitCount=8 时, 1 个像素占 1 个字节 ; 当 biBitCount=24 时 ,1 个像素占 3 个字节 ;
Windows 规定一个扫描行所占的字节数必须是 4 的倍数 ( 即以 long 为单位 ), 不足的以 0填充
简单来说 文件格式保存 就是 怎么写进去 就怎么读出来 但是其中转换格式还是很麻烦的
通过网上查资料 BMP解析可以有两种方式实现 其实方法差不多 原理都一样
方法一:
package BMP_Color; import java.awt.Color; import java.io.DataOutputStream; import java.io.FileOutputStream; public class BMPWrite24 { Color [][] color; int width,height; String path; public BMPWrite24(Color [][] color,String path){ this.color=color; this.height=color.length; this.width=color[0].length; this.path=path; this.write(); } public void write(){ try { FileOutputStream fos=new FileOutputStream(path); DataOutputStream dos=new DataOutputStream(fos); int bfSize=54+width*height*3+(4 - width * 3 % 4) * height; int bfReserved1=0; int bfReserved2=0; int bfOffBits=54; dos.write('B'); dos.write('M'); dos.write(ChangeByte(bfSize),0,4); dos.write(ChangeByte(bfReserved1),0,2); dos.write(ChangeByte(bfReserved2),0,2); dos.write(ChangeByte(bfOffBits),0,4); int size=40; int image_width=width; int image_height=height; int Planes=1; int biBitCount=24; int biCompression=0; int SizeImage=width*height; int biXPelsPerMeter=0; int biYPelsPerMeter=0; int biClrUsed=0; int biClrImportant=0; // 因为java是大端存储,那么也就是说同样会大端输出。 // 但计算机是按小端读取,如果我们不改变多字节数据的顺序的话,那么机器就不能正常读取。 // 所以首先调用方法将int数据转变为多个byte数据,并且按小端存储的顺序。 dos.write(ChangeByte(size),0,4); dos.write(ChangeByte(image_width),0,4); dos.write(ChangeByte(image_height),0,4); dos.write(ChangeByte(Planes),0,2); dos.write(ChangeByte(biBitCount),0,2); dos.write(ChangeByte(biCompression),0,4); dos.write(ChangeByte(SizeImage),0,4); dos.write(ChangeByte(biXPelsPerMeter),0,4); dos.write(ChangeByte(biYPelsPerMeter),0,4); dos.write(ChangeByte(biClrUsed),0,4); dos.write(ChangeByte(biClrImportant),0,4); int skip=0; if(!(width*3%4==0)){ skip=4-width*3%4; } // 因为是24位图,所以没有颜色表 // 通过遍历输入位图数据 // 这里遍历的时候注意,在计算机内存中位图数据是从左到右,从下到上来保存的, // 也就是说实际图像的第一行的点在内存是最后一行 for(int i=height-1;i>=0;i--){ for(int j=0;j<width;j++){ int red=color[i][j].getRed(); int green=color[i][j].getGreen(); int blue=color[i][j].getBlue(); byte[] r=ChangeByte(red); byte[] g=ChangeByte(green); byte[] b=ChangeByte(blue); dos.write(b,0,1); dos.write(g,0,1); dos.write(r,0,1); for (int k = 0; k < skip; k++) { dos.write(0); } } } dos.flush(); dos.close(); fos.close(); } catch (Exception e) { e.printStackTrace(); } } public byte[] ChangeByte(int data){ byte b4=(byte)((data)>>24); byte b3=(byte)(((data)<<8)>>24); byte b2=(byte)(((data)<<16)>>24); byte b1=(byte)(((data)<<24)>>24); byte [] b={b1,b2,b3,b4}; return b; } }
package BMP_Color; import java.awt.Color; import java.io.DataInputStream; import java.io.FileInputStream; import javax.swing.JOptionPane; public class BMPReader24{ MyPanel panel; String path; int [][] red,green,blue; public BMPReader24(MyPanel panel,String path){ this.panel=panel; this.path=path; this.reader(); } public void reader(){ try { FileInputStream fis=new FileInputStream(path); DataInputStream dis=new DataInputStream(fis); int biLen=14; byte[] bi=new byte[biLen]; dis.read(bi, 0, biLen); int bfLen=40; byte[] bf=new byte[bfLen]; dis.read(bf, 0, bfLen); int width=ChangeInt(bf, 7); int height=ChangeInt(bf, 11); red=new int [height][width]; green=new int[height][width]; blue=new int[height][width]; // 通过计算得到每行计算机需要填充的字符数。 // 为什么要填充?这是因为windows系统在扫描数据的时候,每行都是按照4个字节的倍数来读取的。 // 因为图片是由每个像素点组成。而每个像素点都是由3个颜色分量来构成的,而每个分量占据1个字节。 // 因此在内存存储中实际图片数据每行的长度是width*3。 int skip=0; if(!(width*3%4==0)){ skip=4-width*3%4; } for(int i=height-1;i>=0;i--){ for(int j=0;j<width;j++){ blue[i][j]=dis.read(); green[i][j]=dis.read(); red[i][j]=dis.read(); DrawListener.color[i][j]=new Color(red[i][j],green[i][j],blue[i][j]); if(j==0){ dis.skipBytes(skip); } } } panel.repaint(); } catch (Exception e) { JOptionPane.showMessageDialog(null, "文件打开失败!!"); e.printStackTrace(); } } public int ChangeInt(byte[] array2, int start) { // 因为char,byte,short这些数据类型经过运算符后会自动转为成int数据类, // 所以array2[start]&0xff的实际意思就是通过&0xff将字符数据转化为正int数据,然后在进行位运算。 // 这里需要注意的是<<的优先级别比&高,所以必须加上括号。 int i = (int) ((array2[start] & 0xff) << 24) | ((array2[start - 1] & 0xff) << 16) | ((array2[start - 2] & 0xff) << 8) | (array2[start - 3] & 0xff); return i; } }
方法二:
import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.swing.JFileChooser; import javax.swing.JOptionPane; /** * 菜单处理监听器 * * @author kowloon * */ public class MenuListener implements ActionListener { private MyPanel panel; public MenuListener(MyPanel panel) { this.panel = panel; } // bmp文件头 public void savebmpTop(OutputStream ops) throws Exception { ops.write('B'); ops.write('M'); int height = DrawListener.array.length; int width = DrawListener.array[0].length; int size = 14 + 40 + height * width * 3 + (4 - width * 3 % 4) * height; // 位图文件的大小 size = 14 + 40 + height * width * 3 + (4 - width * 3 % 4) * 255; writeInt(ops, size); // 保留字节,必须为零 writeShort(ops, (short) 0); writeShort(ops, (short) 0); // 位图偏移量 writeInt(ops, 54); } // 位图信息头 public void savebmpInfo(OutputStream ops) throws Exception { int height = DrawListener.array.length; int width = DrawListener.array[0].length; // 位图信息头长度 writeInt(ops, 40); // 位图宽 writeInt(ops, width); // 位图高 writeInt(ops, height); // 位图位面数总是为1 writeShort(ops, (short) 1); // 位图24位像素 writeShort(ops, (short) 24); // 位图是否被压缩,0为不压缩 writeInt(ops, 0); // 字节数代表位图大小 writeInt(ops, height * width * 3 + (4 - width * 3 % 4) * height); // 水平分辨率 writeInt(ops, 0); // 垂直分辨率 writeInt(ops, 0); // 颜色索引,0为所有调色板 writeInt(ops, 0); // 对图象显示有重要影响的颜色索引的数目。如果是0,表示都重要 writeInt(ops, 0); } // 图像数据阵列 public void savebmpDate(OutputStream ops) throws Exception { int height = DrawListener.array.length; int width = DrawListener.array[0].length; int m = 0; // 进行补0 if (width * 3 % 4 > 0) { m = 4 - width * 3 % 4; } for (int i = height - 1; i >= 0; i--) { for (int j = 0; j < width; j++) { int t = DrawListener.array[i][j]; writeColor(ops, t); } for (int k = 0; k < m; k++) { ops.write(0); } } } public void writeInt(OutputStream ops, int t) throws Exception { int a = (t >> 24) & 0xff; int b = (t >> 16) & 0xff; int c = (t >> 8) & 0xff; int d = t & 0xff; ops.write(d); ops.write(c); ops.write(b); ops.write(a); //System.out.println(d+" <>"+c+"<>"+b+"<>"+a); } public void writeColor(OutputStream ops, int t) throws Exception { int b = (t >> 16) & 0xff; int c = (t >> 8) & 0xff; int d = t & 0xff; ops.write(d); ops.write(c); ops.write(b); } public void writeShort(OutputStream ops, short t) throws Exception { int c = (t >> 8) & 0xff; int d = t & 0xff; ops.write(d); ops.write(c); } // 由于读取的是字节,把读取到的4个byte转化成1个int public int changeInt(InputStream ips) throws IOException { int t1 = ips.read() & 0xff; int t2 = ips.read() & 0xff; int t3 = ips.read() & 0xff; int t4 = ips.read() & 0xff; int num = (t4 << 24) + (t3 << 16) + (t2 << 8) + t1; System.out.println(num); return num; } // 24位的图片是1个像素3个字节。 public int readColor(InputStream ips) throws IOException { int b = ips.read() & 0xff; int g = ips.read() & 0xff; int r = ips.read() & 0xff; int c = (r << 16) + (g << 8) + b; return c; } public void actionPerformed(ActionEvent e) { // 获得被点击的组件的动作命令 String command = e.getActionCommand(); JFileChooser chooser = new JFileChooser(); if (command.equals("保存")) { int t = chooser.showSaveDialog(null); if (t == JFileChooser.APPROVE_OPTION) { String path = chooser.getSelectedFile().getAbsolutePath(); try { FileOutputStream fos = new FileOutputStream(path); DataOutputStream dos = new DataOutputStream(fos); savebmpTop(dos); savebmpInfo(dos); savebmpDate(dos); fos.flush(); fos.close(); } catch (Exception ef) { JOptionPane.showMessageDialog(null, "文件保存失败!!"); ef.printStackTrace(); } } } else if (command.equals("打开")) { int t = chooser.showOpenDialog(null); if (t == JFileChooser.APPROVE_OPTION) { String path = chooser.getSelectedFile().getAbsolutePath(); try { FileInputStream fis = new FileInputStream(path); DataInputStream dis = new DataInputStream(fis); dis.skip(18); int width = changeInt(dis);// 跳过不需要的,读取宽度和高度 int height = changeInt(dis); dis.skip(28); // 跳过,直接读取位图数据。 DrawListener.array = new int[height][width]; int w = 0; if (width * 3 % 4 > 0) { t = 4 - width * 3 % 4; } for (int i = height - 1; i >= 0; i--) { for (int j = 0; j < width; j++) { // 调用自定义方法,得到图片的像素点并保存到int数组中 int c = readColor(dis); DrawListener.array[i][j] = c; } dis.skip(w); } fis.close(); // 刷新界面 panel.repaint(); } catch (Exception ef) { JOptionPane.showMessageDialog(null, "文件打开失败!!"); ef.printStackTrace(); } } } } }
比较两个方法 :
方法一比较麻烦 再定义一个Color[][]数组 保存屏幕上的点的颜色 BMP格式保存时
color[i][j].getRed()getGreen()getBlue()方法获取屏幕点的颜色分量
方法二 直接利用重绘时定义int[][]数组 32位 后24位是红绿蓝颜色分量
public void writeColor(OutputStream ops, int t) throws Exception { int b = (t >> 16) & 0xff; int c = (t >> 8) & 0xff; int d = t & 0xff; ops.write(d); ops.write(c); ops.write(b); }
1.byte转成int为何要&0xff?
第一,oxff默认为整形,二进制位最低8位是1111 1111,前面24位都是0;
第二,&运算: 如果2个bit都是1,则得1,否则得0;
第三,byte的8位和0xff进行&运算后,最低8位中,原来为1的还是1,原来为0的还是0,而0xff其他位都是0,所以&后仍然得0
2.writeInt 方法中传入int值 写出4个int值 依旧是4个字节
因为 write(int a) 是把a转换为byte 而不是byte[] a的高24位将被忽略 只有低8位转成byte写入流
- BMP.zip (80.3 KB)
- 下载次数: 1
发表评论
-
LinkedList、ArrayList、 Vector、HashSet、 TreeSet、HashMap、TreeMap的用法 区别和使用场景
2015-11-11 11:00 4459集合框架:Java中预定义的一些数据结构类 集合框架是定义在J ... -
异常机制 运行时异常、编译时异常 throws和throw的相同点和区别
2015-11-10 10:59 4227异常机制 异常:程序 ... -
== 、equals 、equalsIgnoreCase的用法介绍
2015-10-27 23:52 2184== 用来比较两个变量代表的地址是否相同 相同为true ... -
简易xp画图板程序流程
2015-10-27 00:48 1313关于xp简易画图板程序流程: 1.创建主窗口 设置大小、标题、 ... -
JAVA简单画图板的实现
2015-10-15 23:39 1407Java中边框布局: 1.把窗口分成5个区域:北、南、东、西、 ...
相关推荐
总之,通过自制的BMP格式画图板,我们可以学习到图像处理的基础知识,包括BMP文件格式、颜色模式、绘图算法以及图形用户界面的设计。这个过程不仅锻炼了编程技巧,还加深了对计算机图形学的理解。
NULL 博文链接:https://yangzhenlin.iteye.com/blog/2068428
这个应用程序允许用户在画图板上自由绘制,并将这些创作保存为图片格式,例如BMP。我们将围绕以下几个关键知识点展开讨论: 1. **MFC库**:MFC 是微软开发的一个C++类库,它封装了Windows API,为开发者提供了一套...
总的来说,自制画板打开和保存BMP格式文件涉及到的知识点包括:BMP文件格式的理解与解析,二进制文件操作,图像处理基础,GUI编程,以及事件驱动编程。这个项目既能够加深对图像文件格式的理解,也能锻炼实际编程...
在本篇博文中,我们将探讨如何使用Java编程语言来实现一个简单的画图板应用程序,并学习如何保存和读取BMP(Bitmap)格式的图片。BMP是一种无损图像格式,通常用于存储像素数据,便于程序处理。以下是实现这一功能所...
3. 保存与导出:画图完成后,可以保存为BMP、JPEG、PNG等多种格式,便于在其他程序中使用或分享。 总结,Windows画图板以其简单易用和功能丰富的特点,成为了日常生活中进行动态画图的得力助手。无论是儿童学习绘画...
首先,Android SDK提供了`Bitmap.compress()`方法来将Bitmap保存为JPEG或PNG格式,但不支持BMP。因此,我们需要自定义一个方法来处理BMP格式的转换。这个过程主要包括以下几个步骤: 1. **获取Bitmap的像素数据**:...
标题“画图板自定义格式保存”涉及到的是一个与计算机图形处理相关的技术,尤其是与用户交互式的绘图软件或工具的文件保存功能有关。在许多应用程序中,例如Microsoft Paint这样的简单画图工具,用户可以创建自己的...
本篇文章将深入探讨一个基于VC++(Visual C++)编写的“画图板”程序,该程序旨在模拟Windows系统自带的画图工具,提供手绘线、绘制简单图形、文字输入、图块拖放、重复撤销、画面缩放以及图片的打开与保存等核心...
9. **保存与加载**: 可能还实现了将画布内容保存到文件(如BMP或PNG格式)以及从文件加载的功能。这涉及到`Bitmap`类的使用,以及文件I/O操作。 10. **代码组织与设计模式**: 良好的源码结构可能包括多个类,每个类...
【标题】"仿XP画图板"是一款基于JAVA编程语言开发的应用程序,旨在模拟Windows XP操作系统中的经典画图工具。这个项目是为那些希望学习或熟悉GUI(图形用户界面)编程和事件处理的初学者设计的,同时也为用户提供了...
BMP 图像格式是与硬件设备无关的图像文件格式,使用非常广泛。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BMP 文件所占用的空间很大。BMP 文件的图像深度可选 1bit、4bit、8bit 及 24...
《Windows画图板程序源码解析与探讨》 在计算机编程领域,Windows画图板程序是一种常见的学习和实践窗口应用程序设计的案例。本篇文章将深入剖析"windows画图板程序源码",揭示其背后的原理和技术,以帮助读者更好...
考虑到保存和加载绘图的功能,可以实现`OnFileSave`和`OnFileOpen`函数,将当前的绘图序列化存储到文件中(如BMP或SVG格式),并在打开文件时反序列化并重新绘制。 通过以上步骤,我们可以构建一个功能完备的MFC...
用C#做的一个画图板工具,可以打开图片在其上画圆,矩形,三角形,直线等,实现看鼠标拖拽画图,可任意选择颜色级画刷类型,可保存为BMP格式
你需要定义消息映射来处理特定的消息,如WM_PAINT消息用于窗口重绘,WM_LBUTTONDOWN和WM_LBUTTONUP用于处理鼠标点击。 7. **文件I/O**:为了实现打开和保存BMP文件的功能,你需要使用CFile类或者更高级的fstream来...
【标题】"仿windows画图板"所涉及的知识点主要集中在使用Visual Basic(VB)编程语言来创建一个图形用户界面(GUI),模仿Windows操作系统自带的“画图”应用程序。这个项目旨在提供一个基本的绘图工具,让用户可以...