本人业余时间开发了一个图片切割工具,非常好用,也很灵活!

特别对大型图片切割,更能体现出该软件的优势!

功能说明

可以设定切割的高度和宽度。切割线可以上下拖动,可以增加一个切割区域,可设定某个区域不参与切割。

主要技术点分析

切割区域确定

每个切割区域是一个长方形。用一个结构标识该属性。

 class SpliteMoveIndex
{
public enum EN_DIR
{
NON,
HORIZONTAL,
VERTICAL
};
public EN_DIR direct = EN_DIR.NON;//0 无;1 水平;2垂直
public int rectIndex; //第几个rect
public int lineIndex; //第几个线 1:上或左;2 下或右
public int mouseX;
public int mouseY; public static SpliteMoveIndex CreateNon(int x, int y)
{
SpliteMoveIndex _nonIndex = new SpliteMoveIndex();
_nonIndex.direct = EN_DIR.NON;
_nonIndex.SetMouse(x, y);
return _nonIndex;
} public SpliteMoveIndex()
{ }
public SpliteMoveIndex(int x, int y)
{
SetMouse(x, y);
} public bool IsSameLine(SpliteMoveIndex another)
{
return this.direct == another.direct
&& this.rectIndex == another.rectIndex
&& this.lineIndex == another.lineIndex;
} public bool IsIn()
{
return direct != EN_DIR.NON;
}
public bool IsHorIn()
{
return direct == EN_DIR.HORIZONTAL;
}
public bool IsVertIn()
{
return direct == EN_DIR.VERTICAL;
} public void SetMouse(int x, int y)
{
mouseX = x;
mouseY = y;
}
}

SpliteRectGroup 负责组合这些长方形。当有鼠标移动时,动态调整这些长方形大小,再重画!

  class SpliteRectGroup
{
List<Rectangle> _listSplitRect = new List<Rectangle>();
int _widthSrc;
int _heightSrc; SpliteMoveIndex _lastMoveIndex = new SpliteMoveIndex(); public int _defaultHitSpace = ; int _moveAllFlagR = ;
bool _isMoveAll = false; //不参加切割的区域
List<int> _listSplitRectNotUsed = new List<int>(); public void SetRect(int widthSrc, int heightSrc, int startX, int startY,
int widthDest, int heightDest)
{
_widthSrc = widthSrc;
_heightSrc = heightSrc;
_listSplitRect.Clear(); GetSplitSize(_widthSrc, _heightSrc, startX, startY,
widthDest, heightDest, ref _listSplitRect);
} public List<Rectangle> GetRects()
{
return _listSplitRect;
} public List<Rectangle> GetRectsSplit()
{
List<Rectangle> listShow = new List<Rectangle>(); int i = ;
foreach(Rectangle rect in _listSplitRect)
{
if(IsRectUsed(i))
{
listShow.Add(rect);
}
i++;
}
return listShow;
} public int GetStartX()
{
if (_listSplitRect.Count == )
return ;
Rectangle first = _listSplitRect.First();
return first.X;
} public int GetSpliteWidth()
{
if (_listSplitRect.Count == )
return ;
Rectangle first = _listSplitRect.First();
return first.Width;
} public int GetSpliteTotalHeight()
{
if (_listSplitRect.Count == )
return ;
int i = ;
foreach (Rectangle r in _listSplitRect)
{
i += r.Height;
}
return i;
} public void SetMoveAllFlag(bool flag)
{
_isMoveAll = flag;
} public bool GetMoveAllFlag()
{
return _isMoveAll;
} public int GetStartY()
{
if (_listSplitRect.Count == )
return ;
Rectangle first = _listSplitRect.First();
return first.Y;
}
public void Draw(Graphics g)
{
SolidBrush brushRect = new SolidBrush(Color.FromArgb(, , , ));
Font strfont = new Font("Verdana", );
Brush strBrush = Brushes.Blue;
Brush strBrushBack = Brushes.White; //起点圆
int x = GetStartX();
int y = GetStartY();
g.FillEllipse(brushRect, x - _moveAllFlagR, y - _moveAllFlagR, * _moveAllFlagR, * _moveAllFlagR);
brushRect.Dispose();
//起点信息
string startInfo = string.Format("({0}:{1})",x,y);
SizeF sizeF = g.MeasureString(startInfo, strfont);
Point ptStart = new Point((int)(x-sizeF.Width/), (int)(y -sizeF.Height- _defaultHitSpace*) );
g.FillRectangle(strBrushBack, new RectangleF(ptStart, sizeF));
g.DrawString(startInfo, strfont, strBrush, ptStart); //画方框
Color backColor = Color.FromArgb(, Color.PowderBlue);
HatchBrush hat1 = new HatchBrush(HatchStyle.OutlinedDiamond, Color.DarkBlue, backColor);
HatchBrush hat2 = new HatchBrush(HatchStyle.OutlinedDiamond, Color.Red, backColor); //输出提示信息
Pen rectPen = Pens.Red;
int i = ;
int showIndex = ;
string info;
foreach (Rectangle rect in _listSplitRect)
{
i++;
bool used = IsRectUsed(rect);
if (used)
{
showIndex++;
info = string.Format("{0}-({1}:{2})", showIndex, rect.Width, rect.Height);
}
else
{
info = string.Format("({0}:{1})--不参与切割", rect.Width, rect.Height);
} g.DrawRectangle(rectPen, rect);
if (!used)
{
g.FillRectangle(hat1, rect);
} Point strStart = new Point(rect.X + , rect.Y + );
sizeF = g.MeasureString(info, strfont);
g.FillRectangle(strBrushBack, new RectangleF(strStart, sizeF)); g.DrawString(info, strfont, strBrush, strStart);
}
strfont.Dispose();
hat1.Dispose();
hat2.Dispose();
}
public bool StartPointMoveTo(int x, int y)
{
if (_listSplitRect.Count == )
return false; Rectangle first = _listSplitRect.First();
int moveX = x - first.X;
int moveY = y - first.Y; List<Rectangle> listSplitRectNew = new List<Rectangle>();
foreach (Rectangle r in _listSplitRect)
{
Rectangle tmp = r;
tmp.Offset(moveX, moveY);
listSplitRectNew.Add(tmp);
} _listSplitRect.Clear();
_listSplitRect = listSplitRectNew;
return true;
} public bool IsAllMove(int mouseX, int mouseY)
{
GraphicsPath myGraphicsPath = new GraphicsPath();
myGraphicsPath.Reset();
Region myRegion = new Region(); int x = GetStartX();
int y = GetStartY();
myGraphicsPath.AddEllipse(x - _moveAllFlagR, y - _moveAllFlagR, * _moveAllFlagR, * _moveAllFlagR);//points);
myRegion.MakeEmpty();
myRegion.Union(myGraphicsPath);
//返回判断点是否在多边形里
bool myPoint = myRegion.IsVisible(mouseX, mouseY);
return myPoint;
} public void ResetMoveFlag()
{
_lastMoveIndex.direct = SpliteMoveIndex.EN_DIR.NON;
} public bool SetMove(SpliteMoveIndex index)
{
//移动到区域外
if (!index.IsIn())
{
_lastMoveIndex = index;
return false;
} //不是同一条线
if (!_lastMoveIndex.IsSameLine(index))
{
_lastMoveIndex = index;
return false;
} //移动到新的区域
MoveRect(_lastMoveIndex, index);
_lastMoveIndex = index;
return true;
} public bool IsInSplite()
{
if (_lastMoveIndex == null)
return false;
return _lastMoveIndex.IsIn();
} void MoveRect(SpliteMoveIndex last, SpliteMoveIndex now)
{
if (last.IsHorIn())
{
MoveRectHor(last, now);
}
else if (last.IsVertIn())
{
MoveRectVert(last, now);
}
} void MoveRectHor(SpliteMoveIndex last, SpliteMoveIndex now)
{
int moveY = now.mouseY - last.mouseY;
List<Rectangle> listSplitRectNew = new List<Rectangle>();
int i = ;
int find = ;
foreach (Rectangle r in _listSplitRect)
{
Rectangle tmp = r;
i++;
if (find == )
{
listSplitRectNew.Add(tmp);
continue;
}
if (find == )
{
tmp.Y += moveY;
tmp.Height -= moveY;
find = ;
listSplitRectNew.Add(tmp);
continue;
} if (i == last.rectIndex)
{
if (last.lineIndex == )
{
tmp.Y += moveY;
tmp.Height -= moveY;
find = ;
listSplitRectNew.Add(tmp);
}
else if (last.lineIndex == )
{
tmp.Height += moveY;
find = ;
listSplitRectNew.Add(tmp);
}
}
else
{
listSplitRectNew.Add(tmp);
}
} _listSplitRect.Clear();
_listSplitRect = listSplitRectNew;
} void MoveRectVert(SpliteMoveIndex last, SpliteMoveIndex now)
{
int moveX = now.mouseX - last.mouseX;
List<Rectangle> listSplitRectNew = new List<Rectangle>();
int i = ;
foreach (Rectangle r in _listSplitRect)
{
Rectangle tmp = r;
i++;
if (last.lineIndex == )
{
tmp.X += moveX;
tmp.Width -= moveX;
listSplitRectNew.Add(tmp);
}
else if (last.lineIndex == )
{
tmp.Width += moveX;
listSplitRectNew.Add(tmp);
}
} _listSplitRect.Clear();
_listSplitRect = listSplitRectNew;
} SpliteMoveIndex GetHorizontal(int x, int y, int hitSpace)
{
int startX = GetStartX();
int width = GetSpliteWidth();
if (x < startX || x > (startX + width))
return SpliteMoveIndex.CreateNon(x, y); int i = ;
foreach (Rectangle rect in _listSplitRect)
{
i++;
int y1 = rect.Y;
//是否落在水平线 一定范围内
if (y >= y1 - hitSpace && y <= (y1 + hitSpace))
{
SpliteMoveIndex moveIndex = new SpliteMoveIndex(x, y);
moveIndex.direct = SpliteMoveIndex.EN_DIR.HORIZONTAL;
moveIndex.rectIndex = i;
moveIndex.lineIndex = ;
return moveIndex;
} int y2 = rect.Y + rect.Height;
if (y >= (y2 - hitSpace) && y <= (y2 + hitSpace))
{
SpliteMoveIndex moveIndex = new SpliteMoveIndex(x, y);
moveIndex.direct = SpliteMoveIndex.EN_DIR.HORIZONTAL;
moveIndex.rectIndex = i;
moveIndex.lineIndex = ;
return moveIndex;
}
} return SpliteMoveIndex.CreateNon(x, y);
} SpliteMoveIndex GetVectical(int x, int y, int hitSpace)
{
int startY = GetStartY();
if (y < startY || y > (startY + _heightSrc))
return SpliteMoveIndex.CreateNon(x, y); int i = ;
foreach (Rectangle rect in _listSplitRect)
{
i++;
//是否落在垂直线 一定范围内
if (y >= rect.Y && y <= (rect.Y + rect.Height))
{
int x1 = rect.X;
if (x >= (x1 - hitSpace) && x <= (x1 + hitSpace))
{
SpliteMoveIndex moveIndex = new SpliteMoveIndex(x, y);
moveIndex.direct = SpliteMoveIndex.EN_DIR.VERTICAL;
moveIndex.rectIndex = i;
moveIndex.lineIndex = ;
return moveIndex;
} int x2 = rect.X + rect.Width;
if (x >= (x2 - hitSpace) && x <= (x2 + hitSpace))
{
SpliteMoveIndex moveIndex = new SpliteMoveIndex(x, y);
moveIndex.direct = SpliteMoveIndex.EN_DIR.VERTICAL;
moveIndex.rectIndex = i;
moveIndex.lineIndex = ;
return moveIndex;
}
}
} return SpliteMoveIndex.CreateNon(x, y);
} public SpliteMoveIndex PointHit(int x, int y, int hitSpace)
{
//判断是否在水平线
SpliteMoveIndex hRect = GetHorizontal(x, y, hitSpace);
if (hRect.IsIn())
return hRect; //判断是否在垂直线
SpliteMoveIndex vRect = GetVectical(x, y, hitSpace);
if (vRect.IsIn())
return vRect; return SpliteMoveIndex.CreateNon(x, y);
} public bool PointInRect(int x,int y)
{
int startX = GetStartX();
int width = GetSpliteWidth();
if (x < startX || x > (startX + width))
return false; int startY = GetStartY();
int heght = GetSpliteTotalHeight();
if (y < startY || y > (startY + heght))
return false;
return true;
} public void ClearNotUsedRect()
{
_listSplitRectNotUsed.Clear();
} public bool GetRectIndex(int index,ref Rectangle outRect)
{
int i = ;
foreach (Rectangle rect in _listSplitRect)
{
if (i == index)
{
outRect = rect;
return true;
}
i++;
}
return false;
} public bool IsNotUsed(int x, int y)
{
Rectangle rect = new Rectangle();
foreach (int n in _listSplitRectNotUsed)
{
if (GetRectIndex(n, ref rect) && rect.Contains(x, y))
{
return true;
}
}
return false;
} public bool IsRectUsed(Rectangle rect)
{
Rectangle rectNot = new Rectangle();
foreach (int n in _listSplitRectNotUsed)
{
if (GetRectIndex(n, ref rectNot) && rectNot == rect)
{
return false;
}
}
return true;
} public bool IsRectUsed(int index)
{
foreach (int n in _listSplitRectNotUsed)
{
if (n == index)
return false;
}
return true;
} //区块加入切割
public bool AddRectUsed(int x,int y)
{
int i = ;
Rectangle rectNot = new Rectangle();
foreach (int n in _listSplitRectNotUsed)
{
if (GetRectIndex(n, ref rectNot) && rectNot.Contains(x,y))
{
_listSplitRectNotUsed.RemoveAt(i);
return true;
}
i++;
}
return false;
} //区块不加入切割
public bool DelRectUsed(int x, int y)
{
int i = ;
foreach (Rectangle rect in _listSplitRect)
{
if (rect.Contains(x, y))
{
_listSplitRectNotUsed.Add(i);
return true;
}
i++;
}
return false;
} public bool AddHorLine(int x, int y)
{
List<Rectangle> listSplitRectNew = new List<Rectangle>();
foreach (Rectangle rect in _listSplitRect)
{
if (y > rect.Y && y < rect.Y + rect.Height)
{
Rectangle r1 = new Rectangle(rect.Location, rect.Size);
r1.Height = y - rect.Y;
listSplitRectNew.Add(r1); r1.Y = y ;
r1.Height = (rect.Y + rect.Height - y);
listSplitRectNew.Add(r1);
}
else
{
listSplitRectNew.Add(rect);
}
}
_listSplitRect = listSplitRectNew;
return true;
} //删除水平线
public bool DeleteHorSplite(SpliteMoveIndex index)
{
List<Rectangle> listSplitRectNew = new List<Rectangle>();
int i = ;
bool del = false;
Rectangle lastRect = new Rectangle();
bool haveLast = false; foreach (Rectangle rect in _listSplitRect)
{
i++;
if(haveLast)
{
haveLast = false;
lastRect.Height += rect.Height;
listSplitRectNew.Add(lastRect);
continue;
} if(index.rectIndex == i)
{
del = true;
if (index.lineIndex == )
{
if(listSplitRectNew.Count == )
{
continue;
}
else
{
Rectangle r = listSplitRectNew.Last();
r.Height += rect.Height;
listSplitRectNew.RemoveAt(listSplitRectNew.Count-);
listSplitRectNew.Add(r);
}
}
else if (index.lineIndex == )
{
if(i == _listSplitRect.Count)
{
continue;
}
else
{
lastRect = rect;
haveLast = true;
}
}
else { Debug.Assert(false); }
}
else
{
listSplitRectNew.Add(rect);
}
} _listSplitRect = listSplitRectNew;
return del;
} public static int GetSplitSize(int widthSrc, int heightSrc, int startX, int startY,
int widthDest, int heightDest, ref List<Rectangle> listOut)
{
listOut = new List<Rectangle>(); int width = Math.Min(widthSrc - startX, widthDest); int i = ;
bool stop = false;
while (!stop)
{
Rectangle rect = new Rectangle(); rect.X = startX;
rect.Y = startY + (i * heightDest);
rect.Width = width;
rect.Height = heightDest;
if (rect.Y + rect.Height >= heightSrc)
{
stop = true;
rect.Height = heightSrc - rect.Y;
}
listOut.Add(rect);
i++;
}
return ;
}
}

图像快速切割

图像切割其实就是在一个内存中重新绘制,再将内存中的数据保存到文件。切割代码如下:

 。。。

技术交流联系qq 13712486

一个非常好用的图片切割工具(c# winform开发)的更多相关文章

  1. 一个非常好用的图片切割工具(c# winform开发) 附源码

    本人业余时间开发了一个图片切割工具,非常好用,也很灵活! 特别对大型图片切割,更能体现出该软件的优势! 开发工具为winform,源码下载地址:http://download.csdn.net/dow ...

  2. ShoeBox一个超级好用的图片切割工具

    下载地址:http://renderhjs.net/shoebox/ ShoeBox是一个图片处理软件,体积很小. 我主要用第三个功能拆开图片.根据大图上的小图空白间隙来处理的. 导出后变成很多小图

  3. 一个web图片热点生成工具(winform开发) 附源码

    给图片加热点是web开发中经常用到的一个功能.这方面的工具也不少. 为了更好的满足自己的需求,写了一个winform程序. 可以方便的给图片加热点,更方便灵活! 源码下载 http://downloa ...

  4. 图片切割工具---产生多个div切割图片 采用for和一的二维阵列设置背景位置

    照片库 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveGlhb21vZ2c=/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...

  5. 网络请求以及网络请求下载图片的工具类 android开发java工具类

    package cc.jiusan.www.utils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; ...

  6. Android中将一个图片切割成多个图片

    有种场景,我们想将一个图片切割成多个图片.比如我们在开发一个拼图的游戏,就首先要对图片进行切割. 以下是封装好的两个类,可以实现图片的切割.仅供参考和学习. 一个是ImagePiece类,此类保存了一 ...

  7. Android中将一个图片切割成多个图片[转]

    有种场景,我们想将一个图片切割成多个图片.比如我们在开发一个拼图的游戏,就首先要对图片进行切割. 以下是封装好的两个类,可以实现图片的切割.仅供参考和学习. 一个是ImagePiece类,此类保存了一 ...

  8. 转: ImageMagick 命令行的图片处理工具(客户端与服务器均可用)

    http://www.imagemagick.com.cn/ 关于ImageMagick ImageMagick (TM) 是一个免费的创建.编辑.合成图片的软件.它可以读取.转换.写入多种格式的图片 ...

  9. Java操作图片的工具类

    操作图片的工具类: import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Font; import java.a ...

随机推荐

  1. 反编译 轻松调频 Android APP 下载“飞鱼秀”录音

    经常听“飞鱼秀”,但是由于时间的原因,只能听回放,但是轻松调频的APP做的有点儿... 听回放的时候经常会中断,还不能拖动进度条,就决定把录音下载下来听. 1.反编译apk(Android反编译过程见 ...

  2. 【PAT Advanced Level】1015. Reversible Primes (20)

    转换进制&&逆序可以在一起进行,有一点技巧,不要用十进制数来表示低进制,容易溢出. #include <iostream> #include <vector> ...

  3. Javascript设计模式理论与实战:状态模式

    在软件开发中,很大部分时候就是操作数据,而不同数据下展示的结果我们将其抽象出来称为状态,我们平时开发时本质上就是对应用程序的各种状态进行切换并作出相应处理.状态模式就是一种适合多种状态场景下的设计模式 ...

  4. CodeFirst迁移注意点

    Context构造函数不检查__MigrationHistory 取消当数据库模型发生改变时删除当前数据库重建新数据库的设置.Database.SetInitializer<Context> ...

  5. 深入理解Aspnet Core之Identity(3)

    主题 账户管理一个比较常见的功能就是密码强度策略,Identity已经内置了一个通用的可配置的策略,我们一般情况下可以直接拿来用即可.本篇我会介绍一些Identity内置的密码策略类:Password ...

  6. [.net]基元线程同步构造

    /* 基元线程同步构造 用户模式构造: 易变构造(Volatile Construct) 互锁构造(Interlocked Construct):自旋锁(Spinlock) 乐观锁(Optimisti ...

  7. docker rpm 下载地址

    http://yum.dockerproject.org/repo/main/centos/7/Packages/

  8. Comparable接口——容器中自定义类排序

    1.容器TreeMap,默认根据Key对象中某个属性的从小到大排列元素. (1)如下代码示例,Key是整型数字,所以按照其从小到大的顺序排列 public class TestTreeMap { pu ...

  9. Atcoder Tenka1 Programmer Contest 2019题解

    传送门 \(C\ Stones\) 最后肯定形如左边一段白+右边一段黑,枚举一下中间的断点,预处理一下前缀和就可以了 int main(){ // freopen("testdata.in& ...

  10. 萝卜保卫战3内购破解+Toast窗口增加(Love版)

    涉及到一些不同的破解的方法,以及不同的破解思路,还有一些重要权限的删除等. 作者:HAI_ 这次目标是经常玩的萝卜保卫战,不知不觉,已经更新到3了.详细分析请参考https://bbs.ichunqi ...