一个非常好用的图片切割工具(c# winform开发)
本人业余时间开发了一个图片切割工具,非常好用,也很灵活!
特别对大型图片切割,更能体现出该软件的优势!
功能说明
可以设定切割的高度和宽度。切割线可以上下拖动,可以增加一个切割区域,可设定某个区域不参与切割。
主要技术点分析
切割区域确定
每个切割区域是一个长方形。用一个结构标识该属性。
1 class SpliteMoveIndex 2 { 3 public enum EN_DIR 4 { 5 NON, 6 HORIZONTAL, 7 VERTICAL 8 }; 9 public EN_DIR direct = EN_DIR.NON;//0 无;1 水平;2垂直 10 public int rectIndex; //第几个rect 11 public int lineIndex; //第几个线 1:上或左;2 下或右 12 public int mouseX; 13 public int mouseY; 14 15 16 public static SpliteMoveIndex CreateNon(int x, int y) 17 { 18 SpliteMoveIndex _nonIndex = new SpliteMoveIndex(); 19 _nonIndex.direct = EN_DIR.NON; 20 _nonIndex.SetMouse(x, y); 21 return _nonIndex; 22 } 23 24 public SpliteMoveIndex() 25 { 26 27 } 28 public SpliteMoveIndex(int x, int y) 29 { 30 SetMouse(x, y); 31 } 32 33 public bool IsSameLine(SpliteMoveIndex another) 34 { 35 return this.direct == another.direct 36 && this.rectIndex == another.rectIndex 37 && this.lineIndex == another.lineIndex; 38 } 39 40 public bool IsIn() 41 { 42 return direct != EN_DIR.NON; 43 } 44 public bool IsHorIn() 45 { 46 return direct == EN_DIR.HORIZONTAL; 47 } 48 public bool IsVertIn() 49 { 50 return direct == EN_DIR.VERTICAL; 51 } 52 53 public void SetMouse(int x, int y) 54 { 55 mouseX = x; 56 mouseY = y; 57 } 58 }
SpliteRectGroup 负责组合这些长方形。当有鼠标移动时,动态调整这些长方形大小,再重画!
1 class SpliteRectGroup 2 { 3 List<Rectangle> _listSplitRect = new List<Rectangle>(); 4 int _widthSrc; 5 int _heightSrc; 6 7 SpliteMoveIndex _lastMoveIndex = new SpliteMoveIndex(); 8 9 public int _defaultHitSpace = 5; 10 11 int _moveAllFlagR = 10; 12 bool _isMoveAll = false; 13 14 //不参加切割的区域 15 List<int> _listSplitRectNotUsed = new List<int>(); 16 17 public void SetRect(int widthSrc, int heightSrc, int startX, int startY, 18 int widthDest, int heightDest) 19 { 20 _widthSrc = widthSrc; 21 _heightSrc = heightSrc; 22 _listSplitRect.Clear(); 23 24 GetSplitSize(_widthSrc, _heightSrc, startX, startY, 25 widthDest, heightDest, ref _listSplitRect); 26 } 27 28 public List<Rectangle> GetRects() 29 { 30 return _listSplitRect; 31 } 32 33 public List<Rectangle> GetRectsSplit() 34 { 35 List<Rectangle> listShow = new List<Rectangle>(); 36 37 int i = 0; 38 foreach(Rectangle rect in _listSplitRect) 39 { 40 if(IsRectUsed(i)) 41 { 42 listShow.Add(rect); 43 } 44 i++; 45 } 46 return listShow; 47 } 48 49 50 public int GetStartX() 51 { 52 if (_listSplitRect.Count == 0) 53 return 0; 54 Rectangle first = _listSplitRect.First(); 55 return first.X; 56 } 57 58 public int GetSpliteWidth() 59 { 60 if (_listSplitRect.Count == 0) 61 return 0; 62 Rectangle first = _listSplitRect.First(); 63 return first.Width; 64 } 65 66 public int GetSpliteTotalHeight() 67 { 68 if (_listSplitRect.Count == 0) 69 return 0; 70 int i = 0; 71 foreach (Rectangle r in _listSplitRect) 72 { 73 i += r.Height; 74 } 75 return i; 76 } 77 78 public void SetMoveAllFlag(bool flag) 79 { 80 _isMoveAll = flag; 81 } 82 83 public bool GetMoveAllFlag() 84 { 85 return _isMoveAll; 86 } 87 88 public int GetStartY() 89 { 90 if (_listSplitRect.Count == 0) 91 return 0; 92 Rectangle first = _listSplitRect.First(); 93 return first.Y; 94 } 95 public void Draw(Graphics g) 96 { 97 SolidBrush brushRect = new SolidBrush(Color.FromArgb(200, 255, 0, 0)); 98 Font strfont = new Font("Verdana", 20); 99 Brush strBrush = Brushes.Blue; 100 Brush strBrushBack = Brushes.White; 101 102 //起点圆 103 int x = GetStartX(); 104 int y = GetStartY(); 105 g.FillEllipse(brushRect, x - _moveAllFlagR, y - _moveAllFlagR, 2 * _moveAllFlagR, 2 * _moveAllFlagR); 106 brushRect.Dispose(); 107 //起点信息 108 string startInfo = string.Format("({0}:{1})",x,y); 109 SizeF sizeF = g.MeasureString(startInfo, strfont); 110 Point ptStart = new Point((int)(x-sizeF.Width/2), (int)(y -sizeF.Height- _defaultHitSpace*2) ); 111 g.FillRectangle(strBrushBack, new RectangleF(ptStart, sizeF)); 112 g.DrawString(startInfo, strfont, strBrush, ptStart); 113 114 115 //画方框 116 Color backColor = Color.FromArgb(0, Color.PowderBlue); 117 HatchBrush hat1 = new HatchBrush(HatchStyle.OutlinedDiamond, Color.DarkBlue, backColor); 118 HatchBrush hat2 = new HatchBrush(HatchStyle.OutlinedDiamond, Color.Red, backColor); 119 120 //输出提示信息 121 Pen rectPen = Pens.Red; 122 int i = 0; 123 int showIndex = 0; 124 string info; 125 foreach (Rectangle rect in _listSplitRect) 126 { 127 i++; 128 bool used = IsRectUsed(rect); 129 if (used) 130 { 131 showIndex++; 132 info = string.Format("{0}-({1}:{2})", showIndex, rect.Width, rect.Height); 133 } 134 else 135 { 136 info = string.Format("({0}:{1})--不参与切割", rect.Width, rect.Height); 137 } 138 139 g.DrawRectangle(rectPen, rect); 140 if (!used) 141 { 142 g.FillRectangle(hat1, rect); 143 } 144 145 Point strStart = new Point(rect.X + 5, rect.Y + 5); 146 sizeF = g.MeasureString(info, strfont); 147 g.FillRectangle(strBrushBack, new RectangleF(strStart, sizeF)); 148 149 g.DrawString(info, strfont, strBrush, strStart); 150 } 151 strfont.Dispose(); 152 hat1.Dispose(); 153 hat2.Dispose(); 154 } 155 public bool StartPointMoveTo(int x, int y) 156 { 157 if (_listSplitRect.Count == 0) 158 return false; 159 160 Rectangle first = _listSplitRect.First(); 161 int moveX = x - first.X; 162 int moveY = y - first.Y; 163 164 List<Rectangle> listSplitRectNew = new List<Rectangle>(); 165 foreach (Rectangle r in _listSplitRect) 166 { 167 Rectangle tmp = r; 168 tmp.Offset(moveX, moveY); 169 listSplitRectNew.Add(tmp); 170 } 171 172 _listSplitRect.Clear(); 173 _listSplitRect = listSplitRectNew; 174 return true; 175 } 176 177 public bool IsAllMove(int mouseX, int mouseY) 178 { 179 GraphicsPath myGraphicsPath = new GraphicsPath(); 180 myGraphicsPath.Reset(); 181 Region myRegion = new Region(); 182 183 int x = GetStartX(); 184 int y = GetStartY(); 185 myGraphicsPath.AddEllipse(x - _moveAllFlagR, y - _moveAllFlagR, 2 * _moveAllFlagR, 2 * _moveAllFlagR);//points); 186 myRegion.MakeEmpty(); 187 myRegion.Union(myGraphicsPath); 188 //返回判断点是否在多边形里 189 bool myPoint = myRegion.IsVisible(mouseX, mouseY); 190 return myPoint; 191 } 192 193 public void ResetMoveFlag() 194 { 195 _lastMoveIndex.direct = SpliteMoveIndex.EN_DIR.NON; 196 } 197 198 public bool SetMove(SpliteMoveIndex index) 199 { 200 //移动到区域外 201 if (!index.IsIn()) 202 { 203 _lastMoveIndex = index; 204 return false; 205 } 206 207 //不是同一条线 208 if (!_lastMoveIndex.IsSameLine(index)) 209 { 210 _lastMoveIndex = index; 211 return false; 212 } 213 214 //移动到新的区域 215 MoveRect(_lastMoveIndex, index); 216 _lastMoveIndex = index; 217 return true; 218 } 219 220 public bool IsInSplite() 221 { 222 if (_lastMoveIndex == null) 223 return false; 224 return _lastMoveIndex.IsIn(); 225 } 226 227 void MoveRect(SpliteMoveIndex last, SpliteMoveIndex now) 228 { 229 if (last.IsHorIn()) 230 { 231 MoveRectHor(last, now); 232 } 233 else if (last.IsVertIn()) 234 { 235 MoveRectVert(last, now); 236 } 237 } 238 239 void MoveRectHor(SpliteMoveIndex last, SpliteMoveIndex now) 240 { 241 int moveY = now.mouseY - last.mouseY; 242 List<Rectangle> listSplitRectNew = new List<Rectangle>(); 243 int i = 0; 244 int find = 0; 245 foreach (Rectangle r in _listSplitRect) 246 { 247 Rectangle tmp = r; 248 i++; 249 if (find == 2) 250 { 251 listSplitRectNew.Add(tmp); 252 continue; 253 } 254 if (find == 1) 255 { 256 tmp.Y += moveY; 257 tmp.Height -= moveY; 258 find = 2; 259 listSplitRectNew.Add(tmp); 260 continue; 261 } 262 263 if (i == last.rectIndex) 264 { 265 if (last.lineIndex == 1) 266 { 267 tmp.Y += moveY; 268 tmp.Height -= moveY; 269 find = 2; 270 listSplitRectNew.Add(tmp); 271 } 272 else if (last.lineIndex == 2) 273 { 274 tmp.Height += moveY; 275 find = 1; 276 listSplitRectNew.Add(tmp); 277 } 278 } 279 else 280 { 281 listSplitRectNew.Add(tmp); 282 } 283 } 284 285 _listSplitRect.Clear(); 286 _listSplitRect = listSplitRectNew; 287 } 288 289 290 void MoveRectVert(SpliteMoveIndex last, SpliteMoveIndex now) 291 { 292 int moveX = now.mouseX - last.mouseX; 293 List<Rectangle> listSplitRectNew = new List<Rectangle>(); 294 int i = 0; 295 foreach (Rectangle r in _listSplitRect) 296 { 297 Rectangle tmp = r; 298 i++; 299 if (last.lineIndex == 1) 300 { 301 tmp.X += moveX; 302 tmp.Width -= moveX; 303 listSplitRectNew.Add(tmp); 304 } 305 else if (last.lineIndex == 2) 306 { 307 tmp.Width += moveX; 308 listSplitRectNew.Add(tmp); 309 } 310 } 311 312 _listSplitRect.Clear(); 313 _listSplitRect = listSplitRectNew; 314 } 315 316 SpliteMoveIndex GetHorizontal(int x, int y, int hitSpace) 317 { 318 int startX = GetStartX(); 319 int width = GetSpliteWidth(); 320 if (x < startX || x > (startX + width)) 321 return SpliteMoveIndex.CreateNon(x, y); 322 323 int i = 0; 324 foreach (Rectangle rect in _listSplitRect) 325 { 326 i++; 327 int y1 = rect.Y; 328 //是否落在水平线 一定范围内 329 if (y >= y1 - hitSpace && y <= (y1 + hitSpace)) 330 { 331 SpliteMoveIndex moveIndex = new SpliteMoveIndex(x, y); 332 moveIndex.direct = SpliteMoveIndex.EN_DIR.HORIZONTAL; 333 moveIndex.rectIndex = i; 334 moveIndex.lineIndex = 1; 335 return moveIndex; 336 } 337 338 int y2 = rect.Y + rect.Height; 339 if (y >= (y2 - hitSpace) && y <= (y2 + hitSpace)) 340 { 341 SpliteMoveIndex moveIndex = new SpliteMoveIndex(x, y); 342 moveIndex.direct = SpliteMoveIndex.EN_DIR.HORIZONTAL; 343 moveIndex.rectIndex = i; 344 moveIndex.lineIndex = 2; 345 return moveIndex; 346 } 347 } 348 349 return SpliteMoveIndex.CreateNon(x, y); 350 } 351 352 SpliteMoveIndex GetVectical(int x, int y, int hitSpace) 353 { 354 int startY = GetStartY(); 355 if (y < startY || y > (startY + _heightSrc)) 356 return SpliteMoveIndex.CreateNon(x, y); 357 358 int i = 0; 359 foreach (Rectangle rect in _listSplitRect) 360 { 361 i++; 362 //是否落在垂直线 一定范围内 363 if (y >= rect.Y && y <= (rect.Y + rect.Height)) 364 { 365 int x1 = rect.X; 366 if (x >= (x1 - hitSpace) && x <= (x1 + hitSpace)) 367 { 368 SpliteMoveIndex moveIndex = new SpliteMoveIndex(x, y); 369 moveIndex.direct = SpliteMoveIndex.EN_DIR.VERTICAL; 370 moveIndex.rectIndex = i; 371 moveIndex.lineIndex = 1; 372 return moveIndex; 373 } 374 375 int x2 = rect.X + rect.Width; 376 if (x >= (x2 - hitSpace) && x <= (x2 + hitSpace)) 377 { 378 SpliteMoveIndex moveIndex = new SpliteMoveIndex(x, y); 379 moveIndex.direct = SpliteMoveIndex.EN_DIR.VERTICAL; 380 moveIndex.rectIndex = i; 381 moveIndex.lineIndex = 2; 382 return moveIndex; 383 } 384 } 385 } 386 387 return SpliteMoveIndex.CreateNon(x, y); 388 } 389 390 public SpliteMoveIndex PointHit(int x, int y, int hitSpace) 391 { 392 //判断是否在水平线 393 SpliteMoveIndex hRect = GetHorizontal(x, y, hitSpace); 394 if (hRect.IsIn()) 395 return hRect; 396 397 //判断是否在垂直线 398 SpliteMoveIndex vRect = GetVectical(x, y, hitSpace); 399 if (vRect.IsIn()) 400 return vRect; 401 402 return SpliteMoveIndex.CreateNon(x, y); 403 } 404 405 public bool PointInRect(int x,int y) 406 { 407 int startX = GetStartX(); 408 int width = GetSpliteWidth(); 409 if (x < startX || x > (startX + width)) 410 return false; 411 412 int startY = GetStartY(); 413 int heght = GetSpliteTotalHeight(); 414 if (y < startY || y > (startY + heght)) 415 return false; 416 return true; 417 } 418 419 public void ClearNotUsedRect() 420 { 421 _listSplitRectNotUsed.Clear(); 422 } 423 424 public bool GetRectIndex(int index,ref Rectangle outRect) 425 { 426 int i = 0; 427 foreach (Rectangle rect in _listSplitRect) 428 { 429 if (i == index) 430 { 431 outRect = rect; 432 return true; 433 } 434 i++; 435 } 436 return false; 437 } 438 439 public bool IsNotUsed(int x, int y) 440 { 441 Rectangle rect = new Rectangle(); 442 foreach (int n in _listSplitRectNotUsed) 443 { 444 if (GetRectIndex(n, ref rect) && rect.Contains(x, y)) 445 { 446 return true; 447 } 448 } 449 return false; 450 } 451 452 public bool IsRectUsed(Rectangle rect) 453 { 454 Rectangle rectNot = new Rectangle(); 455 foreach (int n in _listSplitRectNotUsed) 456 { 457 if (GetRectIndex(n, ref rectNot) && rectNot == rect) 458 { 459 return false; 460 } 461 } 462 return true; 463 } 464 465 public bool IsRectUsed(int index) 466 { 467 foreach (int n in _listSplitRectNotUsed) 468 { 469 if (n == index) 470 return false; 471 } 472 return true; 473 } 474 475 //区块加入切割 476 public bool AddRectUsed(int x,int y) 477 { 478 int i = 0; 479 Rectangle rectNot = new Rectangle(); 480 foreach (int n in _listSplitRectNotUsed) 481 { 482 if (GetRectIndex(n, ref rectNot) && rectNot.Contains(x,y)) 483 { 484 _listSplitRectNotUsed.RemoveAt(i); 485 return true; 486 } 487 i++; 488 } 489 return false; 490 } 491 492 //区块不加入切割 493 public bool DelRectUsed(int x, int y) 494 { 495 int i = 0; 496 foreach (Rectangle rect in _listSplitRect) 497 { 498 if (rect.Contains(x, y)) 499 { 500 _listSplitRectNotUsed.Add(i); 501 return true; 502 } 503 i++; 504 } 505 return false; 506 } 507 508 public bool AddHorLine(int x, int y) 509 { 510 List<Rectangle> listSplitRectNew = new List<Rectangle>(); 511 foreach (Rectangle rect in _listSplitRect) 512 { 513 if (y > rect.Y && y < rect.Y + rect.Height) 514 { 515 Rectangle r1 = new Rectangle(rect.Location, rect.Size); 516 r1.Height = y - rect.Y; 517 listSplitRectNew.Add(r1); 518 519 r1.Y = y ; 520 r1.Height = (rect.Y + rect.Height - y); 521 listSplitRectNew.Add(r1); 522 } 523 else 524 { 525 listSplitRectNew.Add(rect); 526 } 527 } 528 _listSplitRect = listSplitRectNew; 529 return true; 530 } 531 532 //删除水平线 533 public bool DeleteHorSplite(SpliteMoveIndex index) 534 { 535 List<Rectangle> listSplitRectNew = new List<Rectangle>(); 536 int i = 0; 537 bool del = false; 538 Rectangle lastRect = new Rectangle(); 539 bool haveLast = false; 540 541 foreach (Rectangle rect in _listSplitRect) 542 { 543 i++; 544 if(haveLast) 545 { 546 haveLast = false; 547 lastRect.Height += rect.Height; 548 listSplitRectNew.Add(lastRect); 549 continue; 550 } 551 552 if(index.rectIndex == i) 553 { 554 del = true; 555 if (index.lineIndex == 1) 556 { 557 if(listSplitRectNew.Count == 0) 558 { 559 continue; 560 } 561 else 562 { 563 Rectangle r = listSplitRectNew.Last(); 564 r.Height += rect.Height; 565 listSplitRectNew.RemoveAt(listSplitRectNew.Count-1); 566 listSplitRectNew.Add(r); 567 } 568 } 569 else if (index.lineIndex == 2) 570 { 571 if(i == _listSplitRect.Count) 572 { 573 continue; 574 } 575 else 576 { 577 lastRect = rect; 578 haveLast = true; 579 } 580 } 581 else { Debug.Assert(false); } 582 } 583 else 584 { 585 listSplitRectNew.Add(rect); 586 } 587 } 588 589 _listSplitRect = listSplitRectNew; 590 return del; 591 } 592 593 public static int GetSplitSize(int widthSrc, int heightSrc, int startX, int startY, 594 int widthDest, int heightDest, ref List<Rectangle> listOut) 595 { 596 listOut = new List<Rectangle>(); 597 598 int width = Math.Min(widthSrc - startX, widthDest); 599 600 int i = 0; 601 bool stop = false; 602 while (!stop) 603 { 604 Rectangle rect = new Rectangle(); 605 606 rect.X = startX; 607 rect.Y = startY + (i * heightDest); 608 rect.Width = width; 609 rect.Height = heightDest; 610 if (rect.Y + rect.Height >= heightSrc) 611 { 612 stop = true; 613 rect.Height = heightSrc - rect.Y; 614 } 615 listOut.Add(rect); 616 i++; 617 } 618 return 0; 619 } 620 }
图像快速切割
图像切割其实就是在一个内存中重新绘制,再将内存中的数据保存到文件。切割代码如下:
。。。
技术交流联系qq 13712486
专注C#、C++。擅长WPF、WinForm、QT等技术。
研究ofd多年,开发了一些列产品。
技术交流QQ群:565438497。