写代码这事,掐指算来已经十有余年。
从html到css到javascript到vbscript到c#,从兴趣到职业,生活总是失落与惊喜并存。
绝大部分时候,出发并不是因为知道该到哪里去,只是知道不能再逗留下去了,如此而已。受过风吹雨打之后会有很多独有的感受及思考,主见开始生发并在摇摆中坚定,想来这就是成长了,嗯……就算是成长吧,呵呵!
话说微软的 .net core 3.0 即将发布了,作为一个码农,还是要关心一下的。从 .net core 2.0 及MySql官方开始支持 .net core 起,我尝试将自己的类库转成 .net standard 类库,开始了解 .net core 的成熟度,写了几个 demo ,之后就放弃了,因为我做的项目还是以web(asp.net core)为主,但是 .net core 对图片的处理是空白的,System.Drawing只是一个摆设(估计以后也是一样),即便是有很多人推崇的 ImageSharp 也因为接口变化过大且缺少文档在稍作尝试之后放弃了,我想,在 .net core 下应该还没有一个成熟的图片处理类库,还不足以迁移项目。如果按微软官方的意思要搞一个独立的图片处理应用来进行图片处理,那 asp.net core 只是作为一个简单的数据接口来用,这就很值得权衡一下了,毕竟中小型项目要去搞一个专门的图片处理应用是得不偿失的,不知道微软的产品和工程师们是否有过这些思量?又或者只是所谓的企业级应用才是他们的目标?
我假设 System.Drawing 在 .net core 中的下场就像 IE 浏览器和 Edge 浏览器在Win10 后的下场一样,也许该放弃了。那就找个替代方案吧,所以最终决定采用 SkiaSharp 来做图片处理,顺便处理一下二维码,毕竟也是图片嘛,并且二维码在天朝是很有场景可用的,之前在 .net framework 中我一致习惯用 ThoughtWorks.QRCode 来处理二维码的生成和解析,不过目前 ThoughtWorks.QRCode 好像还不支持在 .net core 中工作,因此二维码的生成和解析最后采用 ZXing.Net 来处理。
用 ZXing.Net 生成的二维码默认是有一定比例的白边的,大概算是一种工业标准,但是在实际的应用场景中有时候可能不需要白边,所以我对白边做了一点逻辑处理,对白边可以指定要保留的宽度像素值也可以保留原始的默认白边。
通过引用 SkiaSharp(1.68.0) 和 ZXing.Net(0.16.4) ,之后写了一个静态类,用来对图片进行缩放和裁剪等基本操作以及生成和解析二维码,代码如下:
1 public static class ImageHelper 2 { 3 static readonly long maxLength = 10485760;//10*1024*1024 4 public static SkiaSharp.SKEncodedImageFormat GetImageFormatBySuffix(string suffix) 5 { 6 var format = SkiaSharp.SKEncodedImageFormat.Jpeg; 7 if (string.IsNullOrEmpty(suffix)) 8 { 9 return format; 10 } 11 if (suffix[0] == '.') 12 { 13 suffix = suffix.Substring(1); 14 } 15 if (string.IsNullOrEmpty(suffix)) 16 { 17 return format; 18 } 19 suffix = suffix.ToUpper(); 20 switch (suffix) 21 { 22 case "PNG": 23 format = SkiaSharp.SKEncodedImageFormat.Png; 24 break; 25 case "GIF": 26 format = SkiaSharp.SKEncodedImageFormat.Gif; 27 break; 28 case "BMP": 29 format = SkiaSharp.SKEncodedImageFormat.Bmp; 30 break; 31 case "ICON": 32 format = SkiaSharp.SKEncodedImageFormat.Ico; 33 break; 34 case "ICO": 35 format = SkiaSharp.SKEncodedImageFormat.Ico; 36 break; 37 case "DNG": 38 format = SkiaSharp.SKEncodedImageFormat.Dng; 39 break; 40 case "WBMP": 41 format = SkiaSharp.SKEncodedImageFormat.Wbmp; 42 break; 43 case "WEBP": 44 format = SkiaSharp.SKEncodedImageFormat.Webp; 45 break; 46 case "PKM": 47 format = SkiaSharp.SKEncodedImageFormat.Pkm; 48 break; 49 case "KTX": 50 format = SkiaSharp.SKEncodedImageFormat.Ktx; 51 break; 52 case "ASTC": 53 format = SkiaSharp.SKEncodedImageFormat.Astc; 54 break; 55 } 56 return format; 57 } 58 public static SkiaSharp.SKEncodedImageFormat GetImageFormatByPath(string path) 59 { 60 var suffix = ""; 61 if (System.IO.Path.HasExtension(path)) 62 { 63 suffix = System.IO.Path.GetExtension(path); 64 } 65 return GetImageFormatBySuffix(suffix); 66 } 67 public static Tuple<int, int, long, SkiaSharp.SKEncodedImageFormat> GetImageInfo(string path) 68 { 69 if (string.IsNullOrEmpty(path)) 70 { 71 throw new Exception("路径不能为空"); 72 } 73 if (!System.IO.File.Exists(path)) 74 { 75 throw new Exception("文件不存在"); 76 } 77 var fileStream = new System.IO.FileStream(path, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read); //fileInfo.OpenRead(); 78 var fileLength = fileStream.Length; 79 if (fileLength > maxLength) 80 { 81 fileStream.Dispose(); 82 throw new Exception("文件过大"); 83 } 84 var sKManagedStream = new SkiaSharp.SKManagedStream(fileStream, true); 85 var sKBitmap = SkiaSharp.SKBitmap.Decode(sKManagedStream); 86 sKManagedStream.Dispose(); 87 88 if (sKBitmap.IsEmpty) 89 { 90 sKBitmap.Dispose(); 91 throw new Exception("文件无效"); 92 } 93 int w = sKBitmap.Width; 94 int h = sKBitmap.Height; 95 return new Tuple<int, int, long, SkiaSharp.SKEncodedImageFormat>(w, h, fileLength, GetImageFormatByPath(path)); 96 } 97 public static void ImageMaxCutByCenter(string path, string savePath, int saveWidth, int saveHeight, int quality) 98 { 99 var bytes = ImageMaxCutByCenter(path, saveWidth, saveHeight, quality); 100 if (bytes == null || bytes.Length < 1) 101 { 102 return; 103 } 104 string saveDirPath = System.IO.Path.GetDirectoryName(savePath); 105 if (!System.IO.Directory.Exists(saveDirPath)) 106 { 107 System.IO.Directory.CreateDirectory(saveDirPath); 108 } 109 System.IO.FileStream fs = new System.IO.FileStream(savePath, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write, System.IO.FileShare.None); 110 fs.Write(bytes, 0, bytes.Length); 111 fs.Close(); 112 } 113 public static byte[] ImageMaxCutByCenter(string path, int saveWidth, int saveHeight, int quality) 114 { 115 byte[] bytes = null; 116 if (!System.IO.File.Exists(path)) 117 { 118 return bytes; 119 } 120 var fileStream = new System.IO.FileStream(path, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read); //fileInfo.OpenRead(); 121 if (fileStream.Length > maxLength) 122 { 123 fileStream.Dispose(); 124 return bytes; 125 } 126 var sKManagedStream = new SkiaSharp.SKManagedStream(fileStream, true); 127 var sKBitmap = SkiaSharp.SKBitmap.Decode(sKManagedStream); 128 sKManagedStream.Dispose(); 129 130 if (sKBitmap.IsEmpty) 131 { 132 return bytes; 133 } 134 135 if (saveWidth < 1) { saveWidth = 1; } 136 if (saveHeight < 1) { saveHeight = 1; } 137 if (quality < 1) { quality = 1; } 138 if (quality > 100) { quality = 100; } 139 140 int oW = sKBitmap.Width; 141 int oH = sKBitmap.Height; 142 int cutW = saveWidth; 143 int cutH = saveHeight; 144 double ratio = 1; 145 if (cutW > oW) 146 { 147 ratio = (double)oW / (double)cutW; 148 cutH = Convert.ToInt32((double)cutH * ratio); 149 cutW = oW; 150 if (cutH > oH) 151 { 152 ratio = (double)oH / (double)cutH; 153 cutW = Convert.ToInt32((double)cutW * ratio); 154 cutH = oH; 155 } 156 } 157 else if (cutW < oW) 158 { 159 ratio = (double)oW / (double)cutW; 160 cutH = Convert.ToInt32(Convert.ToDouble(cutH) * ratio); 161 cutW = oW; 162 if (cutH > oH) 163 { 164 ratio = (double)oH / (double)cutH; 165 cutW = Convert.ToInt32((double)cutW * ratio); 166 cutH = oH; 167 } 168 } 169 else 170 { 171 if (cutH > oH) 172 { 173 ratio = (double)oH / (double)cutH; 174 cutW = Convert.ToInt32((double)cutW * ratio); 175 cutH = oH; 176 } 177 } 178 int startX = oW > cutW ? (oW / 2 - cutW / 2) : (cutW / 2 - oW / 2); 179 int startY = oH > cutH ? (oH / 2 - cutH / 2) : (cutH / 2 - oH / 2); 180 181 var sKBitmap2 = new SkiaSharp.SKBitmap(saveWidth, saveHeight); 182 var sKCanvas = new SkiaSharp.SKCanvas(sKBitmap2); 183 var sKPaint = new SkiaSharp.SKPaint 184 { 185 FilterQuality = SkiaSharp.SKFilterQuality.Medium, 186 IsAntialias = true 187 }; 188 sKCanvas.DrawBitmap( 189 sKBitmap, 190 new SkiaSharp.SKRect 191 { 192 Location = new SkiaSharp.SKPoint { X = startX, Y = startY }, 193 Size = new SkiaSharp.SKSize { Height = cutH, Width = cutW } 194 }, 195 new SkiaSharp.SKRect 196 { 197 Location = new SkiaSharp.SKPoint { X = 0, Y = 0 }, 198 Size = new SkiaSharp.SKSize { Height = saveHeight, Width = saveWidth } 199 }, sKPaint); 200 sKCanvas.Dispose(); 201 var sKImage2 = SkiaSharp.SKImage.FromBitmap(sKBitmap2); 202 sKBitmap2.Dispose(); 203 var data = sKImage2.Encode(GetImageFormatByPath(path), quality); 204 sKImage2.Dispose(); 205 bytes = data.ToArray(); 206 data.Dispose(); 207 208 return bytes; 209 } 210 public static void ImageScalingToRange(string path, string savePath, int maxWidth, int maxHeight, int quality) 211 { 212 var bytes = ImageScalingToRange(path, maxWidth, maxHeight, quality); 213 if (bytes == null || bytes.Length < 1) 214 { 215 return; 216 } 217 string saveDirPath = System.IO.Path.GetDirectoryName(savePath); 218 if (!System.IO.Directory.Exists(saveDirPath)) 219 { 220 System.IO.Directory.CreateDirectory(saveDirPath); 221 } 222 System.IO.FileStream fs = new System.IO.FileStream(savePath, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write, System.IO.FileShare.None); 223 fs.Write(bytes, 0, bytes.Length); 224 fs.Close(); 225 } 226 public static byte[] ImageScalingToRange(string path, int maxWidth, int maxHeight, int quality) 227 { 228 byte[] bytes = null; 229 if (!System.IO.File.Exists(path)) 230 { 231 return bytes; 232 } 233 var fileStream = new System.IO.FileStream(path, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read); //fileInfo.OpenRead(); 234 if (fileStream.Length > maxLength) 235 { 236 fileStream.Dispose(); 237 return bytes; 238 } 239 var sKManagedStream = new SkiaSharp.SKManagedStream(fileStream, true); 240 var sKBitmap = SkiaSharp.SKBitmap.Decode(sKManagedStream); 241 sKManagedStream.Dispose(); 242 243 if (sKBitmap.IsEmpty) 244 { 245 return bytes; 246 } 247 248 if (maxWidth < 1) { maxWidth = 1; } 249 if (maxHeight < 1) { maxHeight = 1; } 250 if (quality < 1) { quality = 1; } 251 if (quality > 100) { quality = 100; } 252 253 int oW = sKBitmap.Width; 254 int oH = sKBitmap.Height; 255 int nW = oW; 256 int nH = oH; 257 258 if (nW < maxWidth && nH < maxHeight)//放大 259 { 260 if (nW < maxWidth) 261 { 262 var r = (double)maxWidth / (double)nW; 263 nW = maxWidth; 264 nH = (int)Math.Floor((double)nH * r); 265 } 266 if (nH < maxHeight) 267 { 268 var r = (double)maxHeight / (double)nH; 269 nH = maxHeight; 270 nW = (int)Math.Floor((double)nW * r); 271 } 272 } 273 //限制超出(缩小) 274 if (nW > maxWidth) 275 { 276 var r = (double)maxWidth / (double)nW; 277 nW = maxWidth; 278 nH = (int)Math.Floor((double)nH * r); 279 } 280 if (nH > maxHeight) 281 { 282 var r = (double)maxHeight / (double)nH; 283 nH = maxHeight; 284 nW = (int)Math.Floor((double)nW * r); 285 } 286 287 288 var sKBitmap2 = new SkiaSharp.SKBitmap(nW, nH); 289 var sKCanvas = new SkiaSharp.SKCanvas(sKBitmap2); 290 var sKPaint = new SkiaSharp.SKPaint 291 { 292 FilterQuality = SkiaSharp.SKFilterQuality.Medium, 293 IsAntialias = true 294 }; 295 sKCanvas.DrawBitmap( 296 sKBitmap, 297 new SkiaSharp.SKRect 298 { 299 Location = new SkiaSharp.SKPoint { X = 0, Y = 0 }, 300 Size = new SkiaSharp.SKSize { Height = oH, Width = oW } 301 }, 302 new SkiaSharp.SKRect 303 { 304 Location = new SkiaSharp.SKPoint { X = 0, Y = 0 }, 305 Size = new SkiaSharp.SKSize { Height = nH, Width = nW } 306 }, sKPaint); 307 sKCanvas.Dispose(); 308 var sKImage2 = SkiaSharp.SKImage.FromBitmap(sKBitmap2); 309 sKBitmap2.Dispose(); 310 var data = sKImage2.Encode(GetImageFormatByPath(path), quality); 311 sKImage2.Dispose(); 312 bytes = data.ToArray(); 313 data.Dispose(); 314 315 return bytes; 316 } 317 public static void ImageScalingByOversized(string path, string savePath, int maxWidth, int maxHeight, int quality) 318 { 319 var bytes = ImageScalingByOversized(path, maxWidth, maxHeight, quality); 320 if (bytes == null || bytes.Length < 1) 321 { 322 return; 323 } 324 string saveDirPath = System.IO.Path.GetDirectoryName(savePath); 325 if (!System.IO.Directory.Exists(saveDirPath)) 326 { 327 System.IO.Directory.CreateDirectory(saveDirPath); 328 } 329 System.IO.FileStream fs = new System.IO.FileStream(savePath, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write, System.IO.FileShare.None); 330 fs.Write(bytes, 0, bytes.Length); 331 fs.Close(); 332 } 333 public static byte[] ImageScalingByOversized(string path, int maxWidth, int maxHeight, int quality) 334 { 335 byte[] bytes = null; 336 if (!System.IO.File.Exists(path)) 337 { 338 return bytes; 339 } 340 var fileStream = new System.IO.FileStream(path, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read); 341 if (fileStream.Length > maxLength) 342 { 343 fileStream.Dispose(); 344 return bytes; 345 } 346 var sKManagedStream = new SkiaSharp.SKManagedStream(fileStream, true); 347 var sKBitmap = SkiaSharp.SKBitmap.Decode(sKManagedStream); 348 sKManagedStream.Dispose(); 349 350 if (sKBitmap.IsEmpty) 351 { 352 return bytes; 353 } 354 355 if (maxWidth < 1) { maxWidth = 1; } 356 if (maxHeight < 1) { maxHeight = 1; } 357 if (quality < 1) { quality = 1; } 358 if (quality > 100) { quality = 100; } 359 360 int oW = sKBitmap.Width; 361 int oH = sKBitmap.Height; 362 int nW = oW; 363 int nH = oH; 364 365 if (oW > maxWidth || oH > maxHeight) 366 { 367 nW = maxWidth; 368 nH = maxHeight; 369 double ratio = 1; 370 371 if (nW > 0 && nH > 0) 372 { 373 ratio = (double)nW / oW; 374 nH = Convert.ToInt32(oH * ratio); 375 if (maxHeight < nH) 376 { 377 ratio = (double)maxHeight / nH; 378 nW = Convert.ToInt32(nW * ratio); 379 nH = maxHeight; 380 } 381 } 382 if (nW < 1 && nH < 1) 383 { 384 nW = oW; 385 nH = oH; 386 } 387 if (nW < 1) 388 { 389 ratio = (double)nH / oH; 390 nW = Convert.ToInt32(oW * ratio); 391 } 392 if (nH < 1) 393 { 394 ratio = (double)nW / oW; 395 nH = Convert.ToInt32(oH * ratio); 396 } 397 var sKBitmap2 = new SkiaSharp.SKBitmap(nW, nH); 398 var sKCanvas = new SkiaSharp.SKCanvas(sKBitmap2); 399 var sKPaint = new SkiaSharp.SKPaint 400 { 401 FilterQuality = SkiaSharp.SKFilterQuality.Medium, 402 IsAntialias = true 403 }; 404 sKCanvas.DrawBitmap( 405 sKBitmap, 406 new SkiaSharp.SKRect 407 { 408 Location = new SkiaSharp.SKPoint { X = 0, Y = 0 }, 409 Size = new SkiaSharp.SKSize { Height = oH, Width = oW } 410 }, 411 new SkiaSharp.SKRect 412 { 413 Location = new SkiaSharp.SKPoint { X = 0, Y = 0 }, 414 Size = new SkiaSharp.SKSize { Height = nH, Width = nW } 415 }, sKPaint); 416 sKCanvas.Dispose(); 417 sKBitmap.Dispose(); 418 sKBitmap = sKBitmap2; 419 } 420 421 var sKImage = SkiaSharp.SKImage.FromBitmap(sKBitmap); 422 sKBitmap.Dispose(); 423 var data = sKImage.Encode(GetImageFormatByPath(path), quality); 424 sKImage.Dispose(); 425 bytes = data.ToArray(); 426 data.Dispose(); 427 428 return bytes; 429 } 430 431 /// <summary> 432 /// 生成二维码(320*320) 433 /// </summary> 434 /// <param name="text">文本内容</param> 435 /// <param name="savePath">保存路径</param> 436 /// <param name="logoPath">Logo图片路径(缩放到真实二维码区域尺寸的1/6)</param> 437 /// <param name="keepWhiteBorderPixelVal">白边处理(负值表示不做处理,最大值不超过真实二维码区域的1/10)</param> 438 public static void QRCoder(string text, string savePath, string logoPath = "", int keepWhiteBorderPixelVal = -1) 439 { 440 var format = GetImageFormatByPath(savePath); 441 byte[] bytesLogo = null; 442 if (!string.IsNullOrEmpty(logoPath) && System.IO.File.Exists(logoPath)) 443 { 444 var fsLogo = new System.IO.FileStream(logoPath, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read); 445 System.IO.MemoryStream ms = new System.IO.MemoryStream(); 446 fsLogo.CopyTo(ms); 447 fsLogo.Dispose(); 448 bytesLogo = ms.ToArray(); 449 ms.Dispose(); 450 } 451 452 var bytes = QRCoder(text, format, bytesLogo, keepWhiteBorderPixelVal); 453 454 if (bytes == null || bytes.Length < 1) 455 { 456 return; 457 } 458 459 var saveDirPath = System.IO.Path.GetDirectoryName(savePath); 460 if (!System.IO.Directory.Exists(saveDirPath)) 461 { 462 System.IO.Directory.CreateDirectory(saveDirPath); 463 } 464 var fs = new System.IO.FileStream(savePath, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write, System.IO.FileShare.None); 465 fs.Write(bytes, 0, bytes.Length); 466 fs.Close(); 467 } 468 /// <summary> 469 /// 生成二维码(320*320) 470 /// </summary> 471 /// <param name="text">文本内容</param> 472 /// <param name="format">保存格式</param> 473 /// <param name="logoImgae">Logo图片(缩放到真实二维码区域尺寸的1/6)</param> 474 /// <param name="keepWhiteBorderPixelVal">白边处理(负值表示不做处理,最大值不超过真实二维码区域的1/10)</param> 475 /// <returns></returns> 476 public static byte[] QRCoder(string text, SkiaSharp.SKEncodedImageFormat format, byte[] logoImgae = null, int keepWhiteBorderPixelVal = -1) 477 { 478 byte[] reval = null; 479 int width = 320; 480 int height = 320; 481 var qRCodeWriter = new ZXing.QrCode.QRCodeWriter(); 482 var hints = new Dictionary<ZXing.EncodeHintType, object>(); 483 hints.Add(ZXing.EncodeHintType.CHARACTER_SET, "utf-8"); 484 hints.Add(ZXing.EncodeHintType.QR_VERSION, 8); 485 hints.Add(ZXing.EncodeHintType.ERROR_CORRECTION, ZXing.QrCode.Internal.ErrorCorrectionLevel.Q); 486 var bitMatrix = qRCodeWriter.encode(text, ZXing.BarcodeFormat.QR_CODE, width, height, hints); 487 var w = bitMatrix.Width; 488 var h = bitMatrix.Height; 489 var sKBitmap = new SkiaSharp.SKBitmap(w, h); 490 491 int blackStartPointX = 0; 492 int blackStartPointY = 0; 493 int blackEndPointX = w; 494 int blackEndPointY = h; 495 496 #region --绘制二维码(同时获取真实的二维码区域起绘点和结束点的坐标)-- 497 var sKCanvas = new SkiaSharp.SKCanvas(sKBitmap); 498 var sKColorBlack = SkiaSharp.SKColor.Parse("000000"); 499 var sKColorWihte = SkiaSharp.SKColor.Parse("ffffff"); 500 sKCanvas.Clear(sKColorWihte); 501 bool blackStartPointIsNotWriteDown = true; 502 for (var y = 0; y < h; y++) 503 { 504 for (var x = 0; x < w; x++) 505 { 506 var flag = bitMatrix[x, y]; 507 if (flag) 508 { 509 if (blackStartPointIsNotWriteDown) 510 { 511 blackStartPointX = x; 512 blackStartPointY = y; 513 blackStartPointIsNotWriteDown = false; 514 } 515 blackEndPointX = x; 516 blackEndPointY = y; 517 sKCanvas.DrawPoint(x, y, sKColorBlack); 518 } 519 else 520 { 521 //sKCanvas.DrawPoint(x, y, sKColorWihte);//不用绘制(背景是白色的) 522 } 523 } 524 } 525 sKCanvas.Dispose(); 526 #endregion 527 528 int qrcodeRealWidth = blackEndPointX - blackStartPointX; 529 int qrcodeRealHeight = blackEndPointY - blackStartPointY; 530 531 #region -- 处理白边 -- 532 if (keepWhiteBorderPixelVal > -1)//指定了边框宽度 533 { 534 var borderMaxWidth = (int)Math.Floor((double)qrcodeRealWidth / 10); 535 if (keepWhiteBorderPixelVal > borderMaxWidth) 536 { 537 keepWhiteBorderPixelVal = borderMaxWidth; 538 } 539 var nQrcodeRealWidth = width - keepWhiteBorderPixelVal - keepWhiteBorderPixelVal; 540 var nQrcodeRealHeight = height - keepWhiteBorderPixelVal - keepWhiteBorderPixelVal; 541 542 var sKBitmap2 = new SkiaSharp.SKBitmap(width, height); 543 var sKCanvas2 = new SkiaSharp.SKCanvas(sKBitmap2); 544 sKCanvas2.Clear(sKColorWihte); 545 //二维码绘制到临时画布上时无需抗锯齿等处理(避免文件增大) 546 sKCanvas2.DrawBitmap( 547 sKBitmap, 548 new SkiaSharp.SKRect 549 { 550 Location = new SkiaSharp.SKPoint { X = blackStartPointX, Y = blackStartPointY }, 551 Size = new SkiaSharp.SKSize { Height = qrcodeRealHeight, Width = qrcodeRealWidth } 552 }, 553 new SkiaSharp.SKRect 554 { 555 Location = new SkiaSharp.SKPoint { X = keepWhiteBorderPixelVal, Y = keepWhiteBorderPixelVal }, 556 Size = new SkiaSharp.SKSize { Width = nQrcodeRealWidth, Height = nQrcodeRealHeight } 557 }); 558 559 blackStartPointX = keepWhiteBorderPixelVal; 560 blackStartPointY = keepWhiteBorderPixelVal; 561 qrcodeRealWidth = nQrcodeRealWidth; 562 qrcodeRealHeight = nQrcodeRealHeight; 563 564 sKCanvas2.Dispose(); 565 sKBitmap.Dispose(); 566 sKBitmap = sKBitmap2; 567 } 568 #endregion 569 570 #region -- 绘制LOGO -- 571 if (logoImgae != null && logoImgae.Length > 0) 572 { 573 SkiaSharp.SKBitmap sKBitmapLogo = SkiaSharp.SKBitmap.Decode(logoImgae); 574 if (!sKBitmapLogo.IsEmpty) 575 { 576 var sKPaint2 = new SkiaSharp.SKPaint 577 { 578 FilterQuality = SkiaSharp.SKFilterQuality.None, 579 IsAntialias = true 580 }; 581 var logoTargetMaxWidth = (int)Math.Floor((double)qrcodeRealWidth / 6); 582 var logoTargetMaxHeight = (int)Math.Floor((double)qrcodeRealHeight / 6); 583 var qrcodeCenterX = (int)Math.Floor((double)qrcodeRealWidth / 2); 584 var qrcodeCenterY = (int)Math.Floor((double)qrcodeRealHeight / 2); 585 var logoResultWidth = sKBitmapLogo.Width; 586 var logoResultHeight = sKBitmapLogo.Height; 587 if (logoResultWidth > logoTargetMaxWidth) 588 { 589 var r = (double)logoTargetMaxWidth / logoResultWidth; 590 logoResultWidth = logoTargetMaxWidth; 591 logoResultHeight = (int)Math.Floor(logoResultHeight * r); 592 } 593 if (logoResultHeight > logoTargetMaxHeight) 594 { 595 var r = (double)logoTargetMaxHeight / logoResultHeight; 596 logoResultHeight = logoTargetMaxHeight; 597 logoResultWidth = (int)Math.Floor(logoResultWidth * r); 598 } 599 var pointX = qrcodeCenterX - (int)Math.Floor((double)logoResultWidth / 2) + blackStartPointX; 600 var pointY = qrcodeCenterY - (int)Math.Floor((double)logoResultHeight / 2) + blackStartPointY; 601 602 var sKCanvas3 = new SkiaSharp.SKCanvas(sKBitmap); 603 var sKPaint = new SkiaSharp.SKPaint 604 { 605 FilterQuality = SkiaSharp.SKFilterQuality.Medium, 606 IsAntialias = true 607 }; 608 sKCanvas3.DrawBitmap( 609 sKBitmapLogo, 610 new SkiaSharp.SKRect 611 { 612 Location = new SkiaSharp.SKPoint { X = 0, Y = 0 }, 613 Size = new SkiaSharp.SKSize { Height = sKBitmapLogo.Height, Width = sKBitmapLogo.Width } 614 }, 615 new SkiaSharp.SKRect 616 { 617 Location = new SkiaSharp.SKPoint { X = pointX, Y = pointY }, 618 Size = new SkiaSharp.SKSize { Height = logoResultHeight, Width = logoResultWidth } 619 }, sKPaint); 620 sKCanvas3.Dispose(); 621 sKPaint.Dispose(); 622 sKBitmapLogo.Dispose(); 623 } 624 else 625 { 626 sKBitmapLogo.Dispose(); 627 } 628 } 629 #endregion 630 631 SkiaSharp.SKImage sKImage = SkiaSharp.SKImage.FromBitmap(sKBitmap); 632 sKBitmap.Dispose(); 633 var data = sKImage.Encode(format, 75); 634 sKImage.Dispose(); 635 reval = data.ToArray(); 636 data.Dispose(); 637 638 return reval; 639 } 640 public static string QRDecoder(string qrCodeFilePath) 641 { 642 if (!System.IO.File.Exists(qrCodeFilePath)) 643 { 644 throw new Exception("文件不存在"); 645 } 646 647 System.IO.FileStream fileStream = new System.IO.FileStream(qrCodeFilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read); 648 if (fileStream.Length > maxLength) 649 { 650 fileStream.Dispose(); 651 throw new Exception("图片文件太大"); 652 } 653 return QRDecoder(fileStream); 654 } 655 public static string QRDecoder(byte[] qrCodeBytes) 656 { 657 if(qrCodeBytes==null|| qrCodeBytes.Length<1) 658 { 659 throw new Exception("参数qrCodeBytes不存在"); 660 } 661 if (qrCodeBytes.Length > maxLength) 662 { 663 throw new Exception("图片文件太大"); 664 } 665 System.IO.MemoryStream ms = new System.IO.MemoryStream(qrCodeBytes); 666 return QRDecoder(ms); 667 } 668 public static string QRDecoder(System.IO.Stream qrCodeFileStream) 669 { 670 var sKManagedStream = new SkiaSharp.SKManagedStream(qrCodeFileStream, true); 671 var sKBitmap = SkiaSharp.SKBitmap.Decode(sKManagedStream); 672 sKManagedStream.Dispose(); 673 if (sKBitmap.IsEmpty) 674 { 675 sKBitmap.Dispose(); 676 throw new Exception("未识别的图片文件"); 677 } 678 679 var w = sKBitmap.Width; 680 var h = sKBitmap.Height; 681 int ps = w * h; 682 byte[] bytes = new byte[ps * 3]; 683 int byteIndex = 0; 684 for (var x = 0; x < w; x++) 685 { 686 for (var y = 0; y < h; y++) 687 { 688 var color = sKBitmap.GetPixel(x, y); 689 bytes[byteIndex + 0] = color.Red; 690 bytes[byteIndex + 1] = color.Green; 691 bytes[byteIndex + 2] = color.Blue; 692 byteIndex += 3; 693 } 694 } 695 sKBitmap.Dispose(); 696 697 var qRCodeReader = new ZXing.QrCode.QRCodeReader(); 698 var rGBLuminanceSource = new ZXing.RGBLuminanceSource(bytes, w, h); 699 var hybridBinarizer = new ZXing.Common.HybridBinarizer(rGBLuminanceSource); 700 var binaryBitmap = new ZXing.BinaryBitmap(hybridBinarizer); 701 var hints = new Dictionary<ZXing.DecodeHintType, object>(); 702 hints.Add(ZXing.DecodeHintType.CHARACTER_SET, "utf-8"); 703 var result = qRCodeReader.decode(binaryBitmap, hints); 704 705 return result != null ? result.Text : ""; 706 } 707 }View Code
开个控制台程序测试一下,测试代码:
1 static void Main(string[] args) 2 { 3 var path = @"d:\a\文字设计.png";//size=595*842 4 ImageHelper.ImageMaxCutByCenter(path, @"d:\a\文字设计002.png", 1024, 768, 75);//size=1024*768 5 ImageHelper.ImageMaxCutByCenter(path, @"d:\a\文字设计003.png", 768, 1024, 75);//size=768*1024 6 ImageHelper.ImageScalingToRange(path, @"d:\a\文字设计004.png", 1024, 768, 75);//size=542*768 7 ImageHelper.ImageScalingToRange(path, @"d:\a\文字设计005.png", 768, 1024, 75);//size=724*1024 8 ImageHelper.ImageScalingByOversized(path, @"d:\a\文字设计006.png", 640, 320, 75);//size=226*320 9 ImageHelper.ImageScalingByOversized(path, @"d:\a\文字设计007.png", 320, 640, 75);//size=320*453 10 11 var qrcodeSavePath = @"d:\a\hello.png"; 12 var qrcodeLogoPath = @"d:\a\logo.png"; 13 var qrcodeWhiteBorderPixelVal = 5; 14 var qrcodeText = "高堂明镜悲白发,朝如青丝暮成雪!"; 15 ImageHelper.QRCoder(qrcodeText, qrcodeSavePath, qrcodeLogoPath, qrcodeWhiteBorderPixelVal); 16 var result = ImageHelper.QRDecoder(qrcodeSavePath); 17 Console.WriteLine(result); 18 19 var imageInfo = ImageHelper.GetImageInfo(qrcodeLogoPath); 20 Console.WriteLine($"WIDTH={imageInfo.Item1}&HEIGHT={imageInfo.Item2}&LENGTH={imageInfo.Item3}&FORMAT={imageInfo.Item4}"); 21 }View Code
程序运行结果:
图片处理结果:
最后的图片质量还是挺好的,如果你也有在 .net core 下的图片处理需求,可以试试这个处理方式!
以后会陆续将自己写的一些小东西发出来和大家一起分享,多半都是一些零零碎碎的东西,目的在于解决实实在在的具体的问题,希望对各位来过的朋友有所帮助!