如果你正在找Node.js的学习资料及指南,那么请继续(阅读),我们的教程将会覆盖即时聊天应用、API服务编写、投票问卷应用、人物投票APP、社交授权、 Node.js on Raspberry Pi等等。 以下是Node.js入门的简单介绍,如果你对Node.js略有了解可以直接跳过此部分。 那什么是Node.js呢? Node.js是迄今运用最多的服务端JavaScript运行时环境,使用JavaScript开发跨平台的实时WEB应用。 Node.js基于Google的V8 JavaScript引擎。基于事件驱动,非阻塞的输入输出模型,这也使得其高效而轻量。 Node.js的强大之处在于其能力支持数据密级性的交互应用,因为这类应用的函数围绕着用户事件,数据I/O和数据流的处理。对于JSON的API,Node.js同样显得很神奇,它是单页WEB应用的心脏和灵魂。 Node.js的流行程度可以感觉得到,像eBay、target、City Group、Sony、Uber、LinkedIn、Medium、Netflix等等的大公司都有在他们的web项目中应用。 怎样学习Node.js呢? Node.js的开发者来自全球,他们用Node.js开发复杂交互的,数据密集型的WEB Apps和网站。同时网上有着大量的资源和教程教你怎样构建自己的Node.js应用。 我们不防看看Node.js大神针对Web Apps写的Node.js最佳指南。 我们所覆盖的指南将包括针对初学者的Node.js、中级指南,同时还有教你怎样构建复杂的Node.js项目。 对于完全的Node.js初学者, 在深入挖掘本文章后续提到的学习项目之前,一步步按照airpair.com上面入门指南的要点 或Node.js入门指南 之类的文章学习是不错的选择。Nodeschool.io是另一个比较好的网站,上面有着非常多的资料,可以学习基本的node.js及相关的技术。 通过Express框架和Socket.io构建即时通讯APP 将一个新技术学到一定高度的最好方式就是通过功能性的应用了。在这个指南中,你将学习使用Express框架和Socket.io构建即时通讯应用,这些技术可以使用基于事件的双向通信。在这个指南中,你还将使用JavaScript模板引擎Jade。 我们要介绍的指南由Krasimir Tsonev所撰写,发布在tutsplus.com上面,从node.js的环境搭建,使用Express框架的后端服务,到最后使用Jade模板引擎的前端UI开发,覆盖了所有前端到后端的方方面面。 你可以在后面这个链接中找到这篇指南: 即时通讯(Node.js Tutorial – Real Time Chat) . 使用Swagger创建Restful API 这个精心设计的教程解释了如何使用Swagger来创建Rest API,并且包括了Rest API应该支持的所有操作(CRUD,创建,查询,更新,删除)。这个例子是由来自scotch.io的Samuela Zara提供的,它实现了管理电影集合的Rest API。 Swagger是一个非常强大的框架来描述REST APIs,它提供了交互式的API文档编辑,客户端sdk生成以及发现服务。通过使用Swagger,你所书写的API将更容易被大家所理解并且使用。 在这个教程中,Samuela一开始先教会大家如何安装node.js的Swagger模块,然后介绍了Swagger提供的模拟模式。通过这个模式,API开发者可以先集中精神进行API本身的设计,而不用写一行的代码。当这个API结构被设计完成之后,教程开始带领大家具体实现获取电影,增加/修改电影以及删除电影的操作。 你可以通过Restful API in Node.js using Swagger这个链接来访问该教程。 在 Node.js 中使用 Nodal 建立 API Services 在大多数模块和框架之上,你就不会需要再建立任何简陋的 JavaScript/Node.js 项目。为了多平台(移动端,web,IOT)产品开发,面向服务的架构是关键的,而本教程就将带你更进一步。 在本教程中,Keith Horwood 利用 Nodal 框架在 Node.js 中来创建 API services,并让这一切看起来极其简单。Nodal 是一个可扩展的,以意见为依据的全服务框架,它帮助数据中心加快在 Node.js 基础上的API创建。 在本教程中,你将会在开发 MVC (模型,控制器和视图)之前安装 node.js 环境,安装 Nodal 和 postgreSQL。 你可以在这里找到本教程: 在 Node.js 中使用 Nodal 建立 API Services. Node.js Apps – 使用通行证进行社交认证 社交认证几乎随处可见,因为我们绝大多数人在Facebook […]
View Details如下是运行微信支付测试代码时出错代码: Warning: curl_setopt() expects parameter 2 to be long, string given in D:\wwwroot\weixinpaytest\pay\WxPay.JsApiPay.php on line 99 Fatal error: Uncaught exception ‘WxPayException‘ with message ‘curl出错,错误码:60‘ in D:\wwwroot\weixinpaytest\lib\WxPay.Api.php:564 Stack trace: #0 D:\wwwroot\weixinpaytest\lib\WxPay.Api.php(62): WxPayApi::postXmlCurl(‘<xml><appid><![…‘, ‘https://api.mch…‘, false, 6) #1 D:\wwwroot\weixinpaytest\pay\jsapi.php(36): WxPayApi::unifiedOrder(Object(WxPayUnifiedOrder)) #2 {main} thrown in D:\wwwroot\weixinpaytest\lib\WxPay.Api.php on line 564 第一个问题,这个问题完全是微信团队的问题,给出的example就是错的: Warning: curl_setopt() expects parameter 2 to be long, string given in D:\wwwroot\weixinpaytest\pay\WxPay.JsApiPay.php on line 99 找到WxPay.JsApiPay.php文件的99行,curl_setopt($ch, CURLOP_TIMEOUT, 30); 微信团队example代码里少了一个“T”,正确代码应该是 curl_setopt($ch, CURLOPT_TIMEOUT, 30); 这样,这一个问题就解决了。 下面说第二个问题: Fatal error: Uncaught exception ‘WxPayException‘ with message ‘curl出错,错误码:60‘ in […]
View Details本教程示例代码见:https://github.com/johnlui/Learn-Laravel-5 在任何地方卡住,最快的办法就是去看示例代码。 本文基于 Laravel 5.2 版本,无奈 5.2 的中文文档还没有跟上,大家勉强看一下 5.1 的吧: Laravel 5.1 中文文档: http://www.golaravel.com/laravel/docs/5.1/ http://laravel-china.org/docs/5.1 默认条件 你应该懂得 PHP 网站运行的基础知识,并且有了一个完善的开发环境。跟随本教程走完一遍,你将会得到一个基础的包含登录、后台编辑、前台评论的简单 blog 系统。 Tips 环境要求:PHP 5.5.9+,MySQL 5.1+ 本教程不推荐完全不懂 PHP 与 MVC 编程的人学习,Laravel 的学习曲线不仅仅是陡峭,而且耗时很长,请先做好心理准备。 这不是 “一步一步跟我做” 教程。本教程需要你付出一定的心智去解决一些或大或小的隐藏任务,以达到真正理解 Laravel 运行逻辑的目的。 本宝宝使用 Safari 截图是为了好看,宝宝们在开发时请选择 Chrome 哦~ 开始学习 1. 安装 许多人被拦在了学习 Laravel 的第一步:安装。并不是因为安装有多复杂,而是因为【众所周知的原因】。在此我推荐一个 composer 全量中国镜像:http://pkg.phpcomposer.com/ 。启用 Composer 镜像服务作为本教程的第一项小作业请自行完成哦。 镜像配置完成后,在终端(Terminal 或 CMD)里切换到你想要放置该网站的目录下(如 C:\wwwroot、/Library/WebServer/Documents/、/var/www/html、/etc/nginx/html 等),运行命令:
1 2 |
composer create-project laravel/laravel learnlaravel5 |
然后,稍等片刻,当前目录下就会出现一个叫 learnlaravel5 的文件夹,安装完成啦~ 2. 运行 为了尽可能地减缓学习曲线,推荐宝宝们使用 PHP 内置 web 服务器驱动我们的网站。运行以下命令:
1 2 3 |
<span class="hljs-built_in">cd</span> public php -S 0.0.0.0:1024 |
我在本地 hosts 中绑定了 fuck.io 到 127.0.0.1(如果你不会或者不想改 hosts,可以直接使用 127.0.0.1 代理我的 fuck.io),这时候访问 http://fuck.io:1024 就是这个样子的: 这时候你可能要问了:为什么本宝宝的页面是一片空白?请使用开发者工具查看网络请求,只要是 200 状态就说明运行成功了,空白是因为这个页面引用了 Google Fonts,你懂的~ 至于为什么选择 1024 […]
View DetailsDrawImage 时按照目标形状给定矩形框大小即可,下面是MSDN上的代码: Bitmap myBitmap = new Bitmap("Spiral.png"); Rectangle expansionRectangle = new Rectangle(135, 10, myBitmap.Width, myBitmap.Height); Rectangle compressionRectangle = new Rectangle(300, 10, myBitmap.Width / 2, myBitmap.Height / 2); myGraphics.DrawImage(myBitmap, 10, 10); myGraphics.DrawImage(myBitmap, expansionRectangle); myGraphics.DrawImage(myBitmap, compressionRectangle); 以上图形转换的代码如下,经测试通过,参考了CxImage的源码: static int ComputePixel(float x, float y, out float x1, out float y1) { double r, nn; if (x == 0 && y == 0) { x1 = x; y1 = y; return 1; } nn = Math.Sqrt(x * x + y * y); r = (Math.Abs(x) > Math.Abs(y)) […]
View Details该类是生成一个验证码的类,集合了网上大部分的C#关于GDI+的文章进行多次改进,现在已经形成了可在生产环节中使用的验证码。 该验证码加入了背景噪点,背景噪点曲线和直线,背景噪点文字以及扭曲,调暗,模糊等。完全可以实现防识别。 对安全性要求比较高的网站尤其适用。 同时该类还还收集了GDI+的图像处理方面的函数,包括雾化,扭曲,水波,锐化,高斯模糊,画直线,画曲线生成随机颜色,缩放图片,柔化图片,图片黑白化,增加曝光度,RGB滤镜,绘制圆角等功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 |
using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Text; using System.Drawing.Imaging; /********************** * 验证码生成类 * 作者:李飞麟 * URL:http://www.xuehuwang.com * Email:lifei6671@163.com * * *********************/ namespace NS.DrawValidationCode { #region 验证码生成类 /// <summary> /// 验证码生成类 /// </summary> public class DrawValidationCode { #region 定义和初始化配置字段 //用户存取验证码字符串 private string validationCode = String.Empty; /// <summary> /// 获取系统生成的随机验证码 /// </summary> public String ValidationCode { get { return validationCode; } } private Int32 validationCodeCount = 4; /// <summary> /// 获取和设置验证码字符串的长度 /// </summary> public Int32 ValidationCodeCount { get { return validationCodeCount; } set { validationCodeCount = value; } } Graphics dc = null; private int bgWidth = 130; /// <summary> /// 验证码的宽度,默认为80 /// </summary> public Int32 Width { get { return bgWidth; } set { bgWidth = value; } } private int bgHeight = 40; /// <summary> /// 验证码的高度,默认为40 /// </summary> public Int32 Height { get { return bgHeight; } set { bgHeight = value; } } /* private string[] fontFace = { "Verdana", "Microsoft Sans Serif", "Comic Sans MS", "Arial", "宋体" }; /// <summary> /// 验证码字体列表,默认为{ "Verdana", "Microsoft Sans Serif", "Comic Sans MS", "Arial", "宋体" } /// </summary> public String[] FontFace { get { return fontFace; } set { fontFace = value; } }*/ private int fontMinSize = 15; /// <summary> /// 验证码字体的最小值,默认为15,建议不小于15像素 /// </summary> public Int32 FontMinSize { get { return fontMinSize; } set { fontMinSize = value; } } private Int32 fontMaxSize = 20; /// <summary> /// 验证码字体的最大值,默认为20 /// </summary> public Int32 FontMaxSize { get { return fontMaxSize; } set { fontMaxSize = value; } } private Color[] fontColor = { }; /// <summary> /// 验证码字体的颜色,默认为系统自动生成字体颜色 /// </summary> public Color[] FontColor { get { return fontColor; } set { fontColor = value; } } private Color backColor = Color.FromArgb(243, 255, 255); /// <summary> /// 验证码的背景色,默认为Color.FromArgb(243, 251, 254) /// </summary> public Color BackgroundColor { get { return backColor; } set { backColor = value; } } private Int32 bezierCount = 3; /// <summary> /// 贝塞尔曲线的条数,默认为3条 /// </summary> public Int32 BezierCount { get { return bezierCount; } set { bezierCount = value; } } private Int32 lineCount = 3; /// <summary> /// 直线条数,默认为3条 /// </summary> public Int32 LineCount { get { return lineCount; } set { lineCount = value; } } Random random; private String charCollection = "2,3,4,5,6,7,8,9,a,s,d,f,g,h,z,c,v,b,n,m,k,q,w,e,r,t,y,u,p,A,S,D,F,G,H,Z,C,V,B,N,M,K,Q,W,E,R,T,Y,U,P"; //定义验证码字符及出现频次 ,避免出现0 o j i l 1 x; /// <summary> /// 随机字符串列表,请使用英文状态下的逗号分隔。 /// </summary> public String CharCollection { get { return charCollection; } set { charCollection = value; } } private Int32 intCount = 4; /// <summary> /// 验证码字符串个数,默认为4个字符 /// </summary> public Int32 IntCount { get { return intCount; } set { intCount = value; } } private Boolean isPixel = true; /// <summary> /// 是否添加噪点,默认添加,噪点颜色为系统随机生成。 /// </summary> public Boolean IsPixel { get { return isPixel; } set { isPixel = value; } } private Boolean isRandString = true; /// <summary> /// 是否添加随机噪点字符串,默认添加 /// </summary> public Boolean IsRandString { get { return isRandString; } set { isRandString = value; } } /// <summary> /// 随机背景字符串的个数 /// </summary> public Int32 RandomStringCount { get; set; } private Int32 randomStringFontSize = 9; /// <summary> /// 随机背景字符串的大小 /// </summary> public Int32 RandomStringFontSize { get { return randomStringFontSize; } set { randomStringFontSize = value; } } /// <summary> /// 是否对图片进行扭曲 /// </summary> public Boolean IsTwist { get; set; } /// <summary> /// 边框样式 /// </summary> public enum BorderStyle { /// <summary> /// 无边框 /// </summary> None, /// <summary> /// 矩形边框 /// </summary> Rectangle, /// <summary> /// 圆角边框 /// </summary> RoundRectangle } private Int32 rotationAngle = 40; /// <summary> /// 验证码字符串随机转动的角度的最大值 /// </summary> public Int32 RotationAngle { get { return rotationAngle; } set { rotationAngle = value; } } /// <summary> /// 设置或获取边框样式 /// </summary> public BorderStyle Border { get; set; } private Point[] strPoint = null; private Double gaussianDeviation = 0; /// <summary> /// 对验证码图片进行高斯模糊的阀值,如果设置为0,则不对图片进行高斯模糊,该设置可能会对图片处理的性能有较大影响 /// </summary> public Double GaussianDeviation { get { return gaussianDeviation; } set { gaussianDeviation = value; } } private Int32 brightnessValue = 0; /// <summary> /// 对图片进行暗度和亮度的调整,如果该值为0,则不调整。该设置会对图片处理性能有较大影响 /// </summary> public Int32 BrightnessValue { get { return brightnessValue; } set { brightnessValue = value; } } #endregion /// <summary> /// 构造函数,用于初始化常用变量 /// </summary> public DrawValidationCode() { random = new Random(Guid.NewGuid().GetHashCode()); strPoint = new Point[validationCodeCount + 1]; if (gaussianDeviation < 0) gaussianDeviation = 0; } /// <summary> /// 生成验证码 /// </summary> /// <param name="target">用于存储图片的一般字节序列</param> public void CreateImage(Stream target) { Bitmap bit = new Bitmap(bgWidth + 1, bgHeight + 1); //写字符串 dc = Graphics.FromImage(bit); dc.SmoothingMode = SmoothingMode.HighQuality; dc.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; ; dc.InterpolationMode = InterpolationMode.HighQualityBilinear; dc.CompositingQuality = CompositingQuality.HighQuality; dc.Clear(Color.White); dc.DrawImageUnscaled(DrawBackground(), 0, 0); dc.DrawImageUnscaled(DrawRandomString(), 0, 0); //对图片文字进行扭曲 bit = AdjustRippleEffect(bit, 5); //对图片进行高斯模糊 if (gaussianDeviation > 0) { Gaussian gau = new Gaussian(); bit = gau.FilterProcessImage(gaussianDeviation, bit); } //进行暗度和亮度处理 if (brightnessValue != 0) { //对图片进行调暗处理 bit = AdjustBrightness(bit, brightnessValue); } bit.Save(target, ImageFormat.Gif); //brush.Dispose(); bit.Dispose(); dc.Dispose(); } #region 画验证码背景,例如,增加早点,添加曲线和直线等 /// <summary> /// 画验证码背景,例如,增加早点,添加曲线和直线等 /// </summary> /// <returns></returns> private Bitmap DrawBackground() { Bitmap bit = new Bitmap(bgWidth + 1, bgHeight + 1); Graphics g = Graphics.FromImage(bit); g.SmoothingMode = SmoothingMode.HighQuality; g.Clear(Color.White); Rectangle rectangle = new Rectangle(0, 0, bgWidth, bgHeight); Brush brush = new SolidBrush(backColor); g.FillRectangle(brush, rectangle); //画噪点 if (isPixel) { g.DrawImageUnscaled(DrawRandomPixel(30), 0, 0); } g.DrawImageUnscaled(DrawRandBgString(), 0, 0); //画曲线 g.DrawImageUnscaled(DrawRandomBezier(bezierCount), 0, 0); //画直线 g.DrawImageUnscaled(DrawRandomLine(lineCount), 0, 0); //dc.DrawImageUnscaled(DrawStringline(), 0, 0); if (Border == BorderStyle.Rectangle) { //绘制边框 g.DrawRectangle(new Pen(Color.FromArgb(90, 87, 46)), 0, 0, bgWidth, bgHeight); } else if (Border == BorderStyle.RoundRectangle) { //画圆角 DrawRoundRectangle(g, rectangle, Color.FromArgb(90, 87, 46), 1, 3); } return bit; } #endregion #region 画正弦曲线 private Bitmap DrawTwist(Bitmap bmp, Int32 tWidth, Int32 tHeight, float angle, Color color) { //为了方便查看效果,在这里我定义了一个常量。 //它在定义数组的长度和for循环中都要用到。 int size = bgWidth; double[] x = new double[size]; Bitmap b = new Bitmap(bmp.Width, bmp.Height); b.MakeTransparent(); Graphics graphics = Graphics.FromImage(b); Pen pen = new Pen(color); //画正弦曲线的横轴间距参数。建议所用的值应该是 正数且是2的倍数。 //在这里采用2。 int val = 2; float temp = 0.0f; //把画布下移100。为什么要这样做,只要你把这一句给注释掉,运行一下代码, //你就会明白是为什么? graphics.TranslateTransform(0, 100); graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; for (int i = 0; i < size; i++) { //改变tWidth,实现正弦曲线宽度的变化。 //改tHeight,实现正弦曲线高度的变化。 x[i] = Math.Sin(2 * Math.PI * i / tWidth) * tHeight; graphics.DrawLine(pen, i * val, temp, i * val + val / 2, (float)x[i]); temp = (float)x[i]; } graphics.RotateTransform(60, MatrixOrder.Prepend); //旋转图片 // b = KiRotate(b, angle, Color.Transparent); return b; } #endregion #region 正弦曲线Wave扭曲图片 /// <summary> /// 正弦曲线Wave扭曲图片 /// </summary> /// <param name="srcBmp">图片路径</param> /// <param name="bXDir">如果扭曲则选择为True</param> /// <param name="dMultValue">波形的幅度倍数,越大扭曲的程度越高,一般为3</param> /// <param name="dPhase">波形的起始相位,取值区间[0-2*PI)</param> /// <returns></returns> public Bitmap TwistImage(Bitmap srcBmp, bool bXDir, double dMultValue, double dPhase) { System.Drawing.Bitmap destBmp = new Bitmap(srcBmp.Width, srcBmp.Height); double PI2 = 6.283185307179586476925286766559; // 将位图背景填充为白色 System.Drawing.Graphics graph = System.Drawing.Graphics.FromImage(destBmp); graph.FillRectangle(new SolidBrush(System.Drawing.Color.White), 0, 0, destBmp.Width, destBmp.Height); graph.Dispose(); double dBaseAxisLen = bXDir ? (double)destBmp.Height : (double)destBmp.Width; for (int i = 0; i < destBmp.Width; i++) { for (int j = 0; j < destBmp.Height; j++) { double dx = 0; dx = bXDir ? (PI2 * (double)j) / dBaseAxisLen : (PI2 * (double)i) / dBaseAxisLen; dx += dPhase; double dy = Math.Sin(dx); // 取得当前点的颜色 int nOldX = 0, nOldY = 0; nOldX = bXDir ? i + (int)(dy * dMultValue) : i; nOldY = bXDir ? j : j + (int)(dy * dMultValue); System.Drawing.Color color = srcBmp.GetPixel(i, j); if (nOldX >= 0 && nOldX < destBmp.Width && nOldY >= 0 && nOldY < destBmp.Height) { destBmp.SetPixel(nOldX, nOldY, color); } } } return destBmp; } #endregion #region 图片任意角度旋转 /// <summary> /// 图片任意角度旋转 /// </summary> /// <param name="bmp">原始图Bitmap</param> /// <param name="angle">旋转角度</param> /// <param name="bkColor">背景色</param> /// <returns>输出Bitmap</returns> public static Bitmap KiRotate(Bitmap bmp, float angle, Color bkColor) { int w = bmp.Width; int h = bmp.Height; PixelFormat pf; if (bkColor == Color.Transparent) { pf = PixelFormat.Format32bppArgb; } else { pf = bmp.PixelFormat; } Bitmap tmp = new Bitmap(w, h, pf); Graphics g = Graphics.FromImage(tmp); g.Clear(bkColor); g.DrawImageUnscaled(bmp, 1, 1); g.Dispose(); GraphicsPath path = new GraphicsPath(); path.AddRectangle(new RectangleF(0f, 0f, w, h)); Matrix mtrx = new Matrix(); mtrx.Rotate(angle); RectangleF rct = path.GetBounds(mtrx); Bitmap dst = new Bitmap((int)rct.Width, (int)rct.Height, pf); g = Graphics.FromImage(dst); g.Clear(bkColor); g.TranslateTransform(-rct.X, -rct.Y); g.RotateTransform(angle); g.InterpolationMode = InterpolationMode.HighQualityBilinear; g.DrawImageUnscaled(tmp, 0, 0); g.Dispose(); tmp.Dispose(); return dst; } #endregion #region 随机生成贝塞尔曲线 /// <summary> /// 随机生成贝塞尔曲线 /// </summary> /// <param name="bmp">一个图片的实例</param> /// <param name="lineNum">线条数量</param> /// <returns></returns> public Bitmap DrawRandomBezier(Int32 lineNum) { Bitmap b = new Bitmap(bgWidth, bgHeight); b.MakeTransparent(); Graphics g = Graphics.FromImage(b); g.Clear(Color.Transparent); g.SmoothingMode = SmoothingMode.HighQuality; g.PixelOffsetMode = PixelOffsetMode.HighQuality; GraphicsPath gPath1 = new GraphicsPath(); Int32 lineRandNum = random.Next(lineNum); for (int i = 0; i < (lineNum - lineRandNum); i++) { Pen p = new Pen(GetRandomDeepColor()); Point[] point = { new Point(random.Next(1, (b.Width / 10)), random.Next(1, (b.Height))), new Point(random.Next((b.Width / 10) * 2, (b.Width / 10) * 4), random.Next(1, (b.Height))), new Point(random.Next((b.Width / 10) * 4, (b.Width / 10) * 6), random.Next(1, (b.Height))), new Point(random.Next((b.Width / 10) * 8, b.Width), random.Next(1, (b.Height))) }; gPath1.AddBeziers(point); g.DrawPath(p, gPath1); p.Dispose(); } for (int i = 0; i < lineRandNum; i++) { Pen p = new Pen(GetRandomDeepColor()); Point[] point = { new Point(random.Next(1, b.Width), random.Next(1, b.Height)), new Point(random.Next((b.Width / 10) * 2, b.Width), random.Next(1, b.Height)), new Point(random.Next((b.Width / 10) * 4, b.Width), random.Next(1, b.Height)), new Point(random.Next(1, b.Width), random.Next(1, b.Height)) }; gPath1.AddBeziers(point); g.DrawPath(p, gPath1); p.Dispose(); } return b; } #endregion #region 画直线 /// <summary> /// 画直线 /// </summary> /// <param name="bmp">一个bmp实例</param> /// <param name="lineNum">线条个数</param> /// <returns></returns> public Bitmap DrawRandomLine(Int32 lineNum) { if (lineNum < 0) throw new ArgumentNullException("参数bmp为空!"); Bitmap b = new Bitmap(bgWidth, bgHeight); b.MakeTransparent(); Graphics g = Graphics.FromImage(b); g.Clear(Color.Transparent); g.PixelOffsetMode = PixelOffsetMode.HighQuality; g.SmoothingMode = SmoothingMode.HighQuality; for (int i = 0; i < lineNum; i++) { Pen p = new Pen(GetRandomDeepColor()); Point pt1 = new Point(random.Next(1, (b.Width / 5) * 2), random.Next(b.Height)); Point pt2 = new Point(random.Next((b.Width / 5) * 3, b.Width), random.Next(b.Height)); g.DrawLine(p, pt1, pt2); p.Dispose(); } return b; } #endregion #region 画随机噪点 /// <summary> /// 画随机噪点 /// </summary> /// <param name="pixNum">噪点的百分比</param> /// <returns></returns> public Bitmap DrawRandomPixel(Int32 pixNum) { Bitmap b = new Bitmap(bgWidth, bgHeight); b.MakeTransparent(); Graphics graph = Graphics.FromImage(b); graph.SmoothingMode = SmoothingMode.HighQuality; graph.InterpolationMode = InterpolationMode.HighQualityBilinear; //画噪点 for (int i = 0; i < (bgHeight * bgWidth) / pixNum; i++) { int x = random.Next(b.Width); int y = random.Next(b.Height); b.SetPixel(x, y, GetRandomDeepColor()); //下移坐标重新画点 if ((x + 1) < b.Width && (y + 1) < b.Height) { //画图片的前景噪音点 graph.DrawRectangle(new Pen(Color.Silver), random.Next(b.Width), random.Next(b.Height), 1, 1); } } return b; } #endregion #region 画随机字符串中间连线 /// <summary> /// 画随机字符串中间连线 /// </summary> /// <returns></returns> private Bitmap DrawStringline() { Bitmap b = new Bitmap(bgWidth, bgHeight); b.MakeTransparent(); Graphics g = Graphics.FromImage(b); g.SmoothingMode = SmoothingMode.AntiAlias; Point[] p = new Point[validationCodeCount]; for (int i = 0; i < validationCodeCount; i++) { p[i] = strPoint[i]; //throw new Exception(strPoint.Length.ToString()); } // g.DrawBezier(new Pen(GetRandomDeepColor()), strPoint); //g.DrawClosedCurve(new Pen(GetRandomDeepColor()), strPoint); g.DrawCurve(new Pen(GetRandomDeepColor(), 1), strPoint); return b; } #endregion #region 写入验证码的字符串 /// <summary> /// 写入验证码的字符串 /// </summary> private Bitmap DrawRandomString() { if (fontMaxSize >= (bgHeight / 5) * 4) throw new ArgumentException("字体最大值参数FontMaxSize与验证码高度相近,这会导致描绘验证码字符串时出错,请重新设置参数!"); Bitmap b = new Bitmap(bgWidth, bgHeight); b.MakeTransparent(); Graphics g = Graphics.FromImage(b); g.Clear(Color.Transparent); g.PixelOffsetMode = PixelOffsetMode.Half; g.SmoothingMode = SmoothingMode.HighQuality; g.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit; g.InterpolationMode = InterpolationMode.HighQualityBilinear; char[] chars = GetRandomString(validationCodeCount).ToCharArray();//拆散字符串成单字符数组 validationCode = chars.ToString(); //设置字体显示格式 StringFormat format = new StringFormat(StringFormatFlags.NoClip); format.Alignment = StringAlignment.Center; format.LineAlignment = StringAlignment.Center; FontFamily f = new FontFamily(GenericFontFamilies.Monospace); Int32 charNum = chars.Length; Point sPoint = new Point(); Int32 fontSize = 9; for (int i = 0; i < validationCodeCount; i++) { int findex = random.Next(5); //定义字体 Font textFont = new Font(f, random.Next(fontMinSize, fontMaxSize), FontStyle.Bold); //定义画刷,用于写字符串 //Brush brush = new SolidBrush(GetRandomDeepColor()); Int32 textFontSize = Convert.ToInt32(textFont.Size); fontSize = textFontSize; Point point = new Point(random.Next((bgWidth / charNum) * i + 5, (bgWidth / charNum) * (i + 1)), random.Next(bgHeight / 5 + textFontSize / 2, bgHeight - textFontSize / 2)); //如果当前字符X坐标小于字体的二分之一大小 if (point.X < textFontSize / 2) { point.X = point.X + textFontSize / 2; } //防止文字叠加 if (i > 0 && (point.X - sPoint.X < (textFontSize / 2 + textFontSize / 2))) { point.X = point.X + textFontSize; } //如果当前字符X坐标大于图片宽度,就减去字体的宽度 if (point.X > (bgWidth - textFontSize / 2)) { point.X = bgWidth - textFontSize / 2; } sPoint = point; float angle = random.Next(-rotationAngle, rotationAngle);//转动的度数 g.TranslateTransform(point.X, point.Y);//移动光标到指定位置 g.RotateTransform(angle); //设置渐变画刷 Rectangle myretang = new Rectangle(0, 1, Convert.ToInt32(textFont.Size), Convert.ToInt32(textFont.Size)); Color c = GetRandomDeepColor(); LinearGradientBrush mybrush2 = new LinearGradientBrush(myretang, c, GetLightColor(c, 120), random.Next(180)); g.DrawString(chars[i].ToString(), textFont, mybrush2, 1, 1, format); g.RotateTransform(-angle);//转回去 g.TranslateTransform(-point.X, -point.Y);//移动光标到指定位置,每个字符紧凑显示,避免被软件识别 strPoint[i] = point; textFont.Dispose(); mybrush2.Dispose(); } return b; } #endregion #region 画干扰背景文字 /// <summary> /// 画背景干扰文字 /// </summary> /// <returns></returns> private Bitmap DrawRandBgString() { Bitmap b = new Bitmap(bgWidth, bgHeight); String[] randStr = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" }; b.MakeTransparent(); Graphics g = Graphics.FromImage(b); g.Clear(Color.Transparent); g.PixelOffsetMode = PixelOffsetMode.HighQuality; g.SmoothingMode = SmoothingMode.HighQuality; g.TextRenderingHint = TextRenderingHint.AntiAlias; g.InterpolationMode = InterpolationMode.HighQualityBilinear; //设置字体显示格式 StringFormat format = new StringFormat(StringFormatFlags.NoClip); format.Alignment = StringAlignment.Center; format.LineAlignment = StringAlignment.Center; FontFamily f = new FontFamily(GenericFontFamilies.Serif); Font textFont = new Font(f, randomStringFontSize, FontStyle.Underline); int randAngle = 60; //随机转动角度 for (int i = 0; i < RandomStringCount; i++) { Brush brush = new System.Drawing.SolidBrush(GetRandomLightColor()); Point pot = new Point(random.Next(5, bgWidth - 5), random.Next(5, bgHeight - 5)); //随机转动的度数 float angle = random.Next(-randAngle, randAngle); //转动画布 g.RotateTransform(angle); g.DrawString(randStr[random.Next(randStr.Length)], textFont, brush, pot, format); //转回去,为下一个字符做准备 g.RotateTransform(-angle); //释放资源 brush.Dispose(); } textFont.Dispose(); format.Dispose(); f.Dispose(); return b; } #endregion #region 生成随机字符串 /// <summary> /// 生成随机字符串 /// </summary> /// <returns></returns> private string GetRandomString(Int32 textLength) { string[] randomArray = charCollection.Split(','); //将字符串生成数组 int arrayLength = randomArray.Length; string randomString = ""; for (int i = 0; i < textLength; i++) { randomString += randomArray[random.Next(0, arrayLength)]; } return randomString; //长度是textLength +1 } #endregion #region 内部方法:绘制验证码背景 private void DrawBackground(HatchStyle hatchStyle) { //设置填充背景时用的笔刷 HatchBrush hBrush = new HatchBrush(hatchStyle, backColor); //填充背景图片 dc.FillRectangle(hBrush, 0, 0, this.bgWidth, this.bgHeight); } #endregion #region 根据指定长度,返回随机验证码 /// <summary> /// 根据指定长度,返回随机验证码 /// </summary> /// <param >制定长度</param> /// <returns>随即验证码</returns> public string Next(int length) { this.validationCode = GetRandomCode(length); return this.validationCode; } #endregion #region 内部方法:返回指定长度的随机验证码字符串 /// <summary> /// 根据指定大小返回随机验证码 /// </summary> /// <param >字符串长度</param> /// <returns>随机字符串</returns> private string GetRandomCode(int length) { StringBuilder sb = new StringBuilder(6); for (int i = 0; i < length; i++) { sb.Append(Char.ConvertFromUtf32(RandomAZ09())); } return sb.ToString(); } #endregion #region 内部方法:产生随机数和随机点 /// <summary> /// 产生0-9A-Z的随机字符代码 /// </summary> /// <returns>字符代码</returns> private int RandomAZ09() { int result = 48; Random ram = new Random(); int i = ram.Next(2); switch (i) { case 0: result = ram.Next(48, 58); break; case 1: result = ram.Next(65, 91); break; } return result; } /// <summary> /// 返回一个随机点,该随机点范围在验证码背景大小范围内 /// </summary> /// <returns>Point对象</returns> private Point RandomPoint() { Random ram = new Random(); Point point = new Point(ram.Next(this.bgWidth), ram.Next(this.bgHeight)); return point; } #endregion #region 随机生成颜色值 /// <summary> /// 生成随机深颜色 /// </summary> /// <returns></returns> public Color GetRandomDeepColor() { int nRed, nGreen, nBlue; // nBlue,nRed nGreen 相差大一点 nGreen 小一些 //int high = 255; int redLow = 160; int greenLow = 100; int blueLow = 160; nRed = random.Next(redLow); nGreen = random.Next(greenLow); nBlue = random.Next(blueLow); Color color = Color.FromArgb(nRed, nGreen, nBlue); return color; } /// <summary> /// 生成随机浅颜色 /// </summary> /// <returns>randomColor</returns> public Color GetRandomLightColor() { int nRed, nGreen, nBlue; //越大颜色越浅 int low = 180; //色彩的下限 int high = 255; //色彩的上限 nRed = random.Next(high) % (high - low) + low; nGreen = random.Next(high) % (high - low) + low; nBlue = random.Next(high) % (high - low) + low; Color color = Color.FromArgb(nRed, nGreen, nBlue); return color; } /// <summary> /// 生成随机颜色值 /// </summary> /// <returns></returns> public Color GetRandomColor() { int nRed, nGreen, nBlue; //越大颜色越浅 int low = 10; //色彩的下限 int high = 255; //色彩的上限 nRed = random.Next(high) % (high - low) + low; nGreen = random.Next(high) % (high - low) + low; nBlue = random.Next(high) % (high - low) + low; Color color = Color.FromArgb(nRed, nGreen, nBlue); return color; } /// <summary> /// 获取与当前颜色值相加后的颜色 /// </summary> /// <param name="c"></param> /// <returns></returns> public Color GetLightColor(Color c, Int32 value) { int nRed = c.R, nGreen = c.G, nBlue = c.B; //越大颜色越浅 if (nRed + value < 255 && nRed + value > 0) { nRed = c.R + 40; } if (nGreen + value < 255 && nGreen + value > 0) { nGreen = c.G + 40; } if (nBlue + value < 255 && nBlue + value > 0) { nBlue = c.B + 40; } Color color = Color.FromArgb(nRed, nGreen, nBlue); return color; } #endregion #region 合并图片 /// <summary> /// 合并图片 /// </summary> /// <param name="maps"></param> /// <returns></returns> private Bitmap MergerImg(params Bitmap[] maps) { int i = maps.Length; if (i == 0) throw new Exception("图片数不能够为0"); //创建要显示的图片对象,根据参数的个数设置宽度 Bitmap backgroudImg = new Bitmap(i * 12, 16); Graphics g = Graphics.FromImage(backgroudImg); //清除画布,背景设置为白色 g.Clear(System.Drawing.Color.White); for (int j = 0; j < i; j++) { //g.DrawImage(maps[j], j * 11, 0, maps[j].Width, maps[j].Height); g.DrawImageUnscaled(maps[j], 0, 0); } g.Dispose(); return backgroudImg; } #endregion #region 生成不重复的随机数,该函数会消耗大量系统资源 /// <summary> /// 生成不重复的随机数,该函数会消耗大量系统资源 /// </summary> /// <returns></returns> private static int GetRandomSeed() { byte[] bytes = new byte[4]; System.Security.Cryptography.RNGCryptoServiceProvider rng = new System.Security.Cryptography.RNGCryptoServiceProvider(); rng.GetBytes(bytes); return BitConverter.ToInt32(bytes, 0); } #endregion #region 缩放图片 /// <summary> /// 缩放图片 /// </summary> /// <param name="bmp">原始Bitmap</param> /// <param name="newW">新的宽度</param> /// <param name="newH">新的高度</param> /// <param name="Mode">缩放质量</param> /// <returns>处理以后的图片</returns> public static Bitmap KiResizeImage(Bitmap bmp, int newW, int newH, InterpolationMode Mode) { try { Bitmap b = new Bitmap(newW, newH); Graphics g = Graphics.FromImage(b); // 插值算法的质量 g.InterpolationMode = Mode; g.DrawImage(bmp, new Rectangle(0, 0, newW, newH), new Rectangle(0, 0, bmp.Width, bmp.Height), GraphicsUnit.Pixel); g.Dispose(); return b; } catch { return null; } } #endregion #region 绘制圆角矩形 /// <summary> /// C# GDI+ 绘制圆角矩形 /// </summary> /// <param name="g">Graphics 对象</param> /// <param name="rectangle">Rectangle 对象,圆角矩形区域</param> /// <param name="borderColor">边框颜色</param> /// <param name="borderWidth">边框宽度</param> /// <param name="r">圆角半径</param> private static void DrawRoundRectangle(Graphics g, Rectangle rectangle, Color borderColor, float borderWidth, int r) { // 如要使边缘平滑,请取消下行的注释 g.SmoothingMode = SmoothingMode.HighQuality; // 由于边框也需要一定宽度,需要对矩形进行修正 //rectangle = new Rectangle(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); Pen p = new Pen(borderColor, borderWidth); // 调用 getRoundRectangle 得到圆角矩形的路径,然后再进行绘制 g.DrawPath(p, getRoundRectangle(rectangle, r)); } #endregion #region 根据普通矩形得到圆角矩形的路径 /// <summary> /// 根据普通矩形得到圆角矩形的路径 /// </summary> /// <param name="rectangle">原始矩形</param> /// <param name="r">半径</param> /// <returns>图形路径</returns> private static GraphicsPath getRoundRectangle(Rectangle rectangle, int r) { int l = 2 * r; // 把圆角矩形分成八段直线、弧的组合,依次加到路径中 GraphicsPath gp = new GraphicsPath(); gp.AddLine(new Point(rectangle.X + r, rectangle.Y), new Point(rectangle.Right - r, rectangle.Y)); gp.AddArc(new Rectangle(rectangle.Right - l, rectangle.Y, l, l), 270F, 90F); gp.AddLine(new Point(rectangle.Right, rectangle.Y + r), new Point(rectangle.Right, rectangle.Bottom - r)); gp.AddArc(new Rectangle(rectangle.Right - l, rectangle.Bottom - l, l, l), 0F, 90F); gp.AddLine(new Point(rectangle.Right - r, rectangle.Bottom), new Point(rectangle.X + r, rectangle.Bottom)); gp.AddArc(new Rectangle(rectangle.X, rectangle.Bottom - l, l, l), 90F, 90F); gp.AddLine(new Point(rectangle.X, rectangle.Bottom - r), new Point(rectangle.X, rectangle.Y + r)); gp.AddArc(new Rectangle(rectangle.X, rectangle.Y, l, l), 180F, 90F); return gp; } #endregion #region 柔化 ///<summary> /// 柔化 /// </summary> /// <param name="b">原始图</param> /// <returns>输出图</returns> public static Bitmap KiBlur(Bitmap b) { if (b == null) { return null; } int w = b.Width; int h = b.Height; try { Bitmap bmpRtn = new Bitmap(w, h, PixelFormat.Format24bppRgb); BitmapData srcData = b.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); BitmapData dstData = bmpRtn.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb); unsafe { byte* pIn = (byte*)srcData.Scan0.ToPointer(); byte* pOut = (byte*)dstData.Scan0.ToPointer(); int stride = srcData.Stride; byte* p; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { //取周围9点的值 if (x == 0 || x == w - 1 || y == 0 || y == h - 1) { //不做 pOut[0] = pIn[0]; pOut[1] = pIn[1]; pOut[2] = pIn[2]; } else { int r1, r2, r3, r4, r5, r6, r7, r8, r9; int g1, g2, g3, g4, g5, g6, g7, g8, g9; int b1, b2, b3, b4, b5, b6, b7, b8, b9; float vR, vG, vB; //左上 p = pIn - stride - 3; r1 = p[2]; g1 = p[1]; b1 = p[0]; //正上 p = pIn - stride; r2 = p[2]; g2 = p[1]; b2 = p[0]; //右上 p = pIn - stride + 3; r3 = p[2]; g3 = p[1]; b3 = p[0]; //左侧 p = pIn - 3; r4 = p[2]; g4 = p[1]; b4 = p[0]; //右侧 p = pIn + 3; r5 = p[2]; g5 = p[1]; b5 = p[0]; //右下 p = pIn + stride - 3; r6 = p[2]; g6 = p[1]; b6 = p[0]; //正下 p = pIn + stride; r7 = p[2]; g7 = p[1]; b7 = p[0]; //右下 p = pIn + stride + 3; r8 = p[2]; g8 = p[1]; b8 = p[0]; //自己 p = pIn; r9 = p[2]; g9 = p[1]; b9 = p[0]; vR = (float)(r1 + r2 + r3 + r4 + r5 + r6 + r7 + r8 + r9); vG = (float)(g1 + g2 + g3 + g4 + g5 + g6 + g7 + g8 + g9); vB = (float)(b1 + b2 + b3 + b4 + b5 + b6 + b7 + b8 + b9); vR /= 9; vG /= 9; vB /= 9; pOut[0] = (byte)vB; pOut[1] = (byte)vG; pOut[2] = (byte)vR; } pIn += 3; pOut += 3; }// end of x pIn += srcData.Stride - w * 3; pOut += srcData.Stride - w * 3; } // end of y } b.UnlockBits(srcData); bmpRtn.UnlockBits(dstData); return bmpRtn; } catch { return null; } } // end of KiBlur #endregion #region 滤镜 /// <summary> /// 红色滤镜 /// </summary> /// <param name="bitmap">Bitmap</param> /// <param name="threshold">阀值 -255~255</param> /// <returns></returns> public System.Drawing.Bitmap AdjustToRed(System.Drawing.Bitmap bitmap, int threshold) { for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { // 取得每一個 pixel var pixel = bitmap.GetPixel(x, y); var pR = pixel.R + threshold; pR = Math.Max(pR, 0); pR = Math.Min(255, pR); // 將改過的 RGB 寫回 // 只寫入紅色的值 , G B 都放零 System.Drawing.Color newColor = System.Drawing.Color.FromArgb(pixel.A, pR, 0, 0); bitmap.SetPixel(x, y, newColor); } } // 回傳結果 return bitmap; } /// <summary> /// 绿色滤镜 /// </summary> /// <param name="bitmap">一个图片实例</param> /// <param name="threshold">阀值 -255~+255</param> /// <returns></returns> public System.Drawing.Bitmap AdjustToGreen(System.Drawing.Bitmap bitmap, int threshold) { for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { // 取得每一個 pixel var pixel = bitmap.GetPixel(x, y); //判斷是否超過255 如果超過就是255 var pG = pixel.G + threshold; //如果小於0就為0 if (pG > 255) pG = 255; if (pG < 0) pG = 0; // 將改過的 RGB 寫回 // 只寫入綠色的值 , R B 都放零 System.Drawing.Color newColor = System.Drawing.Color.FromArgb(pixel.A, 0, pG, 0); bitmap.SetPixel(x, y, newColor); } } // 回傳結果 return bitmap; } /// <summary> /// 蓝色滤镜 /// </summary> /// <param name="bitmap">一个图片实例</param> /// <param name="threshold">阀值 -255~255</param> /// <returns></returns> public System.Drawing.Bitmap AdjustToBlue(System.Drawing.Bitmap bitmap, int threshold) { for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { // 取得每一個 pixel var pixel = bitmap.GetPixel(x, y); //判斷是否超過255 如果超過就是255 var pB = pixel.B + threshold; //如果小於0就為0 if (pB > 255) pB = 255; if (pB < 0) pB = 0; // 將改過的 RGB 寫回 // 只寫入藍色的值 , R G 都放零 System.Drawing.Color newColor = System.Drawing.Color.FromArgb(pixel.A, 0, 0, pB); bitmap.SetPixel(x, y, newColor); } } // 回傳結果 return bitmap; } /// <summary> /// 调整 RGB 色调 /// </summary> /// <param name="bitmap"></param> /// <param name="thresholdRed">红色阀值</param> /// <param name="thresholdBlue">蓝色阀值</param> /// <param name="thresholdGreen">绿色阀值</param> /// <returns></returns> public System.Drawing.Bitmap AdjustToCustomColor(System.Drawing.Bitmap bitmap, int thresholdRed, int thresholdGreen, int thresholdBlue) { for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { // 取得每一個 pixel var pixel = bitmap.GetPixel(x, y); //判斷是否超過255 如果超過就是255 var pG = pixel.G + thresholdGreen; //如果小於0就為0 if (pG > 255) pG = 255; if (pG < 0) pG = 0; //判斷是否超過255 如果超過就是255 var pR = pixel.R + thresholdRed; //如果小於0就為0 if (pR > 255) pR = 255; if (pR < 0) pR = 0; //判斷是否超過255 如果超過就是255 var pB = pixel.B + thresholdBlue; //如果小於0就為0 if (pB > 255) pB = 255; if (pB < 0) pB = 0; // 將改過的 RGB 寫回 // 只寫入綠色的值 , R B 都放零 System.Drawing.Color newColor = System.Drawing.Color.FromArgb(pixel.A, pR, pG, pB); bitmap.SetPixel(x, y, newColor); } } return bitmap; } #endregion #region 图片去色(图片黑白化) /// <summary> /// 图片去色(图片黑白化) /// </summary> /// <param name="original">一个需要处理的图片</param> /// <returns></returns> public static Bitmap MakeGrayscale(Bitmap original) { //create a blank bitmap the same size as original Bitmap newBitmap = new Bitmap(original.Width, original.Height); //get a graphics object from the new image Graphics g = Graphics.FromImage(newBitmap); g.SmoothingMode = SmoothingMode.HighQuality; //create the grayscale ColorMatrix ColorMatrix colorMatrix = new ColorMatrix(new float[][] { new float[] {.3f, .3f, .3f, 0, 0}, new float[] {.59f, .59f, .59f, 0, 0}, new float[] {.11f, .11f, .11f, 0, 0}, new float[] {0, 0, 0, 1, 0}, new float[] {0, 0, 0, 0, 1} }); //create some image attributes ImageAttributes attributes = new ImageAttributes(); //set the color matrix attribute attributes.SetColorMatrix(colorMatrix); //draw the original image on the new image //using the grayscale color matrix g.DrawImage(original, new Rectangle(0, 0, original.Width, original.Height), 0, 0, original.Width, original.Height, GraphicsUnit.Pixel, attributes); //dispose the Graphics object g.Dispose(); return newBitmap; } #endregion #region 增加或減少亮度 /// <summary> /// 增加或減少亮度 /// </summary> /// <param name="img">System.Drawing.Image Source </param> /// <param name="valBrightness">0~255</param> /// <returns></returns> public System.Drawing.Bitmap AdjustBrightness(System.Drawing.Image img, int valBrightness) { // 讀入欲轉換的圖片並轉成為 Bitmap System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(img); for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { // 取得每一個 pixel var pixel = bitmap.GetPixel(x, y); // 判斷 如果處理過後 255 就設定為 255 如果小於則設定為 0 var pR = ((pixel.R + valBrightness > 255) ? 255 : pixel.R + valBrightness) < 0 ? 0 : ((pixel.R + valBrightness > 255) ? 255 : pixel.R + valBrightness); var pG = ((pixel.G + valBrightness > 255) ? 255 : pixel.G + valBrightness) < 0 ? 0 : ((pixel.G + valBrightness > 255) ? 255 : pixel.G + valBrightness); var pB = ((pixel.B + valBrightness > 255) ? 255 : pixel.B + valBrightness) < 0 ? 0 : ((pixel.B + valBrightness > 255) ? 255 : pixel.B + valBrightness); // 將改過的 RGB 寫回 System.Drawing.Color newColor = System.Drawing.Color.FromArgb(pixel.A, pR, pG, pB); bitmap.SetPixel(x, y, newColor); } } // 回傳結果 return bitmap; } #endregion #region 浮雕效果 /// <summary> /// 浮雕效果 /// </summary> /// <param name="src">一个图片实例</param> /// <returns></returns> public Bitmap AdjustToStone(Bitmap src) { // 依照 Format24bppRgb 每三个表示一 Pixel 0: 蓝 1: 绿 2: 红 BitmapData bitmapData = src.LockBits(new Rectangle(0, 0, src.Width, src.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); unsafe { // 抓住第一个 Pixel 第一个数值 byte* p = (byte*)(void*)bitmapData.Scan0; // 跨步值 - 宽度 *3 可以算出畸零地 之后跳到下一行 int nOffset = bitmapData.Stride - src.Width * 3; for (int y = 0; y < src.Height; ++y) { for (int x = 0; x < src.Width; ++x) { // 为了理解方便 所以特地在命名 int r, g, b; // 先取得下一个 Pixel var q = p + 3; r = Math.Abs(p[2] - q[2] + 128); r = r < 0 ? 0 : r; r = r > 255 ? 255 : r; p[2] = (byte)r; g = Math.Abs(p[1] - q[1] + 128); g = g < 0 ? 0 : g; g = g > 255 ? 255 : g; p[1] = (byte)g; b = Math.Abs(p[0] - q[0] + 128); b = b < 0 ? 0 : b; b = b > 255 ? 255 : b; p[0] = (byte)b; // 跳去下一个 Pixel p += 3; } // 跨越畸零地 p += nOffset; } } src.UnlockBits(bitmapData); return src; } #endregion #region 水波纹效果 /// <summary> /// 水波纹效果 /// </summary> /// <param name="src"></param> /// <param name="nWave">坡度</param> /// www.it165.net /// <returns></returns> public Bitmap AdjustRippleEffect(Bitmap src, short nWave) { int nWidth = src.Width; int nHeight = src.Height; // 透过公式进行水波纹的採样 PointF[,] fp = new PointF[nWidth, nHeight]; Point[,] pt = new Point[nWidth, nHeight]; Point mid = new Point(); mid.X = nWidth / 2; mid.Y = nHeight / 2; double newX, newY; double xo, yo; //先取样将水波纹座标跟RGB取出 for (int x = 0; x < nWidth; ++x) for (int y = 0; y < nHeight; ++y) { xo = ((double)nWave * Math.Sin(2.0 * 3.1415 * (float)y / 128.0)); yo = ((double)nWave * Math.Cos(2.0 * 3.1415 * (float)x / 128.0)); newX = (x + xo); newY = (y + yo); if (newX > 0 && newX < nWidth) { fp[x, y].X = (float)newX; pt[x, y].X = (int)newX; } else { fp[x, y].X = (float)0.0; pt[x, y].X = 0; } if (newY > 0 && newY < nHeight) { fp[x, y].Y = (float)newY; pt[x, y].Y = (int)newY; } else { fp[x, y].Y = (float)0.0; pt[x, y].Y = 0; } } //进行合成 Bitmap bSrc = (Bitmap)src.Clone(); // 依照 Format24bppRgb 每三个表示一 Pixel 0: 蓝 1: 绿 2: 红 BitmapData bitmapData = src.LockBits(new Rectangle(0, 0, src.Width, src.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); BitmapData bmSrc = bSrc.LockBits(new Rectangle(0, 0, bSrc.Width, bSrc.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int scanline = bitmapData.Stride; IntPtr Scan0 = bitmapData.Scan0; IntPtr SrcScan0 = bmSrc.Scan0; unsafe { byte* p = (byte*)(void*)Scan0; byte* pSrc = (byte*)(void*)SrcScan0; int nOffset = bitmapData.Stride - src.Width * 3; int xOffset, yOffset; for (int y = 0; y < nHeight; ++y) { for (int x = 0; x < nWidth; ++x) { xOffset = pt[x, y].X; yOffset = pt[x, y].Y; if (yOffset >= 0 && yOffset < nHeight && xOffset >= 0 && xOffset < nWidth) { p[0] = pSrc[(yOffset * scanline) + (xOffset * 3)]; p[1] = pSrc[(yOffset * scanline) + (xOffset * 3) + 1]; p[2] = pSrc[(yOffset * scanline) + (xOffset * 3) + 2]; } p += 3; } p += nOffset; } } src.UnlockBits(bitmapData); bSrc.UnlockBits(bmSrc); return src; } #endregion #region 调整曝光度值 /// <summary> /// 调整曝光度值 /// </summary> /// <param name="src">原图</param> /// <param name="r"></param> /// <param name="g"></param> /// <param name="b"></param> /// <returns></returns> public Bitmap AdjustGamma(Bitmap src, double r, double g, double b) { // 判断是不是在0.2~5 之间 r = Math.Min(Math.Max(0.2, r), 5); g = Math.Min(Math.Max(0.2, g), 5); b = Math.Min(Math.Max(0.2, b), 5); // 依照 Format24bppRgb 每三个表示一 Pixel 0: 蓝 1: 绿 2: 红 BitmapData bitmapData = src.LockBits(new Rectangle(0, 0, src.Width, src.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); unsafe { // 抓住第一个 Pixel 第一个数值 byte* p = (byte*)(void*)bitmapData.Scan0; // 跨步值 - 宽度 *3 可以算出畸零地 之后跳到下一行 int nOffset = bitmapData.Stride - src.Width * 3; for (int y = 0; y < src.Height; y++) { for (int x = 0; x < src.Width; x++) { p[2] = (byte)Math.Min(255, (int)((255.0 * Math.Pow(p[2] / 255.0, 1.0 / r)) + 0.5)); p[1] = (byte)Math.Min(255, (int)((255.0 * Math.Pow(p[1] / 255.0, 1.0 / g)) + 0.5)); p[0] = (byte)Math.Min(255, (int)((255.0 * Math.Pow(p[0] / 255.0, 1.0 / b)) + 0.5)); // 跳去下一个 Pixel p += 3; } // 跨越畸零地 p += nOffset; } } src.UnlockBits(bitmapData); return src; } #endregion #region 高对比,对过深的颜色调浅,过浅的颜色调深。 /// <summary> /// 高对比,对过深的颜色调浅,过浅的颜色调深。 /// </summary> /// <param name="src"></param> /// <param name="effectThreshold"> 高对比程度 -100~100</param> /// <returns></returns> public Bitmap Contrast(Bitmap src, float effectThreshold) { // 依照 Format24bppRgb 每三个表示一 Pixel 0: 蓝 1: 绿 2: 红 BitmapData bitmapData = src.LockBits(new Rectangle(0, 0, src.Width, src.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); // 判断是否在 -100~100 effectThreshold = effectThreshold < -100 ? -100 : effectThreshold; effectThreshold = effectThreshold > 100 ? 100 : effectThreshold; effectThreshold = (float)((100.0 + effectThreshold) / 100.0); effectThreshold *= effectThreshold; unsafe { // 抓住第一个 Pixel 第一个数值 www.it165.net byte* p = (byte*)(void*)bitmapData.Scan0; // 跨步值 - 宽度 *3 可以算出畸零地 之后跳到下一行 int nOffset = bitmapData.Stride - src.Width * 3; for (int y = 0; y < src.Height; y++) { for (int x = 0; x < src.Width; x++) { double buffer = 0; // 公式 (Red/255)-0.5= 偏离中间值程度 // ((偏离中间值程度 * 影响范围)+0.4 ) * 255 buffer = ((((p[2] / 255.0) - 0.5) * effectThreshold) + 0.5) * 255.0; buffer = buffer > 255 ? 255 : buffer; buffer = buffer < 0 ? 0 : buffer; p[2] = (byte)buffer; buffer = ((((p[1] / 255.0) - 0.5) * effectThreshold) + 0.5) * 255.0; buffer = buffer > 255 ? 255 : buffer; buffer = buffer < 0 ? 0 : buffer; p[1] = (byte)buffer; buffer = ((((p[0] / 255.0) - 0.5) * effectThreshold) + 0.5) * 255.0; buffer = buffer > 255 ? 255 : buffer; buffer = buffer < 0 ? 0 : buffer; p[0] = (byte)buffer; // 跳去下一个 Pixel p += 3; } // 跨越畸零地 p += nOffset; } } src.UnlockBits(bitmapData); return src; } #endregion #region 对图片进行雾化效果 /// <summary> /// 对图片进行雾化效果 /// </summary> /// <param name="bmp"></param> /// <returns></returns> public Bitmap Atomization(Bitmap bmp) { int Height = bmp.Height; int Width = bmp.Width; Bitmap newBitmap = new Bitmap(Width, Height); Bitmap oldBitmap = bmp; Color pixel; for (int x = 1; x < Width - 1; x++) { for (int y = 1; y < Height - 1; y++) { Random MyRandom = new Random( Guid.NewGuid().GetHashCode()); int k = MyRandom.Next(123456); //像素块大小 int dx = x + k % 19; int dy = y + k % 19; if (dx >= Width) dx = Width - 1; if (dy >= Height) dy = Height - 1; pixel = oldBitmap.GetPixel(dx, dy); newBitmap.SetPixel(x, y, pixel); } } return newBitmap; } #endregion } //END Class DrawValidationCode #endregion #region 高斯模糊算法 /// <summary> /// 高斯模糊算法 /// </summary> public class Gaussian { public static double[,] Calculate1DSampleKernel(double deviation, int size) { double[,] ret = new double[size, 1]; double sum = 0; int half = size / 2; for (int i = 0; i < size; i++) { ret[i, 0] = 1 / (Math.Sqrt(2 * Math.PI) * deviation) * Math.Exp(-(i - half) * (i - half) / (2 * deviation * deviation)); sum += ret[i, 0]; } return ret; } public static double[,] Calculate1DSampleKernel(double deviation) { int size = (int)Math.Ceiling(deviation * 3) * 2 + 1; return Calculate1DSampleKernel(deviation, size); } public static double[,] CalculateNormalized1DSampleKernel(double deviation) { return NormalizeMatrix(Calculate1DSampleKernel(deviation)); } public static double[,] NormalizeMatrix(double[,] matrix) { double[,] ret = new double[matrix.GetLength(0), matrix.GetLength(1)]; double sum = 0; for (int i = 0; i < ret.GetLength(0); i++) { for (int j = 0; j < ret.GetLength(1); j++) sum += matrix[i, j]; } if (sum != 0) { for (int i = 0; i < ret.GetLength(0); i++) { for (int j = 0; j < ret.GetLength(1); j++) ret[i, j] = matrix[i, j] / sum; } } return ret; } public static double[,] GaussianConvolution(double[,] matrix, double deviation) { double[,] kernel = CalculateNormalized1DSampleKernel(deviation); double[,] res1 = new double[matrix.GetLength(0), matrix.GetLength(1)]; double[,] res2 = new double[matrix.GetLength(0), matrix.GetLength(1)]; //x-direction for (int i = 0; i < matrix.GetLength(0); i++) { for (int j = 0; j < matrix.GetLength(1); j++) res1[i, j] = processPoint(matrix, i, j, kernel, 0); } //y-direction for (int i = 0; i < matrix.GetLength(0); i++) { for (int j = 0; j < matrix.GetLength(1); j++) res2[i, j] = processPoint(res1, i, j, kernel, 1); } return res2; } private static double processPoint(double[,] matrix, int x, int y, double[,] kernel, int direction) { double res = 0; int half = kernel.GetLength(0) / 2; for (int i = 0; i < kernel.GetLength(0); i++) { int cox = direction == 0 ? x + i - half : x; int coy = direction == 1 ? y + i - half : y; if (cox >= 0 && cox < matrix.GetLength(0) && coy >= 0 && coy < matrix.GetLength(1)) { res += matrix[cox, coy] * kernel[i, 0]; } } return res; } /// <summary> /// 对颜色值进行灰色处理 /// </summary> /// <param name="cr"></param> /// <returns></returns> private Color grayscale(Color cr) { return Color.FromArgb(cr.A, (int)(cr.R * .3 + cr.G * .59 + cr.B * 0.11), (int)(cr.R * .3 + cr.G * .59 + cr.B * 0.11), (int)(cr.R * .3 + cr.G * .59 + cr.B * 0.11)); } /// <summary> /// 对图片进行高斯模糊 /// </summary> /// <param name="d">模糊数值,数值越大模糊越很</param> /// <param name="image">一个需要处理的图片</param> /// <returns></returns> public Bitmap FilterProcessImage(double d, Bitmap image) { Bitmap ret = new Bitmap(image.Width, image.Height); Double[,] matrixR = new Double[image.Width, image.Height]; Double[,] matrixG = new Double[image.Width, image.Height]; Double[,] matrixB = new Double[image.Width, image.Height]; for (int i = 0; i < image.Width; i++) { for (int j = 0; j < image.Height; j++) { //matrix[i, j] = grayscale(image.GetPixel(i, j)).R; matrixR[i, j] = image.GetPixel(i, j).R; matrixG[i, j] = image.GetPixel(i, j).G; matrixB[i, j] = image.GetPixel(i, j).B; } } matrixR = Gaussian.GaussianConvolution(matrixR, d); matrixG = Gaussian.GaussianConvolution(matrixG, d); matrixB = Gaussian.GaussianConvolution(matrixB, d); for (int i = 0; i < image.Width; i++) { for (int j = 0; j < image.Height; j++) { Int32 R = (int)Math.Min(255, matrixR[i, j]); Int32 G = (int)Math.Min(255, matrixG[i, j]); Int32 B = (int)Math.Min(255, matrixB[i, j]); ret.SetPixel(i, j, Color.FromArgb(R, G, B)); } } return ret; } } #endregion } |
from:http://www.cnblogs.com/Fooo/p/3576800.html
View Details
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
理解绘图规则 一般来说,Windows的一个优点(实际上是现代操作系统的优点)是它可以让开发人员不考虑特定设备的细节。例如:不需要理解硬盘设备驱动程序,只需在相关的.NET类中调用合适的方法,就可以编程读写磁盘上的文件。这个规则也适用于绘图。计算机在屏幕上绘图时,把指令发送给视频卡。问题是市面上有几百种不同的视频卡,大多数有不同的指令集合功能。如果把这个i考虑在内,在应用程序中为每个视频卡驱动程序编写在屏幕上绘图的特定代码,这样的应用程序就根本不可能编写出来。这就是为什么在Windows最早的版本中有Windows Graphical Device Interface(GDI)的原因。 GDI+提供了一个抽象层,隐藏了不同视频卡之间的区别,这样就可以调用Windows API函数完成指定的任务了,GDI还在内部指出在运行特定的代码时,如果让客户机的视频卡完成要绘制的图形。GDI还可以完成其他任务。大多数计算机都有多个显示设备---监视器、打印机。GDI成功的使应用程序所使用的打印机看起来与屏幕一样。如果要打印某些东西,而不是显示他们,只需告诉系统输出设备是打印机,再用相同的方式调用相同的Windows API函数可以。 可以看出DC(设备环境)是一个功能非常强大的对象,在GDI下,所有的绘图工作都必须通过设备环境完成。DC甚至可用于不涉及在屏幕或其他硬件设备上绘图的其他操作,例如在内存中修改图像。 GDI开发人员提供了一个相当高级的API,但它仍是一个基于旧Windows API并且有C语言风格函数的API,所以使用起来不是很方便。GDI+在很大程度上是GDI和应用程序之间的一层,提供了更直观、基于继承性的对象模型。尽管GDI+基本上是GDI的一个包装器,但Microsoft已经能通过GDI+提供新的功能了并宣称他又一些性能方面的改进。 1.GDI+命名空间 (不说了,自己看去吧!!!) 2.设备环境和Graphics对象 GDI使用设备环境(DC)对像识别输出设备。DC对象存储特定设备的信息并把GDI API函数调用转换为要发送给设备的命令。还可以通过DC对象确定对应的设备有什么功能(如打印机是彩色还是黑白的)。如果要求设备完成它不能完成的任务,设备对象就会检测到并采取措施。 DC对象不仅可以硬件还可以用作到Windows的一个桥梁。例如如果Windows知道只有一小部分应用程序窗口需要重新绘制,DC就可以捕获和撤销在该地区外的绘图工作。因为DC与Windows的关系非常密切,通过Dc来工作就可以用其他方式简化代码。 绘制图形 下面举例来说明如何在应用程序的主窗口中绘图。DisplayAtStartup 创建一个C# 应用程序并在启动窗体时在构造函数中绘制它。这并不是在屏幕上绘图的最佳方式,这个示例并不能在启动后按照需要重新绘制窗体。这样只是不必作太多的工作就可以说明一些问题。 首先把窗体的背景色设置为白色。如果使用设计视图设置背景色,系统会自动添加代码: private void InitializeComponent() { this.AutoScaleBaseSize = new System.Drawing.Size(5,13); this.BackColor = System.Drawing.Color.White; this.ClientSize = new System.Drawing.Size(292,266); this.Name = "Form1"; this.Text = "Form1"; } 接着给Form1构造函数添加代码。使用窗体的CreateGraphics()方法创建一个Graphics对象,其中包括绘图时需要的使用的Windows DC。创建的DC即与显示设备相关也与窗口相关。 public Form1() { InitializeComponent(); Graphics dc = this.CreateGraphics(); this.Show(); Pen bluePen = new Pen(Color.Blue,3); dc.DrawRectangle(bluePen,0,0,50,50); //矩形 Pen redPen = new Pen(Color.Red,2); dc.DrawEllipse(redPen,0,50,80,60); // 椭圆 } 然后调用Show()方法显示窗口。必须让窗口立即显示,因为在其显示之前不能作任何工作。(没有绘图的地方) 最后显示一个矩形和椭圆。注意其中坐标(x,y)表示从窗口的客户区域左上角向右的X个像素,向下的Y个像素。 (其中DrawRectangle()和DrawEillipse()这两个函数前面已经讲过不再重复了。) 上面程序窗体如果最小化再恢复,绘制好的图形就不见了。如果在该窗体上拖动另一个窗口,使之只遮挡一部分图形,再把该窗口拖离这个窗体,临时被遮挡的部分就消失了,只剩下一半椭圆或矩形了!原因是:如果窗体的一部分被隐藏了,Windows通常会立即删除与其中显示的内容相关的所有信息。在窗口的某一部分消失时,那些像素也就丢失了(即Windows释放了保存这些像素的内存)。 但要注意窗口的一部分被隐藏了,当它检测到窗口不再被隐藏时,就请求拥有该窗口的应用程序重新绘制其内容。这个规则有一些例外----窗口的一小部分被挡住的时间比较短(显示菜单时)。一般情况下应用程序就需要在以后重新绘制它。 由于本示例把绘图代码放在Form1的构造函数中,故不能在启动后再次调用该构造函数进行重新绘制。 使用OnPaint()绘制图形 Windows会利用Paint事件通知应用程序完成重新绘制的要求。Paint事件的Form1处理程序处理虚方法OnPaint()的调用,同时传给他一个参数PaintEventArgs。也就是说只要重写OnPaint()执行画图操作。 下面创建一个Windows应用程序DrawShapes来完成这个操作。 protected override void OnPaint(PaintEventarges e) { base.OnPaint(e); Graphics dc = e.Graphics; Pen bluePen = new Pen(Color.Blue,3); dc.DrawRectangle(bluePen,0,0,50,50); Pen redpen = new Pen(Color.Red,2); dc.DrawEllipse(redPen,0,50,80.60); } PaintEventArgs是一个派生自EventArgs的类,一般用于传送有关事件的信息。PaintEventArgs有另外两个属性,其中一个比较重要的是Graphics实例,它们主要用于优化绘制窗口中需要绘制的部分。这样就不必调用CreateGraphics(),在OnPaint()方法中获取DC。 在完成我们的绘图后,还要调用基类OnPaint()方法,因为Windows在绘图过程中可能会执行一些他自己的工作。 这段代码的结果与前面的示例结果相同,但当最小化或隐藏它时,应用程序会正确执行。 使用剪切区域 DrawShapes示例说明了在窗口中绘制的主要规则,但它并不是很高效。原因是它试图绘制窗口中的所有内容,而没有考虑需要绘制多少内容。如下图所示,运行DrawShapes示例,当该示例在屏幕上绘制时,打开一个窗口,把它移动到DrawShapes窗体上,使之隐藏一部分窗体。 到现在为止一切正常。但移动上面的窗口时,DrawShapes窗口会再次全部显示出来,WIndows通常会给窗体发送一个Paint事件,要求它重新绘制本身。矩形和椭圆都位于客户区域的左上角,所以在任何时候都是可见的。在本例中不需要重新绘制这部分,而只要重新绘制白色背景区域。但是,Windows并不知道这一点,他认为应引发Paint事件,调用OnPaint()方法的执行代码。OnPiant()不必重新绘制矩形和椭圆。 在本例中,没有重新绘制图形。原因是我们使用了设备环境。Windows将利用重新绘制某些区域所需要的信息预先初始化设备环境。在GDI中,被标记出来的重绘区域称为无效区域,但在GDI+中,该术语改为剪切区域,设备环境知道这个区域的内容,它截取在这个区域外部的绘图操作,且不把相关的绘图命令传送给显卡。这听起来不错,但仍有一个潜在的性能损失。在确定是在无效区域外部绘图前,我们不知道必须进行多少设备环境处理。在某些情况下,要处理的任务比较多,因为计算哪些像素需要改变什么颜色,将会占用许多处理器时间。 其底线是让Graphics实例完成在无效区域外部的绘图工作,肯定会浪费处理器时间,减慢应用程序的运行。在设计优良的应用程序中,代码将执行一些检查,以查看需要进行哪些绘图工作,然后调用相关的Graphics实例方法。下面将编写一个示例DrawShapesClipping,修改DisplayShapes示例,只完成需要的重新绘制工作。在OnPaint()代码中,进行一个简单的测试,看看无效区域是否需要绘制的区域重叠,如果是就调用绘图方法。 首先,需要获得剪切区域的信息。这需要使用PaintEventArgs的另一个属性。这个属性叫做ClipRectangle,包含要重绘区域的坐标,并包装在一个结构实例System.Drawing.Rectangle中。Rectangle是一个相当简单的结构,包含4个属性:Top、Bottom、Left、Right。它们分别含矩形的上下的垂直坐标,左右的水平坐标。 接着,需要确定进行什么测试,以决定是否进行绘制。这里进行一个简单的测试。注意,在我们的绘图过程中,矩形和椭圆完全包含在(0,0)到(80,130)的矩形客户区域中,实际上,点(82,132)就已经在安全区域中了,因为线条大约偏离这个区域一个像素。所以我们要看看剪切区域的左上角是否在这个矩形区域内。如果是,就重新绘制如果不是就不必麻烦了。 protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); Graphics dc = e.Graphics; if (e.ClipRectangle.Top < 32 && e.ClipRectangle.Left < 82) { Pen bluePen = new Pen(Color.Blue,3); dc.DrawRectangle(bluePen,0,0,50,50); Pen redPen = new Pen(Color.Red,2); dc.DrawEllipse(redPen,0,50,80,60); } } 注意:这个结果和前一个结果完全相同,只是进行了早期测试,确定不需要重绘制的区域,提高了性能。还要注意这个是否进行绘图测试是非常粗略的。还可以进行更精细的测试,确定矩形和椭圆是否要重新绘制。这里有一个平衡。可以在OnPaint()中进行更复杂的测试,以提高性能,也可以使OnPaint()代码复杂一些。进行一些测试总是值得的,因为编写一些代码,可以更多的解除Graphics实例之外的会址内容,Graphics实例只是盲目地执行绘图命令。 测量坐标和区域 GDI+使用几个类似的结构来表示坐标或区域。下面介绍几个结构,他们都是在System.Drawing命名空间中定义的。 结构 | 主 要 公 共 属 性| | Point | X,Y | | PointF | | | Size | Width,Height | | SizeF | | | Rectangle | Left,Right,Top,Bottom,Width,Height| | RectangleF | ,X,Y,Location,Size | (1) Point、PointrF结构 从概念上讲,Point在这个结构中是最简单的,在数学上,它完全等价于一个二维矢量,包含两个公共整形属性,表示它与某个特定位置的水平和垂直距离(在屏幕上),如下图。 为了从点A到点B,需要水平移动20个单位,并向下垂直移动10个单位,在图中标为x和y,这就是他们的一般含义。创建一个Point结构,表示他们: Point ab = new Point(20,10); Console.WriteLine("Moved{0} across,{1} down",ab.x,ab.y); X 和 Y都是读写属性,也可以在Point中设置这些值: Point ab = new Point(); ab.X = 20; ab.Y = 10; Console.WirteLine("Moved{0} across,{1} down",ab.X,ab.Y); 注意:按照惯例,水平和垂直坐标表示为x和y(小写),但对应的Point属性是X和Y(大写),因为在C#中,公共属性的一般约定是名称以大写字母开头。 PointF与Point完全相同,但X和Y属性的类型是float,而不是int。PointF属性用于坐标不是整数值得情况。已经为这些结构定义了数据类型转换,这样就可以把Point隐式转换为PointF(这个转换是结构之间的)。但没有相应的逆过程,要把Point转换为Point,必须显示的复制值或使用下面的3个转换方法:Round(),Truncate(),Ceiling()。 PointF abFloat = new PointF(25.5F,10.9F); // converting to Point Point ab = new Point(); ab.X = (int)abFloat.X; ab.Y = (int)abFloat.Y; Point ab1 = Point.Round(abFloat); Point ab2 = Point.Truncate(abFloat); point ab3 = Point.Ceiling(abFloat); // but conversion back to PointF is implicit PointF abFloat2 = ab; 在默认情况下,GDI+把单位看作是屏幕(或打印机,无论图形设备是什么,都可以这样认为)的像素,这就是Graphics对象方法把它们接受到的坐标看作其参数的方式。例如:点 new Point(20,10)表示在屏幕上水平移动20个像素,向下垂直移动10个像素。通常这些像素从窗体客户区域的左上角开始测量,如上图。但是,情况并不是如此。在某些情况下,需要以窗口的左上角(包括其边框)为原点来绘图,甚至以屏幕的左上角为原点。除特殊说明,大多数可以假定像素是相对于客户区域的左上角。 (2) Size、SizeF结构 Size结构用于int类型,SizeF用于float类型。 在许多情况下Size结构与Point结构是相同的。有两个整形属性,表示水平和垂直距离----区别是两个属性的名称是:Width和Heihgt。 Size ab = new Size(20,10); Console.WriteLine("Moved {0} across,{1} down",ab.Width,ab.Height); 严格的讲,Size在数学上与Point表示的含义相同;但在概念上它使用的方式略有不同。Point用于说明实体在什么地方,而Size用于说明实体有多大。但是Size和 Piont是紧密相关的,目前甚至支持他们之间的显示转换: Point point = new Point(); Size size = (Size)point; Point anotherPoint = (Point)size; 例如:前面绘制的矩形,其左上角的坐标是(0,0),大小是(50,50)。这个矩形的大小是(50,50),可以用一个Size实例来表示。其右下角的坐标也是(50,50),但它由一个Point来表示。 Point和Size结构的相加运算符都已经重载了,所以可以把一个Size加到Point结构上,得到另一个Point结构: static void Main(string[] args) { Point topLeft = new Point(10,10); Size rectangleSize = new Size(50,50); Point bottomRight = topLeft + rectangleSize; Console.WriteLine("topLeft = " + topLeft); Console.WriteLine("bottomRight = " + bottomRight); Console.WirteLine("Size = " + rectangleSize); } 运行结果: topLeft = {X=10,Y=10} bottomRight = {X=60,Y=60} Size = {Width=50,Height=50} 这个结果说明Point和Size的ToString()方法已被重写并以{X,Y}的格式显示。 还可以进行Point和Size之间的显示数据类型转换: Point topLeft = new Point(10,10); Size s1 = (Size)topLeft; Point p1 = (Point)s1; 说明:s1.Width被赋予topLeft.X,s1.Height被赋予topLeft.Y的值。最后p1与topLeft的值相同。 接上一章内容 (3)Rectangle 和 RectangleF 这两个结构表示一个矩形区域。与Point和Size一样,这里只介绍Rectangle结构,Rectangle与RectangleF基本相同,但它的属性类型是float类型,而Rectangle的属性类型是int类型。 Rectangle可以看作由一个Point和一个Size组成,其中Point表示矩形的左上角,Size表示其大小。它的一个结构函数把Point和Size作为其参数。 下面重新编写前面DrawShapes示例代码,绘制一个矩形: Graphics dc = e.Graphics; Pen bluePen = new Pen(Color.Blue,3); Point topLeft = new Point(0,0); Size howBig = new Size(50,50); Rectangle rectangleArea = new Rectangle(topLeft,howBig); dc.DrawRectangle(bluePen,rectangleArea); (4)Region Region表示屏幕上一个有复杂图形的区域。如下图: 可以想象,初始化Region实例的过程相当复杂。从广义上看,可以指定哪些简单的图形组成这个区域,或者绘制这个区域的边界的路径。这种处理就需要Region类。 在进行更高级的绘图工作前介绍几个调试问题。(有一些帮助大家还是看看吧!) 如果在本章的示例中设置了断点,就会注意到调试图形程序不是那样简单。因为进入和退出调试程序常常会把Paint信息传送给应用程序。结果是OnPaint重载方法上设置的断点会让应用程序反复地绘制本身这样程序就不能完成任何工作。 这是典型的一种情况。要明白程序为什么应用程序没有正确显示,可以在OnPaint上设置断点。应用程序会像期望的那样,遇到断点后进入调试程序。此时在 前景上会显示开发环境MDI窗体。如果把开发环境设置为满屏显示,以便更易于观察所有的调试信息,就会完全隐藏目前正在调试的应用程序。 接着检查某些变量的值,希望找出某些有用的信息。然后按F5,告诉程序继续执行,告诉应用程序继续执行,完成某些处理后,看看应用程序在显示其他内容时会 发生什么。但首先发生的是应用程序显示在前景中,Windows检测到窗体再次可见,并提示给他发送了一个Paint事件。当然这表示程序遇到了断点。如 果这就是我们希望的结果,那就很好。但更常见的是,我们希望以后在应用程序绘制了某些有趣的内容之后再遇到断点。我们根本没有在OnPaint中设置断 点,应用程序也不会显示它在最初的启动窗口中显示的内容之外的其他内容。 有一种方式可以解决这个问题。如果有足够大的屏幕,最简单的方式就是恢复开发环境窗口,而不是把它设置为最大化,使之远离应用程序窗口,这样应用程序就不 会被挡住了。但在大多数情况下,这并不是一个有效的解决方案,因为这样会使开发环境窗口过小。另一个解决方案使用相同的规则,即使应用程序声明为在调试时 放在最上层。方法是在Form类中设置属性TopMost,这很容易在InitialzeComponet方法中完成: priavte void InitialzeComponent() { this.TopMost = true; } 也可以在 Visual Studio 2005的属性窗口中设置这个属性。 窗口这是为TopMost 表示应用程序不会被其他窗口挡住(除了其他放在最上层的窗口)。它总是放在其他窗口的上面,甚至在另一个应用程序得到焦点时,也是这样。这是任务管理器的执行方式。 利用这个技巧是必须小心,因为我们不能确定Windows何时会决定应为某种原因引发Paint事件。如果在某些特殊的情况下,OnPaint出了问题 (例如:应用程序在选择某个菜单项后绘图,但此时出了问题)。最好的方式是在OnPaint中编写一些虚拟代码,测试某些条件,这些条件只在特殊情况下才 为True。然后在if 块中设置断点,如下所示: protected override void OnPaint(PaintEventArgs e) { // Condition() evaluates to true when we want to break if (Condition() == true) { int ii = 0; // <-- SET BREAKPOINT!!! } 绘制可滚动的窗口---介绍如何绘制的内容不适合窗口的大小,需要做哪些工作。 下面扩展DrawShapes示例,来解释滚动的概念。为了使该示例更符合实际,首先创建一个BIgShapes示例,该示例将矩形和椭圆画大一些。此时将使用Point,Size,Rectange结构定义绘图域,说明如何使用他们。Form1类的相关部分如下所示: // member fields private Point rectangleTopLeft = new Point(0,0); private Size rectangleSize = new Size(200,200); private Point ellipseTopLeft = new Point(50,200); private Size ellipseSize = new Size(200,150); private Pen bluePen = new Pen(Color.Blue,3); private Pen redPen = new Pen(Color.Red,2); private override void OnPaint(PaintEventArgs e) { base.OnPaint(e); Graphics.dc = e.Graphics; // member fields if (e.ClipRectangle.Top < 350 || e.ClipRectangle.Left < 250) { Rectangle rectangleArea = new Rectangle(rectangleTopLeft,rectangleSize); Rectangle ellipseArea = new Rectangle(ellipseTopLeft,ellipse.Size); dc.DrawRectangle(bluePen,rectangleArea); dc.DrawEllipse(redPen,ellipseArea); } } 注意这里还把Pen、Size、Point对象变成成员字段---这比每次需要绘图时都创建一个新Pen的效率高。 这里有一个问题,图形在300*300像素的绘图区域中放不下。 一般情况下,如果文档太大,不能完全显示,应用程序就会添加滚动条,以便用户滚动窗口,查看其中选中的部分。这是另一个区域,在该区域中如果使用标准控件建立Windows窗体,就让.NET运行环境和基类处理程序。如果窗体上有各种控件,Form实例一般知道这些控件在哪里,如果其窗体可能比较小,Form实例就知道需要添加滚动条。Form实例还会自动添加滚动条,不仅如此,它还可以正确绘制用户滚动到的部分屏幕。此时,用户不需要在代码中做什么工作。但在本章中,我们要在屏幕上绘制图形,所以要帮助Form实例确认何时能滚动。 添加滚动条是很简单的。Form仍会处理所有的操作---因为它不知道绘图区域有多大。在上面的BigShapse示例中没有滚动条的原因是,Windows不知道它们需要滚动条。我们需要确认的是,矩形的大小从文档的左上角(或者是在进行任何滚动前的客户区域左上角)开始向下延伸,其大小应足以包含整个文档。本章把这个区域称为文档区域。在下图可以看出,本例的文档区域应是(250,350)像素。 使用相关的属性Form.AutoScrollMinSize即可确定文档的大小。因此给InitializeComponent()方法或Form1构造函数添加下述代码: private void InitializeComponent() { this.AutoScaleBaseSize = new System.Drawing.Size(5,13); this.ClientSize = new System.Drawing.Size(292,266); this.Name = "From1"; this.Text = "BigShapes"; this.BackColor = Color.White; this.AutoScrollMinSize = new Size(250,350); } 另外,AutoScrollSize属性还可以用VS2005属性窗口设置。 在应用程序启动时设置最小尺寸,并保持不变,在这个应用程序中是必要的,因为我们知道屏幕区域一般是有多大。在运行该应用程序时,这个“文档”是不会改变大小的。但要记住,如果应用程序执行显示文件内容的操作,或者执行某些改变屏幕区域的操作,就需要在其他时间设置这个属性(此时,必须手工调整代码,VS2005属性窗口只能在构建窗体时设置属性的初始值)。 设置MinScrollSize只是一个开始,仅有它是不够的。下图为示例应用程序目前的外观。 注意,不仅窗体正确设置了滚动条,而且他们的大小也正确设置了,以指定文档正确显示的比例。可以试着在运行示例重新设置窗口的大小,这样就会发现滚动条会正确响应,甚至如果窗口变得足够大,不再需要滚动条时,他会消失。 但是,如果使用一个滚动条,并向下滚动它,会发生什么情况?如下图,显然,出错了。 出错的原因是我们没有在OnPaint()重写方法的代码中考虑滚动条的位置。如果最小化窗口,再恢复它,重新绘制一遍窗口,就可以很清楚地看出这一点。结果如图所示。 图形像以前一样进行了绘制,矩形的左上角嵌套在客户区域的左上角,就好像根本没有移动过滚动条一样。 在更正这个问题前,先介绍一下在这些屏幕图上发生了什么。 首先从BigShapes示例开始,如图---所示。在这个例子中,整个窗口刚刚重新进行了绘制。看看前面的代码,该代码的作用是使graphics实例用左上角坐标(0,0)(相对于窗口客户区域的左上角)绘制一个矩形---它是已经绘制过的。问题是,graphics实例在默认情况下把坐标解释为是相对于客户窗口的,它不知道滚动条的存在。代码还没有尝试为滚动条的位置调整坐标。椭圆也是这样。 下面处理图---的问题。在滚动后,注意窗口上半部分显示正确,这是因为它们是在应用程序第一次启动时绘制的。在滚动窗口时,Windows没有要求应用程序重新绘制已经显示在屏幕中的内容。Windows只指出屏幕上目前显示的内容可以平滑移动,以匹配滚动条的位置。这是一个非常高效的过程,因为它也能使用某些硬件加速来完成。在这个屏幕图中,有错的是窗口下部的1/3部分。在应用程序第一次显示时,没有绘制这部分窗口,因为在滚动窗口前,在部分在客户区域的外部。这表示Windows要求BigShapes应用程序绘制这个区域。它引发Paint事件,把这个区域作为剪切的矩形。这也是OnPaint() 重载方法完成的任务。 问题的另一种表达方式是我们把坐标表示为相对于文档开头的左上角---需要转换它们,使之相对于客户区域的左上角。图---说明了这一点。 为了使该图更清晰,我们向下向右扩展了该文档,超出了屏幕的边界,但这不会改变我们的推论,我们还假定其上有一个水平滚动条和一个垂直滚动条。 在该图中,细矩形标记了屏幕区域的边框和整个文档的边框。粗线条标记试图要绘制的矩形和椭圆。P标记要绘制的某个随意点,这个点在后面会作为一个示例。在调用绘图方法时,提供graphics实例和从B点到P点的矢量,这个矢量表示为一个Point实例。我们实际上需要给出从点A到点B的矢量。 不知道A点到P点的矢量,而知道B点到P点的矢量,这是P相对于文档左上角的坐标---要在文档的P点绘图.还知道从B点到A点的矢量,这是滚动的距离,它存储在Form类的一个属性AutoScrollPosition中.但是不知道从A点到P点的矢量. 现在只需进行矢量相减即可.为了使之更简便,Graphics类执行了一个方法来进行这些计算---TranlateTransform.提供水平和垂直坐标,表示客户区域的左上角相对于文档的左上角,然后Graphics设备考虑客户区域相对于文档区域的位置,计算这些坐标. dc.TranslateTranform(this.AutoScrollPosition.X,this.AutoScrollPosition.Y); 在本例还要测试剪切区域,看看是否需要进行绘制工作.这个测试需要调整,把滚动的位置也考虑在内.完成后,该实例的整个绘图代码如下所示: protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); Graphics dc = e.Graphics; Size scrollOffset = new Size(this.AutoScrollPosition); if (e.ClipRectangle.Top + scrollOffset.Width < 350 || e.ClipRectangle.Left + scrollOffset.Height < 250) { Rectangle rectangleArea = new Rectangle(rectangleTopLeft + scrollOffset, rectanlgeSize); Rectangle ellipseArea = new Rectangle(ellipseTopLeft + scrollOffset,ellipseSize); dc.DrawRectangle(bluePen ,rectangleArea); dc.DrawEllipse(redPen,ellipseArea); } 得到正确的滚动屏幕. 世界\页面\设备坐标 测量相对于文档区域左上角的位置和测量相对于屏幕(桌面)左上角的位置之间的区别非常重要,GDI+为它们指定了不同的名称. * 世界坐标(Word Coordinate):要测量的点距离文档区域左上角的位置(以像素为单位). * 页面坐标(Page Coordiante):要测量的点距离客户区域左上角的位置(以像素为单位). 注意: 熟悉GDI开发的人员要注意,世界坐标对应于GDI中的逻辑坐标.页面坐标对应于设备坐标.编写逻辑坐标和设备坐标之间的转换代码在GDI+中有了变化.在GDI中,转化是使用Widows API函数LPtoDP()和DPtoLP()通过设备环境进行的,而在GDI+中,由Control类来维护转化过程中的所需要的信息,Form和各种Windows窗体控件设备派生于Control类. GDI+还有第3种坐标,即设备坐标(Device Coordinate).设备坐标类似于页面坐标,但其测量单位不是像素,而是用户通过调用Graphics.PageUnit属性指定的单位.它可以使用的单位除了默认的像素外,还包括英寸和毫米.它可以用作获取设备的不同像素密度方式.如:在监视器上,100像素约是1英寸.但激光打印机可以达到1200dpi(点/英寸)---这表示一个100像素宽的图形在该激光打印机上打印时会比较小.把单位设置为英寸,指定图形为1英寸宽,就可以确保图形在不同的设备上有相同的大小. 颜色 在GDI+中,颜色用System.Drawing.Color结构的实例来表示。一般情况下,初始化这个结构后,就不能使用对应的Color实例对该结构进行一些操作了----只能把它传送给其他的需要Color的调用方法。前面遇到这种结构,在前面的每个示例中都设置了窗口客户区域的背景色,还设置了要显示的各种图形的颜色。Form.BackColor属性返回一个Color实例。本节将详细介绍这个结构,特别是要介绍构建Color的几种不同方式。 (1) 红绿蓝(RGB)值 监视器可以显示的颜色总数非常大---超过160亿。其确切的数字是2的24方式,即16,777,216。显示,需要对这些颜色进行索引,才能指定在给定的某个像素上要显示什么颜色。 给颜色进行索引的最常见方式是把它们分为红绿蓝成份,这种方式基于以下原则:人眼可以分辨的任何颜色都是由一定量的红色光、绿色光和蓝色光组成的。这些光称为成份(component)。实际上,如果每种成份的光分为256种不同的强度,它们提供了足够平滑的过渡,可以把人眼能分辨出来的图像显示为具有照片质量。因此,指定颜色时,可以给出这些成份的量,其值在0~255之间,其中0表示没有这种成份,255表示这种成份的光达到最大的强度。 这些出了向GDI+说明颜色的第一种方式。可以调用静态函数Color.FromArgb()指定该颜色的红绿蓝值。微软没有为此提供构造函数,原因是除了一般的RGB成份外,还有其它方式表示颜色。因此,微软认为i给定以的构造函数传递会引起误解: Color redColor = Color.FromArgb(255,0,0); Color funnyOrangyBrownColor = Color.FromArgb(255,155,100); Color blackColor = Color.FromArgb(0,0,0); Color whiteColor = Color.FromArgb(255,255,255); 3个参数分别是红绿蓝指。这个函数有许多重载方法,其中一些也允许指定Alpha混合指(这是A在方法FromArgb()中的名称)。Aplha混合超出了本章的范围,但把它与屏幕上已有的颜色混合起来,可以描绘出半透明的颜色。这可以得到一些漂亮的效果,常用于游戏。 (2)命名颜色 使用FromArgb()构造颜色是一种非常灵活的技巧,因为它表示可以指定人眼睛辨识出的任何颜色。但是,如果要得到一些标准、众所周知的纯色,例如红色或蓝色,命名想要的颜色是比较简单的。因此微软还在Color中提供了许多静态属性,每个属性返回一种命名颜色。在下面的实例中,把窗口的背景设置为白色时,就使用了其中一种属性: this.BackColor = Color.White; // has the same effect as; // this.BackColor = Color.FromArgb(255,255,255); 有几百种这样的颜色。完整的列表参见SDK文档。包括所有的纯色:红、白、蓝、绿和黑,还包括MediumAquamarine、LightCoral、DarkOrchid等颜色。还有一个KnownColor枚举,列出了命名的颜色。 (3)图形显示模式和安全的调色板 原则上监视器可以显示超出160亿种RGB颜色,实际上这种取决于如何在计算机上这置显示属性。在Windows中,传统上有3个主要的颜色选项:真彩色(24位)、增强色(16位)、256色。(在目前的一些图形卡上,真彩色是32位的,因为硬件进行了优化,但此时32位中只有24位用于该颜色)。 只有真彩色模式允许同时显示所有的RGB颜色。这听起来是最佳选择,但它是有代价的:完整的RGB值需要用3个字节来保存,这表示要显示的每个像素都需要用图形卡内存中的3个字节来保存。如果图形卡内存需要额外的费用,就可以选择其他模式。增强颜色模式用两个字节表示以像素。每个RGB成份用5位就足够了。所以红色只有32种不同的强度,而不是256种。蓝色和绿色也是这样,总共有65535种颜色。这对于需要偶尔察看照片质量级的图像来说是足够了,但比较微妙的阴影区域会被破坏。 256色模式给出的颜色更少。但是在这种模式下,可以选择任何颜色,系统会建立一个调色板,这是一个从160亿RGB颜色中选择出来的256种颜色列表。在调色板中指定了颜色后,图形设备就只显示所指定的这些颜色。当获得高性能和视频内存需要额外的费用时,才使用256色模式。大多数计算机游戏都使用 这种模式----它们仍能得到相当好的图形,因为调色板经过了非常仔细的选择。 一般情况下,如果显示设备使用增强色或256色模式,并要显示某种RGB颜色,它就会从能显示的颜色池中选择一种在数学上最接近的匹配颜色。因此知道颜色模式是非常重要的。如果要绘制某些涉及微妙阴影区域或照片质量级的图像,而用户没有选择24位颜色模式,就看不到期望的效果。如果要使用GDI+进行绘制,就应该用不同的颜色模式测试应用程序。 (4)安全调色板 这是一种非常常见的默认调色板。它工作的方式是为每种颜色成分这置6个间隔相等的值,这些值分别是0,51,102,153,204,255。换言之,红色成分可以是这些值中的任一个。绿色成分和蓝色成分也一样。所以安全调色板中的颜色就包括(0,0,0)(黑色)、(153,0,0)(暗红色)、(0,255,102)(蓝绿色)等,这样就得到了6的立方=216种颜色。这是一种让调色板包含色谱中颜色和所有亮度的简单方式,但实际上这是不可行的,因为数学上登间隔的颜色成分并不表示这些颜色的区别在人眼看来也是相等的。但安全调色板使用非常广泛,相当多的应用程序和图像仍然使用安全调色板上的颜色。 如果把Windows设置为256色模式,默认的调色板就是安全调色板,其中添加了20种标准的Windows颜色和20种备用颜色。 画笔和钢笔 本节介绍两个辅助类,在绘制图形时需要使用它们。前面已经见过了Pen类,它用于告诉工人graphics实例如何绘制线条。相关的类是System.Drawing.Brush,告诉graphics实例如何填充区域。例如,Pen用于绘制前面示例中的矩形和椭圆的边框。如果需要把这些图形绘制为实心的,就要使用画笔指定如何填充它们。这两个类有一个共同点:很难对他们调用任何方法。用需要的颜色和其他属性构造一个Pen或Brush实例,再把它传送给需要Pen或Brush的绘图方法即可。 <注>: 如果使用以前的GDI编程,可能会注意到在前两个示例中,在GDI+中使用Pen的方式是不同的.在GDI中,一般是调用一个WindowsAPI函数SelectObject(),它把钢笔关联到设备环境上.这个钢笔用于所有需要钢笔的绘图操作中,直到再次调用SelectObject()通知设备环境停止使用它时为止.这个规则也适用于画笔或其它对象,例如字体和位图,而使用GDI+,微软使用一种无状态的模式,其中没有默认的钢笔或其它帮助对象.只需给每个方法调用指定合适的帮助对象即可. <1>画笔 GDI+有几种不同类型的画笔,这里只解释几个比较简单的画笔,每种画笔都由一个派生自抽象类System.Drawing.Brush的类实例来表示.最简单的画笔System.Drawing.SolidBrush仅指定了区域用纯色来填充: Brush solidBeigeBrush = new SolidBrush(Color.Beige); Brush solidFunnyOrangeyBrownBrush = new SolidBrush(Color.FromArgb(25,155,100)); 另外,如果画笔是一种Web安全颜色,就可以用另一个类System.Drawing.Brushes构造出画笔.Brushes是永远不能实例化的一个类(它有一个私有构造函数,禁止实例化).它有许多静态属性,每个属性都返回指定颜色的画笔.如下: Brush solidAzureBrush = Burshes.Azure; Brush solidChoolateBrush = BrushesChoolate; 比较复杂的一种画笔是影线画笔(hatch brush),它通过绘制一种模式填充区域,这种类型的画笔比较高级,所以Drawing2D命名空间中,用System.Drawing.Drawing2D.HatchBrush类表示.Brushes类不能帮助我们使用影线画笔,而需通过提供一个影线型式和两种颜色(前景色和背景色,背景色可以忽略,此时将使用默认的黑色),来显示构造一个影线画笔.影线型式可以取自于枚举Sysytem.Drawing.Drawing2D.HatchStyle,其中有许多HatchStyle值,其完整列表参阅SDK. 一般型式包括: ForwardDiagonal,Cross,DiagonalCross,SmallConfetti,ZigZag.示例如下: Brush crossBrush = new HatchBrush(HatchStyle.Cross,Color.Azure); // background color of CrossBrush is black Brush brickBrush = new HatchBrush(HatchStyle.DiagonalBrick,Color.DarkGoldenrod,Color.Cyan); GDI只能使用实践和影线画笔,GDI+添加了两种新画笔: * System.Drawing.Drawing2D.LinearGradientBrush用一种在屏幕上可变的颜色填充区域. * System.Drawing.Drawing2D.PathGradientBrush与此类似,但其颜色沿着要填充的区域的路径而变化. <2>钢笔 钢笔只使用一个类System.Drawing.Pen来表示.但钢笔比画笔复杂一些,因为它需要指定线条应有多宽(像素),对于一条比较宽的线段,还要确定如何填充该线条中的区域.钢笔还可以指定其他许多属性,本章不讨论它们,其中包括前面提到的Alignment属性,该属性表示相对于图形的边框,线条该如何绘制,以及在线条的末尾绘制什么图形(是否使图形光滑过度). 粗线条中的区域可以用纯色填充,或者使用画笔来填充.因此Pen实例可以包括Brush实例的引用.这是非常强大的,因为这表示可以绘制有影线填充或线性阴影的线条.构造Pen实例有四中不同的方式.可以通过传送一种颜色,或者传送一种画笔.这两个构造函数都会生成一个像素宽的钢笔.另外,还可以传送一种颜色或画笔,以及一个表示钢笔宽度的float类型的值.(该宽度必须是一个float类型的值,以防执行绘图操作的Graphics对象使用非默认的单位,例如毫米或英寸,例如可以指定宽度是英寸的某个分数).例如可以构造如下的钢笔: Brush brickBrush = new HatchBrush(HatchStyle.DiagonalBrick,Color.DarkGoldenrod,Color.Cyan); Pen solidBluePen = new Pen(Color.FromArgb(0,0,255)); Pen solidWideBluePen = new Pen(Color.Blue,4); Pen brickPen = new Pen(brickBrush); Pen brickWidePen = new Pen(brickBrush,10); 另外,为了快速构造钢笔,还可以使用类System.Drawing.Pens,它与Brushes类一样.包括许多存储好的钢笔.这些钢笔的宽度都是一个像素,使用通常的Web安全颜色,这样就可以用下述方式构建一个钢笔: Pen solidYellowPen = Pens.Yellow; 绘制图形和线条System.Drawing.Graphics有很多方法,利用这些方法可以绘制各种线条、空心图形和实心图型。下图给出了只要方法。 在结束绘制简单对象的主题前,用一个简单示例来说明使用画笔可以得到的各种可视效果。该实例是ScrollMoreShapes,它是ScrollShapes的修正版本。除了矩形和椭圆外,我们还添加了一条粗线,用各种定制的画笔填充图形。前面解释了绘图的规则,所以这里只给出代码,而不作多的注释。首先,因为添加了新画笔,所以需要指定使用命名空间System.Drawing.Drawing2D: using System; using System.Collection.Generic; using System.ComponentModel; usgin System.Data; using System.Drawing; using System.Drawing.Drawing2D; using System.Text; using System.Windows.Forms; 接着是Form1类中的一些额外字段,其中包含了要绘制图形的位置信息,以为要使用的各种钢笔和画笔: private Rectangle rectangleBounds = new Rectangle(new Point(0,0),new Size(200,200)); private Rectangle ellipseBounds = new Rectangle(new Point(50,200),new Size(200,150)); private Pen bluePen = new Pen(Color.Blue,3); private Pen redPen = new Pen(Color.Red,2); private Brush solidAzureBrush = Brushes.Azure; private Brush solidYellowBrush = new SolidBrush(Color.Yellow); static private Brush brickBrush = new HatchBrush(HatchStyle.DiagonalBrick,Color.DarkGoldenrod,Color.Cyan); private Pen brickWidePen = new Pen(brickBrush,10); 把BrickBrush字段声明为静态,就可以使用该字段的值初始化BrickWidePen字段了。C#不允许使用一个实例字段初始化另一个实例字段,因为还没有定义要先初始化哪个实例字段,如果把字段声明为静态字段就可以解决这个问题,因为只实例化了Form1类的实例,字段是静态字段还是实例字段就不重要了。 from:http://www.cnblogs.com/Holmes-Jin/archive/2012/01/30/2332030.html |
支持格式:BMP、GIF、JPEG、EXIF、PNG、TIFF、ICON、WMF、EMF等,几乎涵盖所有常用格式 图像类: Image类:Bitmap和Metafile的类提供功能的抽象基类。 Metafile类:定义图形图元文件,图元文件包含描述一系列图形操作的记录,这些操作可以被记录(构造)和被回放(显示) Bitmap类:封装GDI+位图,此位图由图形图像及其属性的像素数据组成,Bitmap是用于处理由像素数据定义的图像的对象。 常用属性以及方法: 名称 说明 公共属性 Height 获取此Image对象的高度。 RawFormat 获取此Image对象的格式。 Size 获取此Image对象的宽度和高度。 Width 获取此Image对象的宽度。 公共方法 GetPixel 获取此Bitmap中指定像素的颜色。 MakeTransparent 使默认的透明颜色对此Bitmap透明。 RotateFlip 旋转、翻转或者同事旋转和翻转Image对象。 Save 将Image对象以指定的格式保存到指定的Stream对象。 SetPixel 设置Bitmap对象中指定像素的颜色。 SetPropertyItem 将指定的属性项设置为指定的值。 SetResolution 设置此Bitmap的分辨率。 构造BitMap实例: public Bitmap(Image ); //从现有图像创建,其中可换成等效参数 public Bitmap(int, int); public Bitmap(Image, bool );//bool:颜色校正标志位 public Bitmap(Type , string );//type提取资源的类, string 资源名 public Bitmap(int , int , PixelFormat );// PixelFormat像素格式 枚举 public Bitmap(int , int , Graphics);// Graphics设置分辨率 public Bitmap(int , int , int stride, PixelFormat , IntPtr );// stride 指定相邻扫描行开始处之间字节偏移量的整数。传递给此参数的值必须为4 的倍数。IntPtr指向包含像素数据的字节数组的指针。 PixelFormat枚举: 名称 说明 DontCare = 0 没有指定像素格式 […]
View Details命名空间: System.Drawing.Drawing2D SolidBrush:一般的画刷,通常只用一种颜色去填充GDI+图形 创建一般画刷: SolidBrush sdBrush1 = new SolidBrush(Color); HatchBrush:阴影画刷,有两种颜色:前景色和背景色,以及6种阴影。 创建阴影画刷: HatchBrush(HatchStyle,Color);//前景 HatchBrush(HatchStyle,Color,Color);//前景、背景 HatchStyle对应阴影方案列表。 名称 说明 BackwardDiagonal 从右上到左下的对角线的线条图案。 Cross 指定交叉的水平线和垂直线。 DarkDownwardDiagonal 从顶点到底点向右倾斜的对角线,两边夹角比ForwardDiagonal小50%,宽度是其两倍。此阴影图案不是锯齿消除的。 DarkHorizontal 指定水平线的两边夹角比Horizontal小50%并且宽度是Horizontal的两倍。 DarkUpwardDiagonal 指定从顶点到底点向左倾斜的对角线,其两边夹角比BackwardDiagonal小50%,宽度是其两倍,但这些直线不是锯齿消除的。 DarkVertical 指定垂直线的两边夹角比Vertical小50%并且宽度是其两倍。 DashedDownwardDiagonal 指定虚线对角线,这些对角线从顶点到底点向右倾斜。 DashedHorizontal 指定虚线水平线。 DashedUpwardDiagonal 指定虚线对角线,这些对角线从顶点到底点向左倾斜。 DashedVertical 指定虚线垂直线。 DiagonalBrick 指定具有分层砖块外观的阴影,它从顶点到底点向左倾斜。 DiagonalCross 交叉对角线的图案。 Divot 指定具有草皮层外观的阴影。 ForwardDiagonal 从左上到右下的对角线的线条图案。 Horizontal 水平线的图案。 HorizontalBrick 指定具有水平分层砖块外观的阴影。 LargeGrid 指定阴影样式Cross。 LightHorizontal 指定水平线,其两边夹角比Horizontal小50%。 LightVertical 指定垂直线的两边夹角比Vertical小50%。 Max 指定阴影样式SolidDiamond。 Min 指定阴影样式Horizontal。 NarrowHorizontal 指定水平线的两边夹角比阴影样式Horizontal小 75%(或者比LightHorizontal小25%)。 NarrowVertical 指定垂直线的两边夹角比阴影样式Vertical小 75%(或者比LightVertica小25%)。 OutlinedDiamond 指定互相交叉的正向对角线和反向对角线,但这些对角线不是锯齿消除的。 Percent05 指定5%阴影。前景色与背景色的比例为5:100。 Percent90 指定90%阴影。前景色与背景色的比例为90:100。 Plaid 指定具有格子花呢材料外观的阴影。 Shingle 指定带有对角分层鹅卵石外观的阴影,它从顶点到底点向右倾斜。 SmallCheckerBoard 指定带有棋盘外观的阴影。 SmallConfetti 指定带有五彩纸屑外观的阴影。 SolidDiamond 指定具有对角放置的棋盘外观的阴影。 Sphere 指定具有球体彼此相邻放置的外观的阴影。 Trellis 指定具有格架外观的阴影。 Vertical 垂直线的图案。 […]
View DetailsGDI+存在的意义:将编程与具体硬件实现细节分开。 GDI+步骤:获取画布,绘制图像、处理图像 命名空间: System.Drawing命名空间提供对GDI+基本图形功能的访问 System.Drawing.Drawing2D:提供高级的二维和矢量图形功能 System.Drawing.Imaging:命名空间提供高级GDI+图像处理功能 System.Drawing.Text:提供高级GDI+排班功能 System.Drawing.Pringting:提供打印相关服务 System.Drawing.Design:扩展设计时,用户界面逻辑和绘制的类。用于扩展,自定义。 画图工具: Graphics(画布):类封装一个GDI+绘图图面,提供将对象绘制到显示设备的方法,Graphics与特定的设备上下文关联。画图方法都被包括在Graphics类中,在画任何对象之前都需要创建一个Graphics类实例作为画布。 创建画布 (三种方法): 利用控件或窗体的Paint事件中的PainEventArgs。 适用场景:为控件创建绘制代码。 示例: //窗体的Paint事件的响应方法 private void Form1_Paint(object sender, PaintEventArgs e) { Graphics g = e.Graphics; } //直接重载控件或窗体的OnPaint方法: protected override void OnPaint(PaintEventArgs e) { Graphics g = e.Graphics; } 注意:Paint事件在重绘控件时发生。 调用某控件或窗体的CreateGraphics方法以获取对Graphics对象的引用,该对象表示该控件或窗体的绘图图面。 适用场景:在已存在的窗体或控件上绘图 Graphics g = this.CreateGraphics(); 由从Image继承的任何对象创建Graphics对象。 适用场景: 需要更改已存在的图像 Image img = Image.FromFile(@"IMG\graph.jpg"); Graphics g = Graphics.FromImage(img); 方法成员: 名称 说明 DrawArc 画弧。 DrawBezier 画立体的贝尔塞曲线。 DrawBeziers 画连续立体的贝尔塞曲线。 DrawClosedCurve 画闭合曲线。 DrawCurve 画曲线。 DrawEllipse 画椭圆。 DrawImage 画图像。 DrawLine 画线。 DrawPath 通过路径画线和曲线。 DrawPie 画饼形。 DrawPolygon […]
View Details在绘制文字之前加上这一行,就可以绘制任意文字而不会出现锯齿: g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias; from:http://blog.sina.com.cn/s/blog_7744890b0102v13q.html
View Details