將圖像劃分為 val × val 像素塊,每塊內所有像素統一為該塊左上角像素的顏色。邊緣不足塊大小的區域同理處理。本質是「以線帶面」:掃描行,按塊記錄基準色,填充塊內其餘像素;非基準行直接復制上一行數據。
示意(val=2):
原像素: 馬賽克後: A B C D A A C C E F G H → A A C C I J K L I I K K M N O P I I K K
使用 Bitmap.LockBits 獲取內存直訪指針,配合 unsafe 上下文逐像素操作,避免 GetPixel/SetPixel 的極慢托管調用。
public static Bitmap ApplyMosaic(Bitmap source, int blockSize) {
if (source == null) return null;
if (blockSize <= 1) return source; // 無效塊大小直接返回
int w = source.Width, h = source.Height;
int stdR = 0, stdG = 0, stdB = 0;
// 鎖定位圖內存,格式 24bppRgb (3 bytes/pixel: BGR 順序)
BitmapData data = source.LockBits(
new Rectangle(0, 0, w, h),
ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
unsafe {
byte* ptr = (byte*)data.Scan0;
int stride = data.Stride; // 行跨度(含內存對齊填充)
for (int y = 0; y < h; y++) {
byte* rowPtr = ptr + y * stride;
for (int x = 0; x < w; x++) {
byte* pixel = rowPtr + x * 3;
if (y % blockSize == 0) { // 基準行
if (x % blockSize == 0) { // 基準列:記錄基準色
stdR = pixel[2]; stdG = pixel[1]; stdB = pixel[0];
} else { // 同塊內其餘列:填充基準色
pixel[0] = (byte)stdB; pixel[1] = (byte)stdG; pixel[2] = (byte)stdR;
}
} else { // 非基準行:直接復制上一行同列像素
byte* upperPixel = pixel - stride;
pixel[0] = upperPixel[0]; pixel[1] = upperPixel[1]; pixel[2] = upperPixel[2];
}
}
}
}
source.UnlockBits(data);
return source;
}
rowPtr,內層避免重複乘法。Format24bppRgb 內存實際為 BGR,讀寫時注意 [0]=B, [1]=G, [2]=R。blockSize <= 1 直接返回,避免無限循環/除零。public static Bitmap ApplyMosaicSafe(Bitmap source, int blockSize) {
if (source == null || blockSize <= 1) return source;
int w = source.Width, h = source.Height;
Bitmap result = new Bitmap(w, h, PixelFormat.Format24bppRgb);
using (Graphics g = Graphics.FromImage(result)) {
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
// 縮小再放大產生馬賽克效果(GPU 加速,極快)
int sw = Math.Max(1, w / blockSize);
int sh = Math.Max(1, h / blockSize);
using (Bitmap small = new Bitmap(source, sw, sh)) {
g.DrawImage(small, 0, 0, w, h);
}
}
return result;
}
此法利用 NearestNeighbor 插值模式 + 縮放繪圖,代碼簡潔、無需 unsafe、自動利用 GPU 加速,適合大多數業務場景。