画图板的24位位图BMP保存
将画图板完善了一些,实现了画图板的重绘及24位位图BMP的保存与打开,算的上是一个较为完善的XP画图板,现在对画图板的制作过程做个小结:
(带有部分代码介绍,详细代码会以附件的形式传上来,完成的最终成品在com.jk.yt.draw9包中,其余包均为一步步完善的过程。)
(之前所做的画图板只实现了按钮和取色的功能,详细的介绍及代码可以去http://leaf-stop.iteye.com/blog/2251203查看,这里将不做详细介绍,)
1.画图板界面的绘制
先绘制一个总界面,然后将他们分为三个部分,左边面板添加形状选择工具,中间面板添加绘图面板,底部面板添加颜色选择工具,最后加上一个菜单栏就完成了画图板的基本布局,如下图所示:
2.按钮功能的实现
先利用按钮组对象,给单选按钮进行逻辑分组ButtonGroup group = new ButtonGroup();再创建一个DrawListener监听器,DrawListener dlis = new DrawListener(drawPanel, group, frontLabel,backLabel);将各个参数传入,后由mousePressed,mouseReleased,mouseDragged实现其按钮的功能。
3.颜色的选择
先定义好颜色数组,然后创建ColorListener 监听器,由ColorListener clis = new ColorListener(frontLabel, backLabel);传入前景色和背景色。我们还可以创建一个颜色板(新增功能),获得更多颜色选择,代码如下:
JButton jb = new JButton("颜色板"); jb.setBounds(470, 30, 90, 40); jb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Color newcolor = JColorChooser.showDialog(drawPanel, "调色板", drawPanel.getBackground()); if (newcolor != null) { frontLabel.setBackground(newcolor);// JFrame类里面只能由内容面板来设定颜色 } } }); foot.add(jb);
4.重绘
在DrawListener中,
1)根据面板大小创建保存面板数据的二维数组
// 获得面板的大小 Dimension dim = drawPanel.getPreferredSize(); // 根据面板大小创建保存面板数据的二维数组 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] = Color.WHITE.getRGB(); } } try { robot = new Robot(); } catch (AWTException e) { e.printStackTrace(); }
2)由截屏方式实现重绘
// 释放一次,就从新保存一次 // 1.截屏 Point point = drawPanel.getLocationOnScreen(); Dimension dim = drawPanel.getPreferredSize(); Rectangle screenRect = new Rectangle(point, dim); BufferedImage bufferImg = 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] = bufferImg.getRGB(j, i); } }
5. 24位位图的BMP保存与打开
1).BMP头文件(14字节)
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;// 文件头的偏移量表示,以字节为单位
2).位图信息头(40 节 字节)
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 字节 )
3).颜色表
class RGBQUAD {
byte rgbBlue;// 蓝色的亮度 ( 值范围为 0-255)
byte rgbGreen; // 绿色的亮度 ( 值范围为 0-255)
byte rgbRed; // 红色的亮度 ( 值范围为 0-255)
byte rgbReserved;// 保留,必须为 0
}
4).位图数据
位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右, 扫描行之间是从下到上。位图的一个像素值所占的字节数:
当 biBitCount=1 时, 8 个像素占 1 个字节 ;
当 biBitCount=4 时, 2 个像素占 1 个字节 ;
当 biBitCount=8 时, 1 个像素占 1 个字节 ;
当 biBitCount=24 时 ,1 个像素占 3 个字节 ;
Windows 规定一个扫描行所占的字节数必须是 4 的倍数 ( 即以 long 为单位 ), 不足的以 0填充,具体数据举例:如某 BMP 文件开头:
424D 4690 0000 0000 0000 4600 0000 2800 0000 8000 0000 9000 0000 0100*1000 0300 0000
0090 0000 A00F 0000 A00F 0000 0000 0000 0000 0000*00F8 E007 1F00 0000*02F1 84F1
04F1 84F1 84F1 06F2 84F1 06F2 04F2 86F2 06F2 86F2 86F2 .... ....
BMP 文件可分为四个部分:位图文件头、位图信息头、彩色板、图像数据阵列,在上图中已用 * 分隔。了解了BMP的基础知识后,就可以进一步完善画图板了,这里只针对24位的BMP图片。
读取一个BMP文件实际上只要获取到文件头的宽度,高度和它的位图数据,而其他部分都是可以跳过的。
(1).创建一个BMP文件的读取类:BMPReader
/** * BMP文件的读取类 * * @author leaf-stop * */ public class BMPReader { /** * 读取BMP文件的方法 * * @param path * 文件路径 * @param panel * 显示的画板 * @throws IOException * 流异常 */ public void readBMP(String path, MyPanel panel) throws IOException { FileInputStream fis = new FileInputStream(path); fis.skip(18);// 跳过不需要的,读取宽度和高度 int width = changeInt(fis); int height = changeInt(fis); fis.skip(28);// 跳过,直接读取位图数据 DrawListener.array = new int[height][width]; int t = 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(fis); DrawListener.array[i][j] = c; } fis.skip(t); } fis.close(); // 刷新,重绘面板,打开系统保存的图片 panel.setPreferredSize(new Dimension(width, height)); } /** * * 由于读取的是字节,把读取到的4个byte转化成1个int * * @param ips * 文件输入流对象 * @return 转换后的int值 * * @throws IOException * 流异常 */ public int changeInt(FileInputStream 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; return num; } /** * 24位的图片是1个像素3个字节,所以要将其颜色值合成一个具体的颜色值 * * @param ips * 文件输入流对象 * @return 颜色值的合成值 * * @throws IOException * 流异常 */ public int readColor(FileInputStream 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; } }
(2).创建一个BMP文件的保存写入类:BMPWriter
/** * BMP文件的保存写入类 * * @author leaf-stop * */ public class BMPWriter { /** * 保存BMP图片头部信息的方法 * * @param fos * 输出流 * @throws Exception * 异常对象 */ 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); } /** * * 保存BMP图片位图信息头部分的方法 * * @param ops * 输出流 * @throws Exception * 异常 * */ 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); writeShort(ops, (short) 1); writeShort(ops, (short) 24); writeInt(ops, 0); writeInt(ops, height * width * 3 + (4 - width * 3 % 4) * height); writeInt(ops, 0); writeInt(ops, 0); writeInt(ops, 0); writeInt(ops, 0); } /** * * 保存BMP图片位图数据部分的方法 * * @param ops * 输出流 * @throws Exception * 异常 */ public void savebmpDate(OutputStream ops) throws Exception { int height = DrawListener.array.length; int width = DrawListener.array[0].length; int m = 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); } } } /** * * 由于写入的是字节,所以要将整型进行转换 * * @param ops * 输出流 * @param t * 整型值 * @throws Exception * 异常 * */ 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); } /** * 由于写入的是字节,所以要将颜色值进行转换 * * * @param ops * 输出流 * * @param t * 整型的颜色值 * * @throws Exception * 异常 * */ 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); } /** * 由于写入的是字节,所以要将短整型进行转换 * * @param ops * 输出流 * @param t * 短整形值 * @throws Exception * 异常 */ public void writeShort(OutputStream ops, short t) throws Exception { int c = (t >> 8) & 0xff; int d = t & 0xff; ops.write(d); ops.write(c); } }
(3).创建一个监听器:MenuListener来实现BMP的打开和保存。
import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.FileOutputStream; import javax.swing.JFileChooser; import javax.swing.JOptionPane; /** * 菜单处理监听器 * * @author leaf-stop * */ public class MenuListener implements ActionListener { private MyPanel panel; public MenuListener(MyPanel panel) { this.panel = panel; } @Override public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub // 获得被点击的组件的动作命令 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); BMPWriter writer = new BMPWriter(); writer.savebmpTop(fos); writer.savebmpInfo(fos); writer.savebmpDate(fos); fos.flush(); fos.close(); } catch (Exception e1) { // TODO Auto-generated catch block JOptionPane.showMessageDialog(null, "文件保存失败"); e1.printStackTrace(); } } } else if (command.equals("打开")) { int t = chooser.showOpenDialog(null); if (t == JFileChooser.APPROVE_OPTION) { String path = chooser.getSelectedFile().getAbsolutePath(); try { BMPReader reader = new BMPReader(); reader.readBMP(path, panel); // 刷新界面 panel.repaint(); } catch (Exception e1) { // TODO Auto-generated catch block JOptionPane.showMessageDialog(null, "文件打开失败"); e1.printStackTrace(); } } } } }
最后,到这里我目前所做的画图板就基本完成了,前期的布局,取色较为简单,重绘及BMP保存与打开较为困难,参考了很多资料才略懂,有些按钮的功能还是没有实现,仍需要努力,希望大家指出代码中的不足,并与我交流改进,完善画图板。
ps:虚线五角星的绘制是新增的(但不是正五角星),这里将不再介绍,请查看附件。其它按钮的功能实现在另一个博客http://leaf-stop.iteye.com/blog/2251203中有介绍
相关推荐
本篇小结将聚焦于如何使用Java实现一个基本的画图板,同时提供相关的源码分析。 1. Java GUI基础 Java提供了丰富的类库用于创建GUI,主要在java.awt和javax.swing包下。在这个项目中,我们可能会使用`JFrame`作为主...
在Java编程语言中,画图板(Canvas)是图形用户界面(GUI)开发的一个关键组件。它是Java AWT(Abstract Window Toolkit)库的一部分,用于在窗口上绘制图形。Canvas类继承自Component,允许程序员直接在屏幕上进行...
6. 设计小结 本报告对 Java 画图板应用程序的设计与实现进行了详细的介绍,涵盖了程序的设计要求、设计环境、总体设计、详细设计与实现、系统测试等方面。程序的设计与实现遵循了 Java 语言的编程规范和设计原则,...
6. **设计小结** 通过本次设计,开发者不仅掌握了Java GUI编程技术,还深化了对事件处理、文件操作和图形绘制的理解。Eclipse的灵活性和扩展性为项目的实现提供了便利,同时也锻炼了开发者对开源工具的运用能力。 ...
该设计方案共分为八个部分:教学目的、教学重难点、教学过程、认识“画图”窗口、尝试完成任务、保存文件、退出“画图”窗口和课堂小结。 一、教学目的 本设计方案的教学目的旨在帮助学生掌握启动与退出“画图”...
课堂小结回顾了画图程序的基本操作,强调了铅笔工具的使用和颜色选择。扩展练习鼓励学生创新,设计出“美丽家园”的画面,提高他们的绘图技能。 在后续的课程中,进一步教授了“铅笔”工具的使用技巧,包括如何画...
目 录 第1章 猜数字游戏 1 1.1 游戏创意 1 1.2 游戏规划 2 1.3 程序实现 4 1.4 游戏调试 6 1.5 文本模式游戏制作 8 1.5.1 文本窗口函数 9 1.5.2 INT10中断功能 11 1.6 本章小结 11 第2章 用C语言函数库画图 12 2.1 ...
1.4 小结 第2章 Java面向对象编程 2.1 面向对象的一些概念 2.1.1 面向对象涉及的概念 2.1.2 类和对象 2.2 面向对象的一些特性 2.2.1 继承特性 2.2.2 多态特性 2.2.3 封装特性 2.3 Java中实现的面向对象特性 2.3.1 ...
在第一章“认识神奇的画图板”中,教学目标主要包括以下几个方面: 1. **知识目标**:学生将初步认识“画图”窗口的布局,理解工具箱中各种工具的功能,例如刷子、填充工具等。同时,他们会了解到多媒体计算机的...
第1章 Visual C++与数字图像处理 1 1.1 数字图像处理概述 2 1.1.1 图像与数字图像 2 1.1.2 数字图像处理研究的内容 4 1.1.3 数字图像处理的应用 6 1.2 Visual C++概述 8 1.2.1 C++语言简介 8 ...15.5 本章小结 640
小结强调了原理图绘制中对元器件参数设置的重要性,以及绘制时对元器件参数的精确设置。在原理图绘制的实训中,学生们不仅学会了如何使用CAD软件来绘制电路图,而且对电路设计的流程有了初步的认识。 原理图库的实...
* 对前面学习的“写字板”、“画图”等软件进行回顾 * 引入 word2003 软件 二、教学新课 * 启动 word2003 程序 * 认识工作界面 * 输入儿歌 * 保存文件 三、小结 * 总结今天的学习内容 * 介绍 word2003 软件的更...
1.9 本章小结 22 本章练习 22 第2章 理解面向对象 23 2.1 面向对象 24 2.1.1 结构化程序设计简介 24 2.1.2 程序的三种基本结构 25 2.1.3 面向对象程序设计简介 27 2.1.4 面向对象的基本特征 28 2.2 UML...
- 附件:所有附件默认保留,因为它们提供了各种实用工具,如记事本、画图等,对于日常使用非常有用。 - 驱动:全部保留,因为驱动是硬件正常工作所必需的,确保所有硬件设备都能正确识别和运行。 - 语言包:只...