一、项目简介:
??先给大家看一下这个小项目的效果: ??运行后生成的pic.bmp图片 ??没错,这个项目的主要功能就是通过函数生成对应的图像。
??主要是用到的技术有:IO流、BMP格式分析、面向对象程序设计。
??项目结构如下: ??Main类主要用来测试,剩下的三个类我会在下面一一介绍。
二、项目内容:
① Color类
??众所周知,图片其实就是由像素组成的二维矩阵,那我们肯定就要构建一个Color类表示像素了,图片信息的存储就是一个Color类型二维数组存储。
package www.spd.pic;
public class Color {
public static final Color BLACK = new Color((byte)0x00, (byte)0x00, (byte)0x00);
public static final Color RED = new Color((byte)0xff, (byte)0x00, (byte)0x00);
public static final Color GREEN = new Color((byte)0x00, (byte)0xff, (byte)0x00);
public static final Color BLUE = new Color((byte)0x00, (byte)0x00, (byte)0xff);
byte red;
byte green;
byte blue;
public Color(byte red, byte green, byte blue) {
this.red = red;
this.green = green;
this.blue = blue;
}
@Override
public String toString() {
int r = red >= 0 ? red : (red + 0x100);
int g = green >= 0 ? green : (green + 0x100);
int b = blue >= 0 ? blue : (blue + 0x100);
return String.format("#%02x%02x%02x", r, g, b);
}
public byte[] toByteArray() {
byte[] arr = new byte[3];
arr[0] = blue;
arr[1] = green;
arr[2] = red;
return arr;
}
}
② BMP类:
??对于BMP的文件格式分析详见这篇博文,写的很详细,我的项目就是参考了这篇博文:位图(bmp)文件格式分析。
package www.spd.pic;
import java.io.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class BMP {
String name;
int bfSize;
int biWidth;
int biHeight;
int biSizeImage;
int appendBytes;
int widthBytes;
Color[][] pixelsMatrix;
public BMP(String path) {
this(new File(path));
}
public BMP(File file) {
name = file.getName();
Pattern p = Pattern.compile(".*\\.bmp");
Matcher m = p.matcher(name);
if (!m.find()) throw new IllegalArgumentException("文件后缀不正确!!");
byte[] arr = getByteArray(file);
if (arr == null) throw new IllegalArgumentException("文件为空文件!!");
int bfType = byteArrayToInteger(arr, 1, 0);
if (bfType != 0x4d42) throw new IllegalArgumentException("文件受损!!");
bfSize = byteArrayToInteger(arr, 0x5, 0x2);
biWidth = byteArrayToInteger(arr, 0x15, 0x12);
biHeight = byteArrayToInteger(arr, 0x19, 0x16);
int biBitCount = byteArrayToInteger(arr, 0x1f, 0x1c);
if (biBitCount != 24) throw new IllegalArgumentException("图片不是24位真色彩位图!!");
biSizeImage = byteArrayToInteger(arr, 0x27, 0x24);
appendBytes = biWidth % 4;
widthBytes = biWidth * 3 + appendBytes;
pixelsMatrix = new Color[biHeight][biWidth];
for (int i = 0; i < biHeight; i++) {
int pixelEnd = (biHeight - i - 1) * widthBytes + 0x36;
for (int j = 0; j < biWidth; j++) {
pixelsMatrix[i][j] = new Color(arr[pixelEnd + 2], arr[pixelEnd + 1], arr[pixelEnd]);
pixelEnd += 3;
}
}
}
public BMP(Color color, String name, int width, int height) {
this.name = name;
biWidth = width;
biHeight = height;
appendBytes = biWidth % 4;
widthBytes = biWidth * 3 + appendBytes;
pixelsMatrix = new Color[biHeight][biWidth];
for (int i = 0; i < biHeight; i++) {
for (int j = 0; j < biWidth; j++) {
pixelsMatrix[i][j] = color;
}
}
biSizeImage = widthBytes * height;
bfSize = biSizeImage + 0x36;
}
public BMP(String name, int width, int height) {
this(new Color((byte)0xff, (byte)0xff, (byte)0xff), name, width, height);
}
public BMP(Color color, int width, int height) {
this(color, "pic.bmp", width, height);
}
public BMP(int width, int height) {
this("pic.bmp", width, height);
}
@Override
public String toString() {
return "BMP{" +
"\n\tname=\"" + name +
"\", \n\tbfSize=" + bfSize +
", \n\tbiWidth=" + biWidth +
", \n\tbiHeight=" + biHeight +
", \n\tbiSizeImage=" + biSizeImage +
", \n\tappendBytes=" + appendBytes +
", \n\twidthBytes=" + widthBytes +
"\n}";
}
private static byte[] getByteArray(File file) {
try(BufferedInputStream bis =
new BufferedInputStream(
new FileInputStream(file));
ByteArrayOutputStream baos =
new ByteArrayOutputStream()) {
int len = -1;
byte[] flush = new byte[1024];
while ((len = bis.read(flush)) != -1) {
baos.write(flush, 0, len);
baos.flush();
}
return baos.toByteArray();
} catch (FileNotFoundException e) {
System.err.println("文件不存在!!");
return null;
} catch (IOException e) {
System.err.println("IO操作出现异常!!");
e.printStackTrace();
return null;
}
}
private int byteArrayToInteger(byte[] arr, int begin, int end) {
int ans = 0;
int temp;
if (begin <= end) {
for (int i = begin; i <= end; i++) {
ans *= 0x100;
temp = arr[i] >= 0 ? arr[i] : (arr[i] + 128);
ans += temp;
}
}
if (begin > end) {
for (int i = begin; i >= end; i--) {
ans *= 0x100;
temp = arr[i] >= 0 ? arr[i] : (arr[i] + 128);
ans += temp;
}
}
return ans;
}
private byte[] integerToByteArray(int size, int num) {
byte[] arr = new byte[size];
int i = 0;
while (num > 0) {
byte temp = (byte) (num % 0x100);
num /= 0x100;
try {
arr[i] = temp;
} catch (IndexOutOfBoundsException e) {
throw new IllegalArgumentException("数字数值大于2^"+size);
}
i++;
}
return arr;
}
public byte[] toByteArray() {
try(ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
baos.write("BM".getBytes());
baos.write(integerToByteArray(4, bfSize));
baos.write(new byte[4]);
baos.write(integerToByteArray(4, 0x36));
baos.flush();
baos.write(integerToByteArray(4, 40));
baos.write(integerToByteArray(4, biWidth));
baos.write(integerToByteArray(4, biHeight));
baos.write(integerToByteArray(2, 1));
baos.write(integerToByteArray(2, 24));
baos.write(new byte[4]);
baos.write(integerToByteArray(4, biSizeImage));
baos.write(new byte[16]);
baos.flush();
byte[] append = new byte[appendBytes];
for (int i = biHeight - 1; i >= 0 ; i--) {
for (int j = 0; j < biWidth; j++) {
baos.write(pixelsMatrix[i][j].toByteArray());
}
baos.write(append);
baos.flush();
}
return baos.toByteArray();
} catch (IOException e) {
System.err.println("IO操作出现异常!!");
e.printStackTrace();
return null;
}
}
public void createFile() {
byte[] arr = toByteArray();
try (FileOutputStream fos = new FileOutputStream(name)) {
fos.write(arr);
fos.flush();
} catch (IOException e) {
System.err.println("将图像对象写入成图像时出线IO异常!!");
}
}
}
③ getPicByFunc类:
??从字面意义上来看:“从函数获得图像类”,它的下面有一个内部接口Functial,即可作为函数的,一个内部类Function,里面有一个Functial对象,和一个Color对象,表示的是一个带颜色的函数。
package www.spd.pic;
public class getPicByFunc {
public static BMP getPic(Function func, int size) {
return getPic("pic.bmp", func, size);
}
public static BMP getPic(String name, Function func, int size) {
return getPic(name, new Function[]{func}, size);
}
public static BMP getPic(Function[] functions, int size) {
return getPic("pic.bmp", functions, size);
}
public static BMP getPic(String name, Function[] functions, int size) {
BMP pic = new BMP(name, size * 40, size * 40);
for (int i = 0; i < size * 40; i++) {
pic.pixelsMatrix[i][size * 20] = Color.BLACK;
pic.pixelsMatrix[i][size * 20 - 1] = Color.BLACK;
pic.pixelsMatrix[size * 20][i] = Color.BLACK;
pic.pixelsMatrix[size * 20 - 1][i] = Color.BLACK;
if (i % 20 == 0) {
pic.pixelsMatrix[i][size * 20 + 1] = Color.BLACK;
pic.pixelsMatrix[i][size * 20 + 2] = Color.BLACK;
pic.pixelsMatrix[i][size * 20 - 2] = Color.BLACK;
pic.pixelsMatrix[i][size * 20 - 3] = Color.BLACK;
pic.pixelsMatrix[size * 20 + 1][i] = Color.BLACK;
pic.pixelsMatrix[size * 20 + 2][i] = Color.BLACK;
pic.pixelsMatrix[size * 20 - 2][i] = Color.BLACK;
pic.pixelsMatrix[size * 20 - 3][i] = Color.BLACK;
}
}
for (Function func : functions) {
for (int i = 0; i < size * 40; i++) {
for (int j = 0; j < size * 40; j++) {
double x = i / 20.0f - size;
double y = j /20.0f - size;
if (func.func.func(x, y)) {
pic.pixelsMatrix[size * 40 - 1 - j][i] = func.color;
}
}
}
}
return pic;
}
public interface Functial {
boolean func(double x, double y);
}
public static boolean equal(double a, double b) {
double n = 0.05;
return -n < (a - b) && (a - b) < n;
}
public static class Function {
Functial func;
Color color;
public Function(Functial func, Color color) {
this.func = func;
this.color = color;
}
public Function(Functial func) {
this.func = func;
this.color = Color.BLACK;
}
}
}
??项目到这里就算完成了,让我们来测试一下。
三、项目测试
??写一个Main函数:
package www.spd;
import www.spd.pic.Color;
import static www.spd.pic.getPicByFunc.*;
import static java.lang.Math.*;
public class Main {
@SuppressWarnings("all")
public static void main(String[] args) {
double tau = 0.3;
int a = 4;
getPic(new Function[]{
new Function((x, y) -> equal(pow(x, 2) + pow(y, 2) + a * y, a * sqrt(pow(x, 2) + pow(y, 2))), Color.RED),
new Function((x, y) -> equal(y, (1 / tau * sqrt(2 * PI)) * exp(-pow(x, 2) / 2 * pow(tau, 2))), Color.GREEN),
new Function(new Circle(3, 3, 3), Color.BLUE),
new Function(new Circle(-2, -2, 2), new Color((byte)0x66, (byte)0xcc, (byte)0xff))
}, 20).createFile();
}
public static class Circle implements Functial {
public final int x;
public final int y;
public final int radius;
@Override
public boolean func(double x, double y) {
return equal(pow(x-this.x, 2) + pow(y-this.y, 2), pow(radius, 2));
}
public Circle(int x, int y, int radius) {
this.x = x;
this.y = y;
this.radius = radius;
}
}
}
??测试运行结果: ??这张bmp格式的图片下面煞风景的有水印,我也不知道怎么去,又想去的可以试试用我做的项目,双层for循环读取像素,若发现有不是#ffffff也不是#0000ff、#00ff00、#ff0000、#66ccff的像素,就把它赋值为#ffffff,就能去水印了。
四、项目的缺点:
??我们来用我这个项目绘制一个抛物线试试: ??很明显,上面成了断点了。这很容易用数学推理出来原因。
??我这个项目生成的是位图而不是矢图,自然就会出现这种状况,解决方案就是生成矢图,但那样就没有意思了。
|