看了不少图片旋转的帖子,旋转是可以的, 但是都是原始的图片旋转。但是我的情况是:图片放置在一个长方形的外框里面,外框的长宽是固定的,图片只能在框里面旋转。如果按照原始图像旋转的话,就会出现图像被截断没法显示的情况(初始的时候图像的长度等于外框的长宽,比如当图像转了90度,刚好垂直于外框的时候,图像明显长于外框的宽度,就显示不全了)。请问,在旋转的时候,怎么根据旋转的角度,计算当前图片的长宽呢?实时的改变图片的大小。(其实就是计算在当前旋转角度下,落在外框中最大的图片矩形,并保持原有的长宽比例)。请大家给于指点。谢谢
public static Bitmap GenThumbnail(Bitmap imageFrom, int width, int height)
{
// 源图宽度及高度
int imageFromWidth = imageFrom.Width;
int imageFromHeight = imageFrom.Height;
// 生成的缩略图实际宽度及高度
if (width >= imageFromWidth && height >= imageFromHeight)
{
return imageFrom;
}
else
{
// 生成的缩略图在上述"画布"上的位置
int X = 0;
int Y = 0; decimal wpercent = (decimal)width / imageFromWidth;
decimal hpercent = (decimal)height / imageFromHeight;
if (wpercent > hpercent)
{
width = (int)(imageFromWidth * hpercent);
}
else if (wpercent < hpercent)
{
height = (int)(imageFromHeight * wpercent);
} // 创建画布
using (Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb))
{
bmp.SetResolution(imageFrom.HorizontalResolution, imageFrom.VerticalResolution);
using (Graphics g = Graphics.FromImage(bmp))
{ // 用白色清空
g.Clear(Color.White); // 指定高质量的双三次插值法。执行预筛选以确保高质量的收缩。此模式可产生质量最高的转换图像。
g.InterpolationMode = InterpolationMode.HighQualityBicubic; // 指定高质量、低速度呈现。
g.SmoothingMode = SmoothingMode.HighQuality; // 在指定位置并且按指定大小绘制指定的 Image 的指定部分。
g.DrawImage(imageFrom, new Rectangle(X, Y, width, height), new Rectangle(0, 0, imageFromWidth, imageFromHeight), GraphicsUnit.Pixel); return bmp;
}
}
}
}
Dim OldImgSize As Size = _Bitmap1.Size
Try
If _LargeEmptyBP IsNot Nothing Then _LargeEmptyBP.Dispose()
_LargeEmptyBP = New Bitmap(1000, 1000) '定义足够大的绘制区域,以便容纳旋转后的图像
Dim Rct As RectangleF
Using G As Graphics = Graphics.FromImage(_LargeEmptyBP)
Dim Gs As Drawing2D.GraphicsState = Nothing
Gs = G.Save
G.TranslateTransform(500, 500)
G.RotateTransform(NumericUpDown1.Value) '旋转的角度
G.DrawImage(_Bitmap1, New Rectangle(-OldImgSize.Width / 2, -OldImgSize.Height / 2, OldImgSize.Width, OldImgSize.Height), New Rectangle(0, 0, OldImgSize.Width, OldImgSize.Height), GraphicsUnit.Pixel)
G.Restore(Gs)
End Using
Rct = GetMinRect(_LargeEmptyBP) '获取经过裁切后的最小矩形图形范围
Dim WW, HH As Integer
WW = Rct.Width : HH = Rct.Height
If B1 IsNot Nothing Then B1.Dispose()
B1 = New Bitmap(WW, HH)
Using G As Graphics = Graphics.FromImage(B1)
G.DrawImage(_LargeEmptyBP, New Rectangle(0, 0, Rct.Width, Rct.Height), Rct, GraphicsUnit.Pixel)
End Using
If B2 IsNot Nothing Then B2.Dispose()
B2 = New Bitmap(PicBx.Width, PicBx.Height)
Using G As Graphics = Graphics.FromImage(B2)
G.Clear(Color.White)
Dim CompWith As Double = 0
Dim DrawX, DrawY As Integer
'计算适当缩放比例
If PicBx.Width <= Rct.Width Then CompWith = PicBx.Width / Rct.Width
If PicBx.Height <= Rct.Height Then CompWith = PicBx.Height / Rct.Height
CompWith = IIf(CompWith = 0, 1, CompWith)
DrawX = (PicBx.Width - Rct.Width * CompWith) / 2
DrawY = (PicBx.Height - Rct.Height * CompWith) / 2
G.DrawImage(B1, New Rectangle(DrawX, DrawY, Rct.Width * CompWith, Rct.Height * CompWith), New Rectangle(0, 0, Rct.Width, Rct.Height), GraphicsUnit.Pixel)
End Using
PicBx.BackgroundImage = B2
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub ''' <summary>
''' 获取经裁剪后的最小容纳图形区域
''' </summary>
''' <param name="B"></param>
''' <returns></returns>
''' <res></res>
Private Function GetMinRect(ByVal B As Bitmap) As Rectangle
Dim XX, YY, WW, HH, _W, _H As Integer
Dim bmpDATA As New BitmapData
bmpDATA = B.LockBits(New Rectangle(0, 0, B.Width - 1, B.Height - 1), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb)
_W = bmpDATA.Stride : _H = bmpDATA.Height
Dim BTS(bmpDATA.Stride * bmpDATA.Height) As Byte
Runtime.InteropServices.Marshal.Copy(bmpDATA.Scan0, BTS, 0, BTS.Length - 1)
B.UnlockBits(bmpDATA)
Dim t1, t2, t3, t4 As Integer
t1 = BTS(0) : t2 = BTS(1) : t3 = BTS(2) : t4 = BTS(3)
Dim LastIndex As Integer = 0
XX = -1 : YY = -1
For I As Integer = 0 To _H - 1
For J As Integer = 0 To _W - 4 Step 4
LastIndex = I * _W + J
If BTS(LastIndex) <> t1 OrElse BTS(LastIndex + 1) <> t2 OrElse BTS(LastIndex + 2) <> t3 OrElse BTS(LastIndex + 3) <> t4 Then
If YY = -1 Then YY = I
If XX = -1 Then
XX = J
Else
XX = Math.Min(XX, J)
End If
End If
Next
Next
WW = -1 : HH = -1
For I As Integer = _H - 1 To 0 Step -1
For J As Integer = _W - 4 To 0 Step -4
LastIndex = I * _W + J
If BTS(LastIndex) <> t1 OrElse BTS(LastIndex + 1) <> t2 OrElse BTS(LastIndex + 2) <> t3 OrElse BTS(LastIndex + 3) <> t4 Then
If HH = -1 Then HH = I
If WW = -1 Then
WW = J
Else
WW = Math.Max(WW, J)
End If
End If
Next
Next
Return New Rectangle(XX / 4, YY, (WW - XX) / 4, HH - YY)
End Function
Private _Bitmap1 As New Bitmap("C:\Users\Administrator\Desktop\新建文件夹\bullet bil.png")
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load VB不懂的可以在线转换成C#代码。
如果可以的话,那麻烦你转个c#代码, 这个问题拖得比较久了,我从来没有用过vb,要转化成c#的话,要花不少时间。再次感谢
也可以不这么做(画图后再用像素找端点,一个是速度慢了,一个是有边角颜色的局限,不能是画布颜色)。
C# Drawing2D库已经提供了Matrix类,可以简单的来判断旋转的新顶点:
Matrix matrix = new Matrix();
matrix.Rotate(angle);
matrix.TransformPoints(corners);
用新顶点就可以算出需要缩放的比例了。
public partial class Form1 : Form
{
Bitmap bmp = new Bitmap("c:\\temp\\penguins.jpg");
Rectangle boundingBox = new Rectangle(50, 50, 200, 150);
PointF center = new PointF(150, 125);
float angle, lastAngle, baseAngle, scale = 1.0f; public Form1()
{
InitializeComponent();
this.DoubleBuffered = true;
}
protected override void OnMouseDown(MouseEventArgs e)
{
lastAngle = angle;
baseAngle = (float)(Math.Atan2(e.Y - center.Y, e.X - center.X) * 180 / Math.PI) + 90;
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (this.Capture == false) return; float currentAngle = (float)(Math.Atan2(e.Y - center.Y, e.X - center.X) * 180 / Math.PI) + 90;
angle = lastAngle + (currentAngle - baseAngle); float halfWidth = boundingBox.Width / 2.0f, halfHeight = boundingBox.Height / 2.0f;
PointF[] corners = { new PointF(-halfWidth, halfHeight), new PointF(halfWidth, halfHeight) };
Matrix matrix = new Matrix(); matrix.Rotate(angle);
matrix.TransformPoints(corners); float bleedX = Math.Max(Math.Abs(corners[0].X), Math.Abs(corners[1].X));
float bleedY = Math.Max(Math.Abs(corners[0].Y), Math.Abs(corners[1].Y));
scale = Math.Min(halfWidth / bleedX, halfHeight / bleedY); this.Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawRectangle(Pens.Red, boundingBox.X-1, boundingBox.Y-1, boundingBox.Width+1, boundingBox.Height + 1);
e.Graphics.TranslateTransform(center.X, center.Y);
e.Graphics.RotateTransform(angle);
e.Graphics.ScaleTransform(scale, scale);
e.Graphics.TranslateTransform(-center.X, -center.Y);
e.Graphics.DrawImage(bmp, boundingBox);
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Drawing.Imaging;
public class Form1
{
private Bitmap _LargeEmptyBP;
private Bitmap B1;
private Bitmap B2;
private Bitmap _Bitmap1 = new Bitmap("C:\\Users\\Administrator\\Desktop\\新建文件夹\\bullet bil.png"); private void Form1_Load(System.Object sender, System.EventArgs e)
{
} private void NumericUpDown1_ValueChanged(System.Object sender, System.EventArgs e)
{
Size OldImgSize = _Bitmap1.Size;
try {
if (_LargeEmptyBP != null)
_LargeEmptyBP.Dispose();
_LargeEmptyBP = new Bitmap(1000, 1000);
//定义足够大的绘制区域,以便容纳旋转后的图像
RectangleF Rct = default(RectangleF);
using (Graphics G = Graphics.FromImage(_LargeEmptyBP)) {
Drawing2D.GraphicsState Gs = null;
Gs = G.Save;
G.TranslateTransform(500, 500);
G.RotateTransform(NumericUpDown1.Value);
//旋转的角度
G.DrawImage(_Bitmap1, new Rectangle(-OldImgSize.Width / 2, -OldImgSize.Height / 2, OldImgSize.Width, OldImgSize.Height), new Rectangle(0, 0, OldImgSize.Width, OldImgSize.Height), GraphicsUnit.Pixel);
G.Restore(Gs);
}
Rct = GetMinRect(_LargeEmptyBP);
//获取经过裁切后的最小矩形图形范围
int WW = 0;
int HH = 0;
WW = Rct.Width;
HH = Rct.Height;
if (B1 != null)
B1.Dispose();
B1 = new Bitmap(WW, HH);
using (Graphics G = Graphics.FromImage(B1)) {
G.DrawImage(_LargeEmptyBP, new Rectangle(0, 0, Rct.Width, Rct.Height), Rct, GraphicsUnit.Pixel);
}
if (B2 != null)
B2.Dispose();
B2 = new Bitmap(PicBx.Width, PicBx.Height);
using (Graphics G = Graphics.FromImage(B2)) {
G.Clear(Color.White);
double CompWith = 0;
int DrawX = 0;
int DrawY = 0;
//计算适当缩放比例
if (PicBx.Width <= Rct.Width)
CompWith = PicBx.Width / Rct.Width;
if (PicBx.Height <= Rct.Height)
CompWith = PicBx.Height / Rct.Height;
CompWith = (CompWith == 0 ? 1 : CompWith);
DrawX = (PicBx.Width - Rct.Width * CompWith) / 2;
DrawY = (PicBx.Height - Rct.Height * CompWith) / 2;
G.DrawImage(B1, new Rectangle(DrawX, DrawY, Rct.Width * CompWith, Rct.Height * CompWith), new Rectangle(0, 0, Rct.Width, Rct.Height), GraphicsUnit.Pixel);
}
PicBx.BackgroundImage = B2;
} catch (Exception ex) {
Console.WriteLine(ex.Message);
}
} /// <summary>
/// 获取经裁剪后的最小容纳图形区域
/// </summary>
/// <param name="B"></param>
/// <returns></returns>
/// <res></res>
private Rectangle GetMinRect(Bitmap B)
{
int XX = 0;
int YY = 0;
int WW = 0;
int HH = 0;
int _W = 0;
int _H = 0;
BitmapData bmpDATA = new BitmapData();
bmpDATA = B.LockBits(new Rectangle(0, 0, B.Width - 1, B.Height - 1), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
_W = bmpDATA.Stride;
_H = bmpDATA.Height;
byte[] BTS = new byte[bmpDATA.Stride * bmpDATA.Height + 1];
System.Runtime.InteropServices.Marshal.Copy(bmpDATA.Scan0, BTS, 0, BTS.Length - 1);
B.UnlockBits(bmpDATA);
int t1 = 0;
int t2 = 0;
int t3 = 0;
int t4 = 0;
t1 = BTS[0];
t2 = BTS[1];
t3 = BTS[2];
t4 = BTS[3];
int LastIndex = 0;
XX = -1;
YY = -1;
for (int I = 0; I <= _H - 1; I++) {
for (int J = 0; J <= _W - 4; J += 4) {
LastIndex = I * _W + J;
if (BTS[LastIndex] != t1 || BTS[LastIndex + 1] != t2 || BTS[LastIndex + 2] != t3 || BTS[LastIndex + 3] != t4) {
if (YY == -1)
YY = I;
if (XX == -1) {
XX = J;
} else {
XX = Math.Min(XX, J);
}
}
}
}
WW = -1;
HH = -1;
for (int I = _H - 1; I >= 0; I += -1) {
for (int J = _W - 4; J >= 0; J += -4) {
LastIndex = I * _W + J;
if (BTS[LastIndex] != t1 || BTS[LastIndex + 1] != t2 || BTS[LastIndex + 2] != t3 || BTS[LastIndex + 3] != t4) {
if (HH == -1)
HH = I;
if (WW == -1) {
WW = J;
} else {
WW = Math.Max(WW, J);
}
}
}
}
return new Rectangle(XX / 4, YY, (WW - XX) / 4, HH - YY);
} private void Form1_MouseDoubleClick(object sender, System.Windows.Forms.MouseEventArgs e)
{
Bitmap B = new Bitmap(this.Width, this.Height);
int J = 0;
for (int I = 0; I <= 360; I += 5) {
this.NumericUpDown1.Value = I;
if (B != null)
B.Dispose();
B = new Bitmap(this.Width, this.Height);
this.DrawToBitmap(B, new Rectangle(0, 0, this.Width, this.Height));
J += 1;
B.Save("C:\\Users\\Administrator\\Desktop\\新建文件夹\\A\\" + "A" + J + ".jpg", ImageFormat.Jpeg);
}
}
public Form1()
{
MouseDoubleClick += Form1_MouseDoubleClick;
Load += Form1_Load;
}
}
1、图片上套一个框,让框转这样保证图片不失真,(如果是WEB程序可以用脚本实现)。
2、转动的时候动态运算,做压缩和放大处理,这样多少会有失真情况。
动态压缩放大代码:
/// <summary>
/// 高质量缩略图
/// </summary>
/// <param name="maxWidth"></param>
/// <param name="maxHeight"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns></returns>
private static Size NewSize( int maxWidth,int maxHeight, int width,int height )
{
double w = 0.0;
double h = 0.0;
double sw = Convert.ToDouble(width);
double sh = Convert.ToDouble(height);
double mw = Convert.ToDouble(maxWidth);
double mh = Convert.ToDouble(maxHeight);
if (sw < mw && sh < mh)
{
w = sw;
h = sh;
}
else if ((sw/sh) > (mw/mh))
{
w = maxWidth;
h = (w * sh)/sw;
}
else
{
h = maxHeight;
w = (h * sw)/sh;
}
return new Size( Convert.ToInt32(w),Convert.ToInt32(h));
} private static void Madefile(Bitmap info)
{
int tw =500;
int th =400;
string fname ="kkkkkk.jpg";
if(true)
{
System.Drawing.Image img = (System.Drawing.Image)info;
System.Drawing.Imaging.ImageFormat thisFormat = img.RawFormat;
Size newSize = NewSize( tw, th, img.Width, img.Height );
Bitmap outBmp = new Bitmap( newSize.Width, newSize.Height );
Graphics g = Graphics.FromImage( outBmp );
// 设置画布的描绘质量
g.CompositingQuality = CompositingQuality.HighQuality;
g.SmoothingMode = SmoothingMode.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage( img, new Rectangle( 0, 0, newSize.Width, newSize.Height ),0, 0, img.Width, img.Height, GraphicsUnit.Pixel );
g.Dispose( );
// 以下代码为保存图片时,设置压缩质量
EncoderParameters encoderParams = new EncoderParameters( );
long[] quality = new long[1];
quality[0] = 100;
EncoderParameter encoderParam = new EncoderParameter( System.Drawing.Imaging.Encoder.Quality, quality );
encoderParams.Param[0] = encoderParam;
//获得包含有关内置图像编码解码器的信息的ImageCodecInfo 对象.
ImageCodecInfo[] arrayICI = ImageCodecInfo.GetImageEncoders( );
ImageCodecInfo jpegICI = null;
for ( int x = 0;x < arrayICI.Length;x++ )
{
if ( arrayICI[x].FormatDescription.Equals( "JPEG" ) )
{
jpegICI = arrayICI[x];
//设置JPEG编码
break;
}
}
if ( jpegICI != null )
{
outBmp.Save( "C:/"+fname, jpegICI, encoderParams );
}
img.Dispose( );
outBmp.Dispose( );
}
}
mat4 matRotate;
matRotate.rotateZ(fAngle);
for(int i=0; i<4; ++i)
{
vDests[i] = MatRotate * vSrc[i];
}vec2 vMax = FindMax(vDest, 4);
vec2 vMin = FindMin(vDest, 4);
vec2 vScale = vec2((vMax.x - vMin.x)/fWidth, (vMax.y - vMin.y)/fHeight);
mat4 matScale;
matScale.scale(vScale, 1.0f);
for(int i=0; i<4; ++i)
{
vDests[i] = MatScale * Dests[i];
}
//投影,光栅化得到最终图像,over,so easy....
{
// 源图宽度及高度
int imageFromWidth = imageFrom.Width;
int imageFromHeight = imageFrom.Height;
// 生成的缩略图实际宽度及高度
if (width >= imageFromWidth && height >= imageFromHeight)
{
return imageFrom;
}
else
{
// 生成的缩略图在上述"画布"上的位置
int X = 0;
int Y = 0; decimal wpercent = (decimal)width / imageFromWidth;
decimal hpercent = (decimal)height / imageFromHeight;
if (wpercent > hpercent)
{
width = (int)(imageFromWidth * hpercent);
}
else if (wpercent < hpercent)
{
height = (int)(imageFromHeight * wpercent);
} // 创建画布
using (Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb))
{
bmp.SetResolution(imageFrom.HorizontalResolution, imageFrom.VerticalResolution);
using (Graphics g = Graphics.FromImage(bmp))
{ // 用白色清空
g.Clear(Color.White); // 指定高质量的双三次插值法。执行预筛选以确保高质量的收缩。此模式可产生质量最高的转换图像。
g.InterpolationMode = InterpolationMode.HighQualityBicubic; // 指定高质量、低速度呈现。
g.SmoothingMode = SmoothingMode.HighQuality; // 在指定位置并且按指定大小绘制指定的 Image 的指定部分。
g.DrawImage(imageFrom, new Rectangle(X, Y, width, height), new Rectangle(0, 0, imageFromWidth, imageFromHeight), GraphicsUnit.Pixel); return bmp;
}
}
}
}
移植性比较高