利用Java實(shí)現(xiàn)讀寫(xiě)bmp文件的示例代碼
一、項(xiàng)目背景詳細(xì)介紹
位圖(Bitmap,BMP)是一種最原始、最簡(jiǎn)單的圖像文件格式,由微軟和 IBM 在 1980 年代聯(lián)合制定,用于 Windows 操作系統(tǒng)。與 JPEG、PNG 等壓縮格式相比,BMP 文件存儲(chǔ)的是未經(jīng)壓縮的原始像素?cái)?shù)據(jù),文件頭結(jié)構(gòu)也相對(duì)簡(jiǎn)單,包含 BMP 文件頭(14 字節(jié))和 DIB 信息頭(通常 40 字節(jié))的元數(shù)據(jù),后面直接跟隨像素?cái)?shù)據(jù)。由于無(wú)壓縮且像素排列規(guī)則,BMP 文件成為圖像處理入門的首選格式,也是許多學(xué)習(xí)圖像算法、文件格式解析的范例。
在 Java 生態(tài)中,雖然 ImageIO 支持讀取和寫(xiě)入 BMP,但其實(shí)現(xiàn)并不支持所有 BMP 變種(如帶調(diào)色板的 8 位 BMP、壓縮的 RLE 格式、位域 BI_BITFIELDS)。更重要的是,通過(guò)手寫(xiě) BMP 解析與生成,可以深入理解二進(jìn)制文件結(jié)構(gòu)、字節(jié)對(duì)齊、像素存儲(chǔ)順序、色彩通道排列、大小端問(wèn)題,以及 Java NIO、ByteBuffer、DataInputStream/DataOutputStream 等 API 的使用。
本項(xiàng)目旨在用純 Java 從零實(shí)現(xiàn)一個(gè)輕量級(jí)的 BMP 文件讀寫(xiě)庫(kù),支持以下功能:
讀取常見(jiàn)的 24 位真彩色 BMP 文件,解析文件頭、信息頭、像素?cái)?shù)據(jù);
將內(nèi)存中的像素?cái)?shù)據(jù)(ARGB 或 RGB 數(shù)組)寫(xiě)出為標(biāo)準(zhǔn) BMP 文件;
支持帶調(diào)色板的 8 位灰度 BMP 讀寫(xiě);
支持行字節(jié)對(duì)齊與填充;
提供簡(jiǎn)單易用的 API:
BmpImage read(File)
、void write(BmpImage, File)
;包含單元測(cè)試與示例,便于學(xué)習(xí)和集成。
通過(guò)本項(xiàng)目,您將掌握二進(jìn)制文件解析、內(nèi)存與磁盤(pán)數(shù)據(jù)映射、圖像像素處理、文件 I/O、字節(jié)序與對(duì)齊等核心技術(shù),既可用于圖像算法學(xué)習(xí),也可在不依賴第三方庫(kù)的情況下完成基礎(chǔ)圖像處理需求。
二、項(xiàng)目需求詳細(xì)介紹
核心功能
BMP 讀取
解析 BMP 文件頭(14 字節(jié)),獲取文件大小、像素?cái)?shù)據(jù)偏移;
解析 DIB 信息頭(至少 BITMAPINFOHEADER,40 字節(jié)),獲取寬度、高度、位深、壓縮方式、像素?cái)?shù)據(jù)大??;
支持 24 位(無(wú)調(diào)色板)和 8 位(帶調(diào)色板)兩種常見(jiàn)格式;
讀取調(diào)色板數(shù)據(jù)(8 位 BMP);
讀取像素?cái)?shù)據(jù),并根據(jù)行對(duì)齊規(guī)則計(jì)算實(shí)際字節(jié)長(zhǎng)度,轉(zhuǎn)換為
int[][]
或byte[][]
數(shù)組表示。
BMP 寫(xiě)入
將內(nèi)存中像素?cái)?shù)據(jù)構(gòu)造 BMP 文件頭和 DIB 頭,計(jì)算文件大小與偏移;
支持將
int[][]
(24 位真彩)或byte[][]
(8 位灰度)像素?cái)?shù)據(jù)寫(xiě)入文件;自動(dòng)填充行尾對(duì)齊字節(jié)(行長(zhǎng)度必須是 4 的倍數(shù));
寫(xiě)入調(diào)色板(灰度表),寫(xiě)入像素?cái)?shù)據(jù)。
API 設(shè)計(jì)
class BmpImage
:封裝寬度、高度、位深、調(diào)色板(可選)、像素?cái)?shù)據(jù);class BmpReader
:靜態(tài)方法BmpImage read(File) throws IOException, BmpParseException
;class BmpWriter
:靜態(tài)方法void write(BmpImage, File) throws IOException
;自定義異常
BmpParseException
用于格式錯(cuò)誤。
擴(kuò)展需求
支持 32 位帶 Alpha 通道 BMP(可選擴(kuò)展);
支持 RLE 壓縮的 8 位 BMP(高階擴(kuò)展);
提供
BmpImage toBufferedImage()
方法轉(zhuǎn)換為java.awt.image.BufferedImage
;提供從
BufferedImage
構(gòu)建BmpImage
的工廠方法;
性能與健壯性
使用
BufferedInputStream
、BufferedOutputStream
或 NIOFileChannel
進(jìn)行高效 I/O;對(duì)所有讀取步驟進(jìn)行合法性校驗(yàn),格式不符時(shí)拋出
BmpParseException
;單元測(cè)試覆蓋寬高、位深、對(duì)齊、調(diào)色板、異常路徑。
文檔與測(cè)試
完整 Javadoc 注釋;
JUnit 5 單元測(cè)試,測(cè)試案例包括小尺寸 BMP、大尺寸 BMP、無(wú)效文件、非 BMP 文件;
示例主程序演示讀取 BMP 文件并保存為另一個(gè) BMP。
三、相關(guān)技術(shù)詳細(xì)介紹
BMP 文件結(jié)構(gòu)
Bitmap File Header(BITMAPFILEHEADER):14 字節(jié)
bfType
(2 bytes): 固定為 “BM” (0x42 0x4D);bfSize
(4 bytes): 文件總大?。ㄗ止?jié));bfReserved1
、bfReserved2
(各 2 bytes):保留,通常為 0;bfOffBits
(4 bytes): 像素?cái)?shù)據(jù)在文件中的偏移量(字節(jié)位置)。
DIB Header(BITMAPINFOHEADER):40 字節(jié)
biSize
(4 bytes): DIB 頭大小,通常為 40;biWidth
(4 bytes)、biHeight
(4 bytes):圖像寬度、高度(像素);biPlanes
(2 bytes): 顏色平面數(shù),固定為 1;biBitCount
(2 bytes): 每像素位數(shù),如 1、4、8、16、24、32;biCompression
(4 bytes):壓縮方式(0 = BI_RGB 無(wú)壓縮;1 = BI_RLE8;2 = BI_RLE4;3 = BI_BITFIELDS);biSizeImage
(4 bytes):像素?cái)?shù)據(jù)大?。ㄗ止?jié)),可為 0;biXPelsPerMeter
、biYPelsPerMeter
(各 4 bytes):水平/垂直分辨率;biClrUsed
(4 bytes):調(diào)色板中顏色數(shù),0 表示默認(rèn);biClrImportant
(4 bytes):重要顏色數(shù),0 表示全部重要。
Color Table(可選):當(dāng)
biBitCount
≤ 8 時(shí)存在,每條 4 字節(jié)(B, G, R, Reserved)Pixel Array:按行從下到上(BMP 默認(rèn)),每行左到右;每行長(zhǎng)度需填充到 4 字節(jié)對(duì)齊。
Java I/O 與 NIO
DataInputStream
/DataOutputStream
:方便讀取/寫(xiě)入基本類型大端或小端;ByteBuffer
:調(diào)整字節(jié)序(order(ByteOrder.LITTLE_ENDIAN)
);FileChannel
+MappedByteBuffer
:可選內(nèi)存映射加速;BufferedInputStream
/BufferedOutputStream
:緩沖字節(jié)流提高效率。
字節(jié)對(duì)齊
BMP 每行像素?cái)?shù)據(jù)占用字節(jié)數(shù) =
((width * bitsPerPixel + 31) / 32) * 4
對(duì)齊后每行末尾填充 0x00。
錯(cuò)誤處理
當(dāng)
bfType
不是 “BM” 或biBitCount
不支持時(shí),拋出BmpParseException
;當(dāng)文件過(guò)短、偏移超出或數(shù)據(jù)不完整時(shí),拋出異常。
Java2D 互操作
將
BmpImage
轉(zhuǎn)為BufferedImage
:
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); for (y,h) ... img.setRGB(x,y,pixel);
從 BufferedImage
構(gòu)造 BmpImage
:
int rgb = img.getRGB(x,y); // 分離 R,G,B 通道
四、實(shí)現(xiàn)思路詳細(xì)介紹
數(shù)據(jù)模型定義
class BmpImage
:
public class BmpImage { int width, height; short bitCount; // 8 或 24 int[][] pixels24; // [row][col] 每像素 0x00RRGGBB byte[][] pixels8; // [row][col] 調(diào)色板索引 int[] palette; // 8 位調(diào)色板,length = colorsUsed }
- 只存儲(chǔ)必要字段,其它 DIB 信息頭字段可忽略或保留。
讀取流程(BmpReader)
- 打開(kāi)文件,使用
DataInputStream
包裝BufferedInputStream
; 讀取并校驗(yàn) BMP 文件頭:
readUnsignedShortLE()
(小端),檢查 “BM”;讀取文件大小、保留字段、像素偏移;
讀取 DIB 頭長(zhǎng)度,判斷格式,僅處理
biSize == 40
;讀取寬、高、平面數(shù)、位深、壓縮方式;
計(jì)算行占用字節(jié)數(shù)
rowBytes = ((width * bitCount + 31) / 32) * 4
;若
bitCount == 8
,讀取colorsUsed
條調(diào)色板,每條 4 字節(jié),存入palette
;根據(jù)
height
正負(fù)判斷存儲(chǔ)方向(正值從下往上,負(fù)值自頂向下);分行讀取像素?cái)?shù)據(jù),解碼 24 位真彩色或 8 位索引,存入
pixels24
或pixels8
;
寫(xiě)入流程(BmpWriter)
根據(jù)
BmpImage
字段,計(jì)算rowBytes
與pixelDataSize = rowBytes * abs(height)
;bfSize = 14 + dibSize + paletteSize + pixelDataSize
;使用
DataOutputStream
,按小端順序?qū)懭?BITMAPFILEHEADER;寫(xiě)入 BITMAPINFOHEADER 各字段;
若 8 位,寫(xiě)入調(diào)色板;
按行填充寫(xiě)入像素?cái)?shù)據(jù),注意 4 字節(jié)對(duì)齊,寫(xiě)入行尾填充字節(jié);
輔助方法
readUnsignedShortLE()
、readIntLE()
:讀取小端數(shù);writeShortLE()
、writeIntLE()
:寫(xiě)入小端;padZeros(int count)
:寫(xiě)入指定數(shù)量的 0;
與 BufferedImage 互操作
BmpImage toBufferedImage()
:構(gòu)造BufferedImage
并填充像素;static BmpImage fromBufferedImage(BufferedImage img, boolean usePalette)
;
異常與校驗(yàn)
在各讀取階段檢查可用字節(jié)數(shù);
對(duì)不支持的格式或參數(shù),立即拋
BmpParseException
;在寫(xiě)入前驗(yàn)證
bitCount
、數(shù)據(jù)數(shù)組與寬高一致。
五、完整實(shí)現(xiàn)代碼
// =================================================== // 文件:src/main/java/com/example/bmp/BmpImage.java // =================================================== package com.example.bmp; import java.awt.image.BufferedImage; /** * BMP 圖像數(shù)據(jù)模型 */ public class BmpImage { public int width; public int height; public short bitCount; // 8 或 24 public int[][] pixels24; // 每像素 0x00RRGGBB public byte[][] pixels8; // 每像素調(diào)色板索引 public int[] palette; // 調(diào)色板,length = colorsUsed /** 轉(zhuǎn)換為 BufferedImage */ public BufferedImage toBufferedImage() { BufferedImage img = new BufferedImage(width, Math.abs(height), bitCount == 24 ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_BYTE_INDEXED); if (bitCount == 24) { for (int y = 0; y < Math.abs(height); y++) { for (int x = 0; x < width; x++) { img.setRGB(x, y, pixels24[y][x]); } } } else { // 8 位,需創(chuàng)建 IndexColorModel(此處略) // 簡(jiǎn)單填充為灰度圖 for (int y = 0; y < Math.abs(height); y++) { for (int x = 0; x < width; x++) { int idx = pixels8[y][x] & 0xFF; int c = palette[idx]; img.setRGB(x, y, c); } } } return img; } } // =================================================== // 文件:src/main/java/com/example/bmp/BmpParseException.java // =================================================== package com.example.bmp; /** BMP 解析異常 */ public class BmpParseException extends Exception { public BmpParseException(String msg) { super(msg); } } // =================================================== // 文件:src/main/java/com/example/bmp/BmpReader.java // =================================================== package com.example.bmp; import java.io.*; import java.nio.ByteOrder; /** * BMP 文件讀取器 */ public class BmpReader { public static BmpImage read(File file) throws IOException, BmpParseException { try (DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(file)))) { // 1. 讀取文件頭 int bfType = readUnsignedShortLE(dis); if (bfType != 0x4D42) throw new BmpParseException("非 BMP 文件"); int bfSize = readIntLE(dis); dis.skipBytes(4); // reserved int bfOffBits = readIntLE(dis); // 2. 讀取 DIB 頭 int dibSize = readIntLE(dis); if (dibSize != 40) throw new BmpParseException("不支持的 DIB 頭大小: " + dibSize); int width = readIntLE(dis); int height = readIntLE(dis); short planes = readShortLE(dis); short bitCount = readShortLE(dis); int compression = readIntLE(dis); if (compression != 0) throw new BmpParseException("不支持壓縮: " + compression); int imageSize = readIntLE(dis); dis.skipBytes(16); // 跳過(guò)分辨率與顏色信息 int colorsUsed = readIntLE(dis); if (colorsUsed == 0 && bitCount <= 8) { colorsUsed = 1 << bitCount; } // 構(gòu)造 BmpImage BmpImage img = new BmpImage(); img.width = width; img.height = height; img.bitCount = bitCount; // 3. 讀取調(diào)色板(8 位) if (bitCount == 8) { img.palette = new int[colorsUsed]; for (int i = 0; i < colorsUsed; i++) { int b = dis.readUnsignedByte(); int g = dis.readUnsignedByte(); int r = dis.readUnsignedByte(); dis.readUnsignedByte(); // 保留 img.palette[i] = (r << 16) | (g << 8) | b; } img.pixels8 = new byte[Math.abs(height)][width]; } else if (bitCount == 24) { img.pixels24 = new int[Math.abs(height)][width]; } else { throw new BmpParseException("僅支持 8 位和 24 位 BMP"); } // 4. 跳轉(zhuǎn)到像素?cái)?shù)據(jù)偏移 long skipped = dis.skip(bfOffBits - 14 - dibSize - (bitCount==8 ? colorsUsed*4 : 0)); // 5. 計(jì)算行長(zhǎng)度(字節(jié))對(duì)齊到 4 字節(jié) int rowBytes = ((width * bitCount + 31) / 32) * 4; // 6. 讀取像素?cái)?shù)據(jù) boolean bottomUp = height > 0; int absHeight = Math.abs(height); for (int row = 0; row < absHeight; row++) { int y = bottomUp ? absHeight - 1 - row : row; byte[] rowData = new byte[rowBytes]; dis.readFully(rowData); ByteArrayInputStream rowIn = new ByteArrayInputStream(rowData); for (int x = 0; x < width; x++) { if (bitCount == 24) { int b = rowIn.read(); int g = rowIn.read(); int r = rowIn.read(); img.pixels24[y][x] = (r << 16) | (g << 8) | b; } else { int idx = rowIn.read(); img.pixels8[y][x] = (byte) idx; } } } return img; } } // 小端讀取輔助 private static int readUnsignedShortLE(DataInputStream dis) throws IOException { int b1 = dis.readUnsignedByte(); int b2 = dis.readUnsignedByte(); return (b2 << 8) | b1; } private static short readShortLE(DataInputStream dis) throws IOException { int u = readUnsignedShortLE(dis); return (short) u; } private static int readIntLE(DataInputStream dis) throws IOException { int b1 = dis.readUnsignedByte(); int b2 = dis.readUnsignedByte(); int b3 = dis.readUnsignedByte(); int b4 = dis.readUnsignedByte(); return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1; } } // =================================================== // 文件:src/main/java/com/example/bmp/BmpWriter.java // =================================================== package com.example.bmp; import java.io.*; /** * BMP 文件寫(xiě)入器 */ public class BmpWriter { public static void write(BmpImage img, File file) throws IOException { try (DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) { int width = img.width; int absHeight = Math.abs(img.height); int bitCount = img.bitCount; int rowBytes = ((width * bitCount + 31) / 32) * 4; int imageSize = rowBytes * absHeight; int paletteSize = (bitCount == 8 ? img.palette.length * 4 : 0); int bfOffBits = 14 + 40 + paletteSize; int bfSize = bfOffBits + imageSize; // 1. 寫(xiě)文件頭 writeShortLE(dos, 0x4D42); // "BM" writeIntLE(dos, bfSize); writeShortLE(dos, 0); writeShortLE(dos, 0); writeIntLE(dos, bfOffBits); // 2. 寫(xiě) DIB 頭(BITMAPINFOHEADER) writeIntLE(dos, 40); writeIntLE(dos, width); writeIntLE(dos, img.height); writeShortLE(dos, 1); // planes writeShortLE(dos, bitCount); writeIntLE(dos, 0); // BI_RGB writeIntLE(dos, imageSize); writeIntLE(dos, 0); writeIntLE(dos, 0); // 分辨率 writeIntLE(dos, bitCount == 8 ? img.palette.length : 0); writeIntLE(dos, 0); // 3. 寫(xiě)調(diào)色板 if (bitCount == 8) { for (int c : img.palette) { int r = (c >> 16) & 0xFF; int g = (c >> 8) & 0xFF; int b = c & 0xFF; dos.writeByte(b); dos.writeByte(g); dos.writeByte(r); dos.writeByte(0); } } // 4. 寫(xiě)像素?cái)?shù)據(jù) byte[] pad = new byte[rowBytes - (width * bitCount / 8)]; for (int row = absHeight - 1; row >= 0; row--) { if (bitCount == 24) { for (int x = 0; x < width; x++) { int rgb = img.pixels24[row][x]; dos.writeByte(rgb & 0xFF); // B dos.writeByte((rgb >> 8) & 0xFF); // G dos.writeByte((rgb >> 16) & 0xFF); // R } } else { for (int x = 0; x < width; x++) { dos.writeByte(img.pixels8[row][x]); } } dos.write(pad); } } } // 小端寫(xiě)入輔助 private static void writeShortLE(DataOutputStream dos, int v) throws IOException { dos.writeByte(v & 0xFF); dos.writeByte((v >> 8) & 0xFF); } private static void writeIntLE(DataOutputStream dos, int v) throws IOException { dos.writeByte(v & 0xFF); dos.writeByte((v >> 8) & 0xFF); dos.writeByte((v >> 16) & 0xFF); dos.writeByte((v >> 24) & 0xFF); } } // 文件:src/main/java/com/example/bmp/Main.java package com.example.bmp; import java.io.File; public class Main { public static void main(String[] args) throws Exception { // 讀取 BMP BmpImage img = BmpReader.read(new File("input.bmp")); System.out.println("讀取完成: " + img.width + "x" + Math.abs(img.height) + " 位深=" + img.bitCount); // 轉(zhuǎn)換為 BufferedImage 并另存為 PNG(示例) // ImageIO.write(img.toBufferedImage(), "png", new File("out.png")); // 修改像素:反轉(zhuǎn)顏色示例 if (img.bitCount == 24) { for (int y = 0; y < Math.abs(img.height); y++) { for (int x = 0; x < img.width; x++) { int rgb = img.pixels24[y][x]; int r = 255 - ((rgb >> 16) & 0xFF); int g = 255 - ((rgb >> 8) & 0xFF); int b = 255 - (rgb & 0xFF); img.pixels24[y][x] = (r << 16) | (g << 8) | b; } } } // 寫(xiě)入 BMP BmpWriter.write(img, new File("output.bmp")); System.out.println("寫(xiě)入完成"); } }
六、代碼詳細(xì)解讀
BmpImage:封裝 BMP 圖像的核心數(shù)據(jù),包括寬度、高度、位深、調(diào)色板(8 位)或真彩色像素?cái)?shù)組,以及與
BufferedImage
互操作的方法。BmpParseException:自定義解析異常,用于格式校驗(yàn)失敗時(shí)拋出。
BmpReader:
讀取 BMP 文件頭(小端),檢查“BM”標(biāo)識(shí);
讀取 DIB 頭中的寬高、位深、壓縮方式,并校驗(yàn)僅支持無(wú)壓縮 8/24 位;
讀取調(diào)色板(8 位),或分配像素?cái)?shù)組;
跳轉(zhuǎn)到像素?cái)?shù)據(jù)偏移位置,按行讀取像素并考慮 4 字節(jié)對(duì)齊;
BmpWriter:
計(jì)算行長(zhǎng)度、像素?cái)?shù)據(jù)大小和文件總大?。?/p>
寫(xiě)入文件頭與 DIB 頭(小端),包括必要字段;
寫(xiě)入調(diào)色板(8 位)或跳過(guò);
按行自下而上寫(xiě)入像素?cái)?shù)據(jù),并填充行尾對(duì)齊字節(jié);
Main:示例演示 BMP 文件讀取、像素修改(反色)、BMP 寫(xiě)入,以及與
BufferedImage
的互操作。
七、項(xiàng)目詳細(xì)總結(jié)
功能完整:支持讀取和寫(xiě)入最常見(jiàn)的 8 位帶調(diào)色板 BMP 和 24 位真彩色 BMP;
純 Java 實(shí)現(xiàn):無(wú)第三方依賴,便于集成到任意 Java 項(xiàng)目;
對(duì)齊與字節(jié)序處理:正確實(shí)現(xiàn)行對(duì)齊及小端字節(jié)序,確??缙脚_(tái)一致性;
易用 API:提供
BmpReader.read()
、BmpWriter.write()
兩個(gè)靜態(tài)方法,簡(jiǎn)潔明了;性能可控:使用緩沖流和按行處理,內(nèi)存占用可控;
可擴(kuò)展:后續(xù)可加入 32 位 Alpha 通道、RLE 壓縮、性能優(yōu)化的 NIO 實(shí)現(xiàn)。
八、項(xiàng)目常見(jiàn)問(wèn)題及解答
Q:為何 BMP 讀取時(shí)要按行倒序?
A:BMP 默認(rèn)自下而上存儲(chǔ)像素,高度字段若為正值表示倒序;Q:如何支持其它 DIB 頭格式?
A:在解析時(shí)根據(jù)biSize
分支處理不同頭結(jié)構(gòu),如 BITMAPV2INFOHEADER(52 字節(jié));Q:寫(xiě)入 32 位帶 Alpha BMP?
A:將bitCount
設(shè)為 32,寫(xiě)入 BGRA 順序像素,DIB 頭中的位域需設(shè)置 BI_BITFIELDS;Q:如何提高大文件讀寫(xiě)性能?
A:可使用 NIOFileChannel
與MappedByteBuffer
,一次映射全部或部分文件;Q:寫(xiě)入時(shí)如何生成灰度調(diào)色板?
A:調(diào)色板數(shù)組palette[i] = (i << 16)|(i<<8)|i
,0-255 等級(jí)灰度。
九、擴(kuò)展方向與性能優(yōu)化
NIO 內(nèi)存映射:使用
FileChannel.map()
將文件映射到內(nèi)存,使用ByteBuffer
直接讀取寫(xiě)入,減少?gòu)?fù)制與方法調(diào)用;并行讀取與處理:對(duì)大圖分塊并行讀取和像素處理,提高多核利用率;
支持更多格式:擴(kuò)展到 RLE 壓縮的 8 位 BMP、16 位 RGB565、32 位 BI_BITFIELDS;
動(dòng)態(tài)調(diào)色板生成:支持自定義調(diào)色板或從圖像均衡化生成偽彩色;
與 Java2D 整合:提供直接在
Graphics2D
上繪制 BMP 數(shù)據(jù)的優(yōu)化方法;流式 API:提供從
InputStream
和OutputStream
讀取寫(xiě)入的重載方法,方便網(wǎng)絡(luò)傳輸;內(nèi)存優(yōu)化:使用壓縮存儲(chǔ)結(jié)構(gòu)、按需加載行數(shù)據(jù),處理超大圖像防止 OOM;
測(cè)試與基準(zhǔn):使用 JMH 對(duì)比
ImageIO
與本實(shí)現(xiàn)的讀寫(xiě)性能差異,并進(jìn)行調(diào)優(yōu);工具類集成:將項(xiàng)目打包為 Maven 依賴,提供 CLI 工具快速轉(zhuǎn)換 BMP 格式。
以上就是利用Java實(shí)現(xiàn)讀寫(xiě)bmp文件的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于Java讀寫(xiě)bmp文件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
關(guān)于SpringSecurity簡(jiǎn)介以及和Shiro的區(qū)別
這篇文章主要介紹了關(guān)于SpringSecurity簡(jiǎn)介以及和Shiro的區(qū)別,在Java應(yīng)用安全領(lǐng)域,Spring Security會(huì)成為被首先推崇的解決方案,就像我們看到服務(wù)器就會(huì)聯(lián)想到Linux一樣順理成章,需要的朋友可以參考下2023-07-07Java掌握本地與網(wǎng)絡(luò)日志技術(shù)方式
本文將深入探討本地日志與網(wǎng)絡(luò)日志的區(qū)別,分析網(wǎng)絡(luò)日志的適用場(chǎng)景,并以 Log4j 為例詳細(xì)介紹如何實(shí)現(xiàn)網(wǎng)絡(luò)日志記錄,我們將提供易于理解的代碼示例,并分享最佳實(shí)踐,以幫助開(kāi)發(fā)者在 Java 應(yīng)用程序中有效實(shí)施日志記錄策略2025-05-05java異常處理機(jī)制示例(java拋出異常、捕獲、斷言)
這篇文章主要介紹了java異常處理機(jī)制示例(java拋出異常、捕獲、斷言),需要的朋友可以參考下2014-05-05詳解SpringSecurity如何實(shí)現(xiàn)前后端分離
這篇文章主要為大家介紹了詳解SpringSecurity如何實(shí)現(xiàn)前后端分離,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03Java深入淺出掌握SpringBoot之MVC自動(dòng)配置原理篇
在進(jìn)行項(xiàng)目編寫(xiě)前,我們還需要知道一個(gè)東西,就是SpringBoot對(duì)我們的SpringMVC還做了哪些配置,包括如何擴(kuò)展,如何定制,只有把這些都搞清楚了,我們?cè)谥笫褂貌艜?huì)更加得心應(yīng)手2021-10-10Java實(shí)現(xiàn)提取HTML文件中的文本內(nèi)容
從?HTML?文件中提取文本內(nèi)容是數(shù)據(jù)抓取中的一個(gè)常見(jiàn)任務(wù),本文主要和大家分享了如何使用免費(fèi)?Java?API?從HTML?文件中提取文本內(nèi)容,需要的可以參考下2024-04-04淺談SpringBoot項(xiàng)目如何讓前端開(kāi)發(fā)提高效率(小技巧)
這篇文章主要介紹了淺談SpringBoot項(xiàng)目如何讓前端開(kāi)發(fā)提高效率(小技巧),主要介紹了Swagger和Nginx提高效率的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04淺析 ArrayList 和 LinkedList 有什么區(qū)別
ArrayList 和 LinkedList 有什么區(qū)別,是面試官非常喜歡問(wèn)的一個(gè)問(wèn)題。今天通過(guò)本文給大家詳細(xì)介紹下,感興趣的朋友跟隨小編一起看看吧2020-10-10springboot validator枚舉值校驗(yàn)功能實(shí)現(xiàn)
這篇文章主要介紹了springboot validator枚舉值校驗(yàn)功能實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01