一、專案簡介:
??先給大家看一下這個小專案的效果:

??運行后生成的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;
/**
* 三個引數分別是三種三原色對應的數值
* @param red 紅色
* @param green 綠色
* @param blue 藍色
*/
public Color(byte red, byte green, byte blue) {
this.red = red;
this.green = green;
this.blue = blue;
}
/**
* 繼承{@link Object#toString()}方法,以實作方便輸出
*/
@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);
}
/**
* 將顏色資訊轉換成位元組陣列
* @return
*/
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;
//整個BMP檔案的大小
int bfSize;
//位圖的寬度,單位是像素
int biWidth;
//位圖的高度,單位是像素
int biHeight;
//位圖全部像素占用的位元組數,BI_RGB時可設為0
int biSizeImage;
//因為32位的Windows作業系統處理4個位元組(32位)的速度比較快,所以BMP的每一行顏色占用的位元組數規定為4的整數倍,
//如果一行顏色有兩個像素,共占用6位元組,如果要補齊4*2=8位元組,就要再加兩個0位元組,
//經計算,appendBit = biWidth % 4;
int appendBytes;
//表示一行像素真正占多少位元組,即本身要占的位元組加上補齊位元組
int widthBytes;
//圖片的像素資訊
Color[][] pixelsMatrix;
public BMP(String path) {
this(new File(path));
}
/**
* 通過傳參{@link java.io.File}獲得圖片的各種資訊(即所有的成員變數)
* @param file BMP檔案
*/
public BMP(File file) {
/* 獲得檔案名稱并通過后綴名利用正則運算式判斷是不是標準的bmp圖片檔案 */
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("檔案為空檔案!!");
/* bmp檔案前兩位元組是“bm”即0x4d42,用來做標識,若前兩位不是該位元組則拋出例外 */
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);
/* 1c到1f位表示一個像素占多少位,24位則是常用的十六進制顏色表示所對應的,兩位就是黑白圖片 */
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回圈獲取像素資訊 */
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;
}
}
}
/**
* 獲得某種顏色的圖片
* @param color 若傳參紅則為一張只有紅色的圖片,以此類推
* @param name 檔案名
* @param width 圖片寬度
* @param height 圖片高度
*/
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];
/* 遍歷矩陣把color傳參給每一個像素 */
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);
}
/**
* 繼承{@link Object#toString()}實作列印圖片資訊
*/
@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}";
}
/**
* 通過傳參{@link java.io.File} 并利用緩沖流{@link java.io.BufferedInputStream}獲得其位元組流
* @param file bmp檔案
* @return 圖片檔案的位元組流
*/
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;
}
}
/**
* 該方法與{@link #integerToByteArray(int, int)}相反<br/>
* 獲得位元組陣列的某幾位對應的整型數<br/>
* 主要在中{@link #BMP(File)}中呼叫
* @param arr 位元組陣列
* @param begin 開始索引
* @param end 結束索引
* @return 對應的整型數
*/
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;
}
/**
* 該方法與{@link #byteArrayToInteger(byte[], int, int)}相反<br/>
* 獲得整型數對應的位元組陣列
* 重要在{@link #toByteArray()}中呼叫
* @param size 回傳的陣列占幾個位元組
* @param num 整型數的值
* @return 回傳的位元組陣列
*/
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;
}
/**
* 把圖片檔案整個轉化成位元組流
* @return 圖片檔案對應的位元組流
*/
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;
}
}
/**
* 把圖片檔案的位元組流通過輸出流{@link java.io.FileOutputStream}寫入到外部檔案中去
*/
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);
}
/**
*
* @param name 寫出的檔案名
* @param functions 幾個函式
* @param size 圖片尺寸
* @return 回傳圖片
*/
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,就能去水印了,
四、專案的缺點:
??我們來用我這個專案繪制一個拋物線試試:

??很明顯,上面成了斷點了,這很容易用數學推理出來原因,
??我這個專案生成的是位圖而不是矢圖,自然就會出現這種狀況,解決方案就是生成矢圖,但那樣就沒有意思了,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/317718.html
標籤:其他
