3.1 坐标系统
GDI+定义了三种形式的坐标系统:世界坐标系统,页面坐标系统和设备坐标系统。
测量相对于文档区域左上角的位置和测量相对于屏幕(桌面)左上角的位置之间的区别非常重要,GDI+为它们指定了不同的名称:
- 世界坐标:用来绘制自然模型的坐标(以像素为单位)。Graphics.Draw*使用的坐标就是该坐标系下的坐标。
- 页面坐标:要测量的点距离客户区域左上角的位置(以像素为单位)。
- 设备坐标:设备坐标类似于页面坐标,但其测量单位不一定是像素,而是用户通过调用Graphics.PageUnit属性指定的单位。
GDI+在设备绘图表面(如屏幕,打印机)绘制图形之前,图形的坐标系统要经过几步变换
- 第一步变换世界坐标到页面坐标,这一变换过程称为世界变换。
- 第二步变换页面坐标到设备坐标,这一变换过程称为页面变换。设备坐标代表绘制的图形时怎样显示在设备上的。
在GDI+中,所有三种坐标系统的起始原点均是客户区左上角原点(0,0)。当画一条从点A(0,0)到点B(120,80)的直线时,该直线的起始点是左上角X方向的0像素点和Y方向的0像素点,终点是X方向的120像素点和Y方向的80像素点。以下程序展示了应用世界坐标系统在窗体上绘制一条从点A(0,0)到点B(120,80)的直线。此时,世界坐标系统与页面坐标系统重合。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.Windows.Forms;
namespace Example
{
public partial class Form1 : Form
{
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.SuspendLayout();
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(332, 266);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
public Form1()
{
InitializeComponent();
this.AutoScaleDimensions = new Size(5, 13);
this.ClientSize = new Size(392, 373);
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
}
protected override void OnPaint(PaintEventArgs e)
{
// 获取绘图对象
Graphics g = e.Graphics;
// 设置垂直方向字体
Font vertFont = new Font("Verdana", 10, FontStyle.Regular);
// 设置水平方向字体
Font horzFont = new Font("Verdana", 10, FontStyle.Regular);
// 设置画刷
SolidBrush blackBrush = new SolidBrush(Color.Black);
// 设置黑色画笔
Pen blackPen = new Pen(Color.Black, 1);
// 设置蓝色画笔
Pen bluePen = new Pen(Color.Blue, 1);
//页面坐标
//绘制X轴标签
g.DrawString("200", horzFont, blackBrush, 200, 5);
g.DrawString("100", horzFont, blackBrush, 100, 5);
g.DrawString("X", horzFont, blackBrush, 250, 0);
//Drawing vertical string
StringFormat vertStrFormat= new StringFormat();
vertStrFormat.FormatFlags = StringFormatFlags.DirectionVertical;
//绘制Y轴标签
g.DrawString("100", vertFont, blackBrush, 0, 100);
g.DrawString("200", vertFont, blackBrush, 0, 200);
g.DrawString("Y", vertFont, blackBrush, 0, 250);
//绘制坐标轴
g.DrawLine(blackPen, 0, 0, 0, 250);
g.DrawLine(blackPen, 0, 0, 250, 0);
// 绘制直线
Point A = new Point(0, 0);
Point B = new Point(120, 80);
g.DrawLine(bluePen, A, B);
}
}
}
现将世界坐标系统的原点从点(0,0)偏移到点(50,40),应用Graphics类中的TranslateTransform方法实现原点的偏移。
- 在世界坐标系统中,该直线仍是起始于点(0,0)终止于点(120,80),
- 在页面坐标系统中该直线是起始于点(50,40)终止于点(120 + 50 = 170, 80 + 40 = 120)
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
g.PageUnit = GraphicsUnit.Inch;
g.DrawLine(Pens.Black, 0,0,2,1);
}
在上述的代码段中,首先将PageUnit属性值设置为英寸,然后画一条从点(0,0)到点(2,1)的直线。这条直线在世界和页面坐标系统中起始于点(0,0)终止于点(2,1),但是在设备坐标中,该直线起始于点(0,0)终止于点(192,96)(假设屏幕的解析度是每英寸96个点)。注意,画笔此时的默认宽度为1个页面单位,即1个英寸宽。当然,也可重新定义笔的宽度。
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
Pen redPen = new Pen(Color.Red, 1/g.Dpix);
g.PageUnit = GraphicsUnit.Inch;
g.DrawLine(redPen, 0,0,2,1);
}
3.2 颜色变换
GDI+提供用于存储和处理图形图像的Image类和Bitmap类,Image对象和Bitmap对象将每个像素的颜色都存储为32位的数:红色、绿色、蓝色和Alpha个占8位。这四个分量的值都是0到255。在红色、绿色、蓝色分量中,0标识没有亮度,255标识最大亮度。Alpha分量指定颜色的透明度,0表示完全透明,255表示完全不透明。颜色矢量采用四元组形式(红色、绿色、蓝色、Alpha)。因此可用以下颜色矩阵来修改该颜色分量。
| Red 0 0 0 |
| 0 Green 0 0 |
| 0 0 Blue 0 |
| 0 0 0 Alpha |
表示颜色的另一种管理是用数字1表示亮度达到最大,GDI+在进行颜色变换时使用以1表示最大亮度的管理。例如,改变颜色(0,255,0,255)到半透明,方法就是用50%替代上述4x4颜色变换矩阵中的Alpha的初始值。相关的计算矩阵如下所示:
| 1 0 0 0 |
| 0 1 0 0 |
|0 255 0 255| x | | = |0 255 0 127.5|
| 0 0 1 0 |
| 0 0 0 0.5 |
上述是通过4x4的颜色变换矩阵进行线性变换。要支持非线性变换,需要将4x4的线性颜色变换矩阵扩展为5x5的仿射颜色变换矩阵。
| Red 0 0 0 0 |
| 0 Green 0 0 0 |
| 0 0 Blue 0 0 |
| 0 0 0 Alpha 0 |
| 0 0 0 0 1 |
例如,假设颜色适量为(25,100,100,255),须同事完成两个操作:
- 将红色元素的值加倍
- 将绿色元素的值加100
| 2 0 0 0 0 |
| 0 1 0 0 0 |
|25 100 100 255 1| x | 0 0 1 0 0 | = |50 200 100 255 1|
| 0 0 0 1 0 |
| 0 100 0 0 1 |