VC/QT/SDL 画图相关函数方法

图片取模软件工具推荐:Img2Lcd.exe 【支持JPG、BMP、EMF、WBMP、GIF、ICO】


//imgData:RGB888格式数据
//width:图片宽度px
//height:图片高度px
//CMFCApplication1Dlg:自定义的项目MFC对话框类
void Cxxxx::DrawPic(char *imgData,int width,int height)
{

	if (imgData == NULL) {
		return;
	}

	CDC MemDC; //首先定义一个显示设备对象
	CBitmap MemBitmap;//定义一个位图对象
	//随后建立与屏幕显示兼容的内存显示设备
	MemDC.CreateCompatibleDC(NULL);

	//建立一个与屏幕显示兼容的位图,位图的大小可以用窗口的大小
	CRect  rcClient;
	GetClientRect(&rcClient);
	static CDC* pDC = GetDC();
	MemBitmap.CreateCompatibleBitmap(pDC, rcClient.Width(), rcClient.Height());
	//将位图选入到内存显示设备中
	//只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上
	CBitmap* pOldBit = MemDC.SelectObject(&MemBitmap);
	//先用背景色将位图清除干净.
	MemDC.FillSolidRect(0, 0, rcClient.Width(), rcClient.Height(),
		RGB(31, 204, 160));//绘图

	//start draw data==================

	CPoint pt;
	COLORREF color = RGB(0, 0, 0);
	long index = 0;
	for (size_t i = 0; i < height; i++)
	{
		for (size_t j = 0; j < width; j++)
		{
			pt.x = j;
			pt.y = i;
			color = RGB(imgData[index], imgData[index + 1], imgData[index + 2]);
			index += 4;//R_G_B_Alpha
			MemDC.SetPixel(pt, color);//大图片比较耗时
		}
	}
	//end draw data=================

	//将内存中的图拷贝到屏幕上进行显示
	pDC->BitBlt(0, 0, rcClient.Width(), rcClient.Height(),
		&MemDC, 0, 0, SRCCOPY);
	MemBitmap.DeleteObject();
	MemDC.DeleteDC();
}


//------------------------------------------
//截取整屏幕保存到g_ScreenData数组
/* Globals */
int g_ScreenX = 0;
int g_ScreenY = 0;
BYTE* g_ScreenData = 0;

void ScreenCap()
{
	HDC hScreen = GetDC(NULL);
	g_ScreenX = GetDeviceCaps(hScreen, HORZRES);
	g_ScreenY = GetDeviceCaps(hScreen, VERTRES);

	HDC hdcMem = CreateCompatibleDC(hScreen);
	HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, g_ScreenX, g_ScreenY);
	HGDIOBJ hOld = SelectObject(hdcMem, hBitmap);
	BitBlt(hdcMem, 0, 0, g_ScreenX, g_ScreenY, hScreen, 0, 0, SRCCOPY);
	SelectObject(hdcMem, hOld);

	BITMAPINFOHEADER bmi = { 0 };
	bmi.biSize = sizeof(BITMAPINFOHEADER);
	bmi.biPlanes = 1;
	bmi.biBitCount = 32;
	bmi.biWidth = g_ScreenX;
	bmi.biHeight = -g_ScreenY;
	bmi.biCompression = BI_RGB;
	bmi.biSizeImage = 0;// 3 * ScreenX * ScreenY;

	if (g_ScreenData)
		free(g_ScreenData);
	g_ScreenData = (BYTE*)malloc(4 * g_ScreenX * g_ScreenY);

	GetDIBits(hdcMem, hBitmap, 0, g_ScreenY, g_ScreenData, (BITMAPINFO*)&bmi, DIB_RGB_COLORS);

	ReleaseDC(GetDesktopWindow(), hScreen);
	DeleteDC(hdcMem);
	DeleteObject(hBitmap);
}

inline int GetScreenPixelB(int x, int y)
{
	return g_ScreenData[4 * ((y * g_ScreenX) + x)];
}

inline int GetScreenPixelG(int x, int y)
{
	return g_ScreenData[4 * ((y * g_ScreenX) + x) + 1];
}

inline int GetScreenPixelR(int x, int y)
{
	return g_ScreenData[4 * ((y * g_ScreenX) + x) + 2];
}
//------------------------------------

void Cxxxx::DrawBitmap(uint8_t* imgData, int bitCount, int width, int height)
{
#define PC_BITCOUNT 32  //电脑固定32bit图像位数
#define RGB565_RED      0xf800
#define RGB565_GREEN    0x07e0
#define RGB565_BLUE     0x001f
	//uint8_t* imgDataOut = (uint8_t*)malloc(width * height * (LV_COLOR_DEPTH / 8));
	long pixelCount = width * height;
	uint8_t* imgDataOut = (uint8_t*)malloc(pixelCount * 4);//32 BIT 图像

	if (bitCount == 16)
	{
		long i = 0;
		long j = 0;
		uint8_t r = 0;
		uint8_t g = 0;
		uint8_t b = 0;
		uint16_t n565Color = 0;

		do
		{
			n565Color = (imgData[j] << 8) | imgData[j + 1];
			b = (n565Color & RGB565_RED) >> 8;
			g = (n565Color & RGB565_GREEN) >> 3;
			r = (n565Color & RGB565_BLUE) << 3;
			j += 2;
			imgDataOut[i + 0] = r;
			imgDataOut[i + 1] = g;
			imgDataOut[i + 2] = b;
			imgDataOut[i + 3] = 0x00;
			i += 4;
		} while (j < pixelCount * bitCount / 8);
	}
	else if (bitCount == 24)
	{
		long i = 0;
		long j = 0;
		uint8_t r = 0;
		uint8_t g = 0;
		uint8_t b = 0;
		do
		{
			b = imgData[j + 0];
			g = imgData[j + 1];
			r = imgData[j + 2];
			j += 3;
			imgDataOut[i + 0] = r;
			imgDataOut[i + 1] = g;
			imgDataOut[i + 2] = b;
			imgDataOut[i + 3] = 0x00;
			i += 4;
		} while (j < pixelCount * bitCount / 8);
	}
	else if (bitCount == 32)
	{
		memcpy(imgDataOut, imgData, pixelCount * 4);
	}

	static CDC* pDC = GetDC();
	HBITMAP hBitMap = CreateBitmap(width, height, 1, PC_BITCOUNT, imgDataOut);
	CBitmap cbmp;
	CDC dcMemory;
	cbmp.Attach(hBitMap);

	dcMemory.CreateCompatibleDC(pDC);
	CBitmap* hOldBitmap = dcMemory.SelectObject(&cbmp);
	pDC->BitBlt(0, 0, width, height,
		&dcMemory,
		0, 0,
		SRCCOPY);

	cbmp.DeleteObject();
	dcMemory.SelectObject(hOldBitmap);
	dcMemory.DeleteDC();
#if 0
	CString str;
	str.Format("DrawData_%dBit",bitCount);
	DumpHex("DrawData", imgDataOut, pixelCount * 4);
#endif
	free(imgDataOut);
}

void BlockTransfer(int x0,int y0,int imgW,int imgH,uint8_t * imageData)
{
	//g_screenBuffer为整个屏幕的buffer,宽度:SCREEN_WIDTH,高度:SCREEN_HEIGH
	//源图长宽为sW,sH;在(x0,y0)位置覆盖 imageData 图形数据 长宽 imgW,imgH;

	int x = 0;
	int y = 0;
	int posDest = 0;
	int posSrc = 0;

	for (int j = 0;j < imgH;j++)
	{
		y = y0 + j;
		if (y > SCREEN_HEIGH)
			break;
		for (int i = 0; i < imgW; i++)
		{
			x = x0 + i;
			if (x > SCREEN_WIDTH)
				break;
			posDest = x + SCREEN_WIDTH * y;
			posSrc = i + imgH * j;
			//请判断坐标是否越界if
			//32BIT(4Byte)图处理方式:
			g_screenBuffer[4 * posDest + 0] = imageData[4 * posSrc + 0];
			g_screenBuffer[4 * posDest + 1] = imageData[4 * posSrc + 1];
			g_screenBuffer[4 * posDest + 2] = imageData[4 * posSrc + 2];
			g_screenBuffer[4 * posDest + 3] = imageData[4 * posSrc + 3];
		}
	}
}
void BlockTransfer(int x0,int y0,int imgW,int imgH,uint8_t * imageData,bool bEnableFilter=false, uint32_t filterColor=0)
{
	//g_screenBuffer为整个屏幕的buffer,宽度:SCREEN_WIDTH,高度:SCREEN_HEIGH
	//源图长宽为sW,sH;在(x0,y0)位置覆盖 imageData 图形数据 长宽 imgW,imgH;
	//bEnableFilter 允许过滤色, filterColor 过滤像素的RGB值

	int x = 0;
	int y = 0;
	int posDest = 0;
	int posSrc = 0;
	uint8_t r, g, b;
	uint32_t nColorCmp;

	for (int j = 0;j < imgH;j++)
	{
		y = y0 + j;
		if (y > SCREEN_HEIGH)
			break;
		for (int i = 0; i < imgW; i++)
		{
			x = x0 + i;
			if (x > SCREEN_WIDTH)
				break;
			posDest = x + SCREEN_WIDTH * y;
			posSrc = i + imgH * j;
			//请注意判断坐标是否越界
			//32BIT(4Byte)图处理方式:
			r = imageData[4 * posSrc + 0];
			g = imageData[4 * posSrc + 1];
			b = imageData[4 * posSrc + 2];
			if (bEnableFilter) {
				nColorCmp = (r << 16) | (g << 8) | (b);
				if (nColorCmp == filterColor) {
					continue;
				}
			}
			g_screenBuffer[4 * posDest + 0] = imageData[4 * posSrc + 0];
			g_screenBuffer[4 * posDest + 1] = imageData[4 * posSrc + 1];
			g_screenBuffer[4 * posDest + 2] = imageData[4 * posSrc + 2];
			g_screenBuffer[4 * posDest + 3] = imageData[4 * posSrc + 3];
		}
	}
}

void qt_draw_bitmap(uint8_t* imgData, int bitCount, uint16_t width, uint16_t height, QPainter* paint)
{
	//#include <QPainter> 
#define RGB565_RED      0xf800
#define RGB565_GREEN    0x07e0
#define RGB565_BLUE     0x001f

	long pixelCount = width * height;
	uint8_t* imgDataOut = (uint8_t*)malloc(pixelCount * 4);//32 BIT 图像

	if (bitCount == 16)
	{
		long i = 0;
		long j = 0;
		uint8_t r = 0;
		uint8_t g = 0;
		uint8_t b = 0;
		uint16_t n565Color = 0;

		do
		{
			n565Color = (imgData[j] << 8) | imgData[j + 1];
			b = (n565Color & RGB565_RED) >> 8;
			g = (n565Color & RGB565_GREEN) >> 3;
			r = (n565Color & RGB565_BLUE) << 3;
			j += 2;
			imgDataOut[i + 0] = r;
			imgDataOut[i + 1] = g;
			imgDataOut[i + 2] = b;
			imgDataOut[i + 3] = 0x00;
			i += 4;
		} while (j < pixelCount * bitCount / 8);
	}
	else if (bitCount == 24)
	{
		long i = 0;
		long j = 0;
		uint8_t r = 0;
		uint8_t g = 0;
		uint8_t b = 0;
		do
		{
			b = imgData[j + 0];
			g = imgData[j + 1];
			r = imgData[j + 2];
			j += 3;
			imgDataOut[i + 0] = r;
			imgDataOut[i + 1] = g;
			imgDataOut[i + 2] = b;
			imgDataOut[i + 3] = 0x00;
			i += 4;
		} while (j < pixelCount * bitCount / 8);
	}
	else if (bitCount == 32)
	{
		memcpy(imgDataOut, imgData, pixelCount * 4);
	}
	QByteArray imageByteArray = QByteArray((const char*)imgDataOut, pixelCount * 4);
	uchar* transData = (unsigned char*)imageByteArray.data();
	QImage image = QImage(transData, width, height, QImage::Format_RGB32);
	free(imgDataOut);

	paint->drawImage(QPoint(0, 0), image);//start point=[0,0]
	paint->end();

}
void qt_draw_bitmap(uint8_t *imgData16Bit, uint16_t width, uint16_t height, QLabel *label)
{
    long pixelCount = width * height;
    QByteArray imageByteArray = QByteArray((const char *) imgData16Bit, pixelCount * 2);
    uchar *transData = (unsigned char *) imageByteArray.data();
    QImage image = QImage(transData, width, height, QImage::Format_RGB16);//RGB565_16bit

    QPixmap tmpPixmap = QPixmap::fromImage(image);
    label->setPixmap(tmpPixmap);
}
#include <stdio.h>
#include <SDL2/SDL.h>

#define SDL_HOR_RES (800)
#define SDL_VER_RES (600)

typedef struct TagMonitor
{
    int screen_w;
    int screen_h;
    SDL_Renderer *render;
    SDL_Texture *texture;
} tagMonitor;

// 将屏幕设置成指定argb颜色
int update_win(tagMonitor *mt, uint32_t argb)
{
    uint32_t fb_data[SDL_HOR_RES * SDL_VER_RES]; // frame buffer data[w*h]
    for (size_t i = 0; i < SDL_HOR_RES * SDL_VER_RES; i++)
    {
        fb_data[i] = argb;
    }

    SDL_UpdateTexture(mt->texture, NULL, fb_data, mt->screen_w * 4);
    SDL_RenderClear(mt->render);
    // 设定渲染的目标区域
    SDL_Rect destRect;
    destRect.x = 0;
    destRect.y = 0;
    destRect.w = mt->screen_w;
    destRect.h = mt->screen_h;

    // 复制材质到渲染器对象
    if (SDL_RenderCopy(mt->render, mt->texture, NULL, &destRect))
    {
        printf("Error,%s \n", SDL_GetError());
        return -1;
    }
    // 执行渲染操作
    SDL_RenderPresent(mt->render);
    return 0;
}

int main(void *arg)
{
    // 窗口大小
    int screen_w = SDL_HOR_RES;
    int screen_h = SDL_VER_RES;

    // 初始化SDL
    if (SDL_Init(SDL_INIT_EVERYTHING))
    {
        printf("could not initialize SDL: %s\n", SDL_GetError());
        return -1;
    }

    SDL_Window *screen = SDL_CreateWindow("SimpleSDL2", 0, 0, screen_w, screen_h, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
    if (!screen)
    {
        printf("could not create window: %s\n", SDL_GetError());
        return -1;
    }

    SDL_Renderer *render = SDL_CreateRenderer(screen, -1, SDL_RENDERER_ACCELERATED);

    if (!render)
    {
        printf("render create fail! \n");
        return -1;
    }

    SDL_Texture *texture = SDL_CreateTexture(render,                      // 指定在哪个渲染器当中创建
                                             SDL_PIXELFORMAT_ARGB8888,    // 指定当前材质的像素格式
                                             SDL_TEXTUREACCESS_STREAMING, // 设定当前材质可修改
                                             screen_w, screen_h           // 指定材质宽高
    );
    if (!texture)
    {
        printf("texture create fail! \n");
        return -1;
    }

    tagMonitor mt;
    mt.screen_w = screen_w;
    mt.screen_h = screen_h;
    mt.texture = texture;
    mt.render = render;

    SDL_Event myEvent; // SDL事件
    int quit = 0;
    while (!quit) // 建立事件主循环
    {
        // 注意:事件只针对SDL创建的窗口内有效
        while (SDL_PollEvent(&myEvent)) // 从队列里取出事件
        {
            // printf("event=%d  \n", myEvent.type);
            switch (myEvent.type) // 根据事件类型分门别类去处理
            {
            case SDL_QUIT: // 离开事件[点击窗口关闭]
                quit = 1;  // quit event poll
                break;

            case SDL_MOUSEMOTION: // 鼠标移动
                printf("mouseXY: %d,%d \n", myEvent.motion.x, myEvent.motion.y);
                break;

            case SDL_MOUSEBUTTONDOWN:                             // 鼠标点击
                printf("ButtonClck:%d\n", myEvent.button.button); // 0:左键,1:中键,2:右键
                break;

            case SDL_KEYDOWN: // 键盘按下
                // 键值表在SDL2/SDL_keycode.h文件中定义
                printf("KEY_DOWN:%d\n", myEvent.key.keysym.sym);
                break;

            case SDL_KEYUP: // 键盘释放
                // 键值表在SDL2/SDL_keycode.h文件中定义
                printf("KEY_UP:%d\n", myEvent.key.keysym.sym);
                break;
            }
        }

        static int cnt = 0;
        static int index = 0;
        static uint32_t argb = 0xffffffff;

        if (cnt++ > 1000)
        {
            if (index == 0)
                argb = 0xFFFF0000; // R
            if (index == 1)
                argb = 0xFF00FF00; // G
            if (index == 2)
                argb = 0xFF0000FF; // B
            if (index == 3)
                argb = 0xFFFFFF00; // Yellow

            printf("argb index=%d.\n", index);
            cnt = 0;
            index = (index + 1) % 4;
        }
        update_win(&mt, argb);

    } //<< while(1)

    printf("quit screen_monitor_thread.! \n");
    exit(0);
    return 0;
}

可以作为课程设计的程序,实现简单的绘图,移动图形的功能。 使用vc框架实现。 GHpaint程序的几个重点 程序的基本功能: 程序提供绘图、删除已绘图形、移动已绘图形。 可以选择绘图颜色、形状、线粗。 重点问题: 1、 程序设计基于面向对象。 程序中所有图形都从基类CShapebase派生,大部分工作(例如:删除、移动等)都在这个基类中完成。此类中的两个纯需函数: //纯需函数DrawShape,每个基类都必须实现的函数。 //指定不同图形的绘制方法。 virtual void DrawShape(CDC* pdc) = 0; //橡皮筋效果的算法函数 virtual void ZoomShape(CDC *pdc) = 0; 这两个函数需要在派生类中重载。这两个函数中主要实现不同图形的绘制方法。 也就是说,不同图形的不同点只有绘制方法不同,其他所有属性和行为都可以统一处理。 这里的继承体现了面向对象的设计,论文中可以体现这一点。 2、 动态模板库对象CPtList的使用,动态模板库基础知识。 CPtList是动态模板库的一个实例化类。功能是以链表形式保存指针,至于指针类型不做要求,需要程序员自己设计。 本程序CPtList对象中保存所有图形对象指针,由于所有图形都从CShapebase派生,所以所有图形指针都可以转化为基类CShapebase指针后保存在CPtList中。以后再遍历列表,调用图形对象函数时出现了多态调用。这里体现面向对象程序的多态,论文中可以说明。 至于CPtList的使用,可以在网上查查,可以看看程序代码,比较简单。 多态:是一种函数调用形式。出现在类继承情况下。详细的多态定义到网上查查。 程序中的多态体现在CShapebase类的虚函数,在使用CShapebase指针调用这两个虚函数,实际调用的使子类的重载函数体。这里实际上是不知道函数怎样工作,但是知道函数功能。 例如:使用CShapebase指针调用DrawShape函数,因为不知道子类是什么图形,所以不知道DrawShape怎样画这个图,但是直到DrawShape函数会把这个图画好,这就达到了要求。 3、 Windows窗口绘图基本知识。 3.1 Windows GUI绘图基本知识。使用MFC类库之后,每个窗口都有一个CDC指针量。这个变量提供图形绘制。CDC类说明查查网络,内容比较多。CDC及提供图形绘制算法,也提供各种绘制模式(单色、异或色等),提供画笔和画刷的功能。 3.2 窗口绘图基本思想: 图形绘制在窗口中,windows不会帮助程序员让图形“长在”窗口上,当窗口被遮挡,最小化后,图形绘消失。程序需要在合适的时机把图形重新画在窗口上。所以窗口上画的内容需要程序员自己思考怎样保存。在本程序中所有图形保存在CPtList对象中。 3.2 Windows为窗口提供了一个重绘消息:OnPaint,在winxp系统中,这个消息会在窗口被遮挡部分从新出现、窗口从最小化恢复时调用。在win7种只有窗口从屏幕外移动进屏幕时调用。具体情况可以查查网络,这里是我的发现,并不是主要内容。 在OnPaint消息中重新绘制所有图形是一个保证图形不消失的好办法。 4、 Windows颜色控制基本知识。 4.1 windows使用RGB三原色(红绿蓝)提供颜色控制,本程序使用24位颜色,在内存中占用4字节,所以用int型表示,但是int型的最高位字节不使用,这样每一种颜色就有一个字节表示,每种颜色级别从0-255。系统提供RGB宏帮助定义颜色,例如:RGB(255,0,0)是红色,RGB(0,0,0)黑色,RGB(255,255,255)白色。 4.2 windows绘图提供多种颜色混合模式,本程序中使用异或模式实现图形的“橡皮筋”效果和移动效果,使用纯色模式定位图形。
8-1 用QPainter绘图(Painting with QPainter) 2011-10-26 19:56:04| 分类: 默认分类 | 标签: |字号大中小 订阅 8-1 用QPainter绘图(Painting with QPainter) 分类: C++ GUI Programming with Qt 4 2007-05-29 21:52 8228人阅读 评论(3) 收藏 举报 要在绘图设备(paint device,一般是一个控件)上开始绘制,我们只要创建一个QPainter,把绘图设备指针传给QPainter对象。例如: oid MyWidget::paintEvent(QPaintEvent *event) { QPainter painter(this); ... } 使用QPainter的draw…()函数我们可以绘制各种图形。图8.1给出了主要的一些。绘制的方式由QPainter的设置决定。设置的一部分是从绘图设备得到的,其他是初始化时的默认值。三个主要的设置为:画笔,刷子和字体。 画笔用来绘制直线和图形的边框。包含颜色,宽度,线型,角设置和连接设置。 刷子是填充几何图形的方式。包含颜色,方式设置,也可以是一个位图或者渐变色。 字体用来绘制文本。字体的属性很多,如字体名,字号等。 这些设置随时可以改变,可用QPen,QBrush,QFont对象调用setPen(),setBrush(),setFont()修改。 Figure 8.1. QPainter's most frequently used draw...() functions Figure 8.2. Cap and join styles < XMLNAMESPACE PREFIX ="O" /> < XMLNAMESPACE PREFIX ="V" /> Figure 8.3. Pen styles Figure 8.4. Predefined brush styles 现在来看看具体的例子。下面的代码是绘制图8.5(a)中椭圆的代码: QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); painter.setPen(QPen(Qt::black, 12, Qt::DashDotLine, Qt::RoundCap)); painter.setBrush(QBrush(Qt::green, Qt::SolidPattern)); painter.drawEllipse(80, 80, 400, 240); 调用函数setRenderHint(QPainter::Antialiasing,true),使绘制时边缘平滑,使用颜色浓度的变化,把图形的边缘转换为象素时引起的扭曲变形尽可能减少,在支持这一功能的平台或者绘图设备上得到一个平滑的边缘。 Figure 8.5. Geometric shape examples 下面的代码是图8.5(b)中绘制扇形的代码: QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); painter.setPen(QPen(Qt::black, 15, Qt::SolidLine, Qt::RoundCap, Qt::MiterJoin)); painter.setBrush(QBrush(Qt::blue, Qt::DiagCrossPattern)); painter.drawPie(80, 80, 400, 240, 60 * 16, 270 * 16); 函数drawPie()的最后两个参数值的单位为一度的十六分之一。 下面的代码是图8.5(c)中绘制贝赛尔曲线的代码: QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); QPainterPath path; path.moveTo(80, 320); path.cubicTo(200, 80, 320, 80, 480, 320); painter.setPen(QPen(Qt::black, 8)); painter.drawPath(path); 通过连接基本图形元素,直线,椭圆,多段线,圆弧,二次和三次贝塞尔曲线等,QPainterPath类能确定任何矢量图形。因此,绘图路径(Painter paths)是最基本的绘制元素,任何图形和图形的组合都可以同路径(path)表示。 一个路径能够确定一个轮廓,由这个轮廓确定的区域可以由刷子来填充。在图8.5(c)中我们没有设置刷子,因此只绘制了轮廓。 以上的三个例子都是使用了默认的刷子(Qt::SolidePattern,Qt::DiagCrossPattern,Qt::NoBrush)。在现在的应用程序中,单色填充已经很少使用,渐变色填充开始收到欢迎。渐变是依靠颜色的变化实现两种或者多种颜色之间平滑的过渡。渐变通常用来处理3D效果,如使用Plastique渐变方式来表现QPushButtons。 Qt支持三种类型的渐变:线形渐变,圆锥渐变和圆形渐变(linear, conical, and radial)。下一节的OvenTimer例子就是在一个控件中使用了所有这三种渐变。 线形渐变由两个控制点和直线上的一系列颜色点组成。图8.6由下面的代码得到:在两个控制点之间,在三个不同的位置确定了三个不同的颜色值。位置有0到1的浮点数得到,0为第一个控制点,1为第二个控制点。不同位置点之间的颜色由差值计算得到。 LinearGradient gradient(50, 100, 300, 350); gradient.setColorAt(0.0, Qt::white); gradient.setColorAt(0.2, Qt::green); gradient.setColorAt(1.0, Qt::black); Figure 8.6. QPainter's gradient brushes 圆形渐变由颜色组,圆心(xc,yc),半径r和焦点(xf,yf)定义。圆心和半径定义一个圆,颜色从焦点开始扩散到周围,焦点可以是圆心也可以是圆内的任意一个点。 圆锥渐变由圆心(xc,yc)和一个角度a定义。颜色从圆心开始像表的秒针一样扩散。 我们已经提到了QPainter的画笔,刷子和字体设置。此外,QPainter还有其他一些设置影响图形和文字的绘制: 1. 背景刷子,当背景模式为Qt::OpaqueMode(缺省值为Qt::transparentMode)时,背景刷子用来填充几何图形,文字,和位图的背景(在绘图刷子的下面) 2. 刷子的起点:刷子的起始绘制点,通常为控件的左上角。 3. 剪辑区域,剪辑区域为绘图设备上可以绘制的区域,在剪辑区域意外进行的绘制是无效的。 4. 视口,窗口,世界坐标:这三个决定了QPainter的逻辑坐标映射到物理坐标的方式。通常,逻辑坐标和物理坐标是重合的。坐标系统在下一节介绍。 5. 组合方式:组合方式决定绘制设备上新绘制的象素和已经存在的象素的影响方式。缺省方式为覆盖式(source over),新象素画在已有元素的上面。只是有限一个绘图设备支持组合方式的设置,将在本章后面介绍 在任何时候,我们可以调用save()把QPainter当前的设置保存在一个内部栈里,然后调用restore()进行恢复。我们能够临时改变QPainter的一些设置,然后恢复先前的值 Qt的二位图形引擎是基于 QPainter 类的。它既可以绘制几何形状,也可以绘制像素映射、图像和文字。自定义窗口部件需要重新实现 QWidget::paintEvent() 。 void Widget:: paintEvent( QPaintEvent * /*paintEvent*/ ) { draw(); drawBezier(); } void Widget:: draw() { QPainter painter( this ); painter. setPen( QPen ( Qt :: black, 10 , Qt :: DashDotLine, Qt :: RoundCap)); painter. setBrush( QBrush ( Qt :: green, Qt :: SolidPattern)); painter. drawEllipse( 10 , 10 , 400 , 240 ); painter. drawRect( QRect ( 10 , 300 , 400 , 140 )); } 首先需要创建一个 QPainter ,将需要绘图的设备的指针传递给 QPainter 。 QPainter 有各种 draw…() 可以绘制不同的图形。画笔、画刷、字体是最重要的三种设置。 画笔用来画线和边缘, QPainter ::setPen() 可以对画笔进行设置,包括颜色、宽度、线型、拐点风格等。 画刷用来填充几何形状的图案, QPainter ::setBrush() 可以对画刷进行设置,包括颜色和纹理风格。 绘制文本时需要对字体进行设置, QPainter ::setFont() 对字体进行设置,包括字体族和磅值。 void Widget:: drawBezier() { QPainter painter( this ); painter. setRenderHint( QPainter :: Antialiasing, true ); QPainterPath path; path. moveTo( 10 , 320 ); path. cubicTo( 200 , 80 , 400 , 80 , 480 , 320 ); painter. setPen( QPen ( Qt :: black, 8 )); painter. setBrush( QBrush ( Qt :: green, Qt :: DiagCrossPattern)); painter. drawPath( path); } QPainterPath 可以通过连接基本的图形单元元素来确定任意的矢量形状,包括:直线、椭圆、多边形、弧形、贝赛尔曲线等。 首先使用 QPainter ::moveto() 来确定图形的起点,然后通过 cubicTo() 来确定绘制的线路。前 4 个参数确定了两个控制点,后两个参数是结束点的位置。 修改函数如下: void Widget:: drawBezier() { QPainter painter( this ); painter. setRenderHint( QPainter :: Antialiasing, true ); QPainterPath path; path. moveTo( 10 , 320 ); path. cubicTo( 200 , 80 , 400 , 80 , 480 , 320 ); painter. setPen( QPen ( Qt :: black, 2 )); painter. drawPath( path); painter. setPen( QPen ( Qt :: red, 4 )); painter. drawPoint( 10 , 320 ); painter. drawPoint( 200 , 80 ); painter. drawPoint( 400 , 80 ); painter. drawPoint( 480 , 320 ); } 画笔样式 为了尝试画笔的样式,这里故意使用了一个新的画笔: painter.setPen(QPen(Qt::black, 5, Qt::DashDotLine, Qt::RoundCap)); 我们对照着API去看,第一个参数是画笔颜色,这里设置为黑色;第二个参数是画笔的粗细,这里是5px;第三个是画笔样式,我们使用了 DashDotLine,正如同其名字所示,是一个短线和一个点相间的类型;第四个是RoundCap,也就是圆形笔帽。然后我们使用一个黄色的画刷填充,画了一个椭圆。 后面的一个和前面的十分相似,唯一的区别是多了一句 painter.setRenderHint(QPainter::Antialiasing, true); 不过这句也很清楚,就是设置Antialiasing属性为true.如果你学过图形学就会知道,这个长长的单词就是"反走样".经过这句设置,我们就打开了QPainter的反走样功能。还记得我们曾经说过,QPainter是一个状态机,因此,只要这里我们打开了它,之后所有的代码都会是反走样绘制的了。 看到这里你会发现,反走样的效果其实比不走样要好得多,那么,为什么不默认打开反走样呢?这是因为,反走样是一种比较复杂的算法,在一些对图像质量要求不高的应用中,是不需要进行反走样的。为了提高效率,一般的图形绘制系统,如Java2D、OpenGL之类都是默认不进行反走样的。 还有一个疑问,既然反走样比不反走样的图像质量高很多,不进行反走样的绘制还有什么作用呢?前面说的是一个方面,也就是,在一些对图像质量要求不高的环境下,或者说性能受限的环境下,比如嵌入式和手机环境,是不必须要进行反走样的。另外还有一点,在一些必须精确操作像素的应用中,也是不能进行反走样的。请看下面的图片: 上图是使用Photoshop的铅笔和画笔工具画的1像素的点在放大到3200%视图下截下来的。Photoshop里面的铅笔工具是不进行反走样,而画笔是要进行反走样的。在放大的情况下就会知道,有反走样的情况下是不能进行精确到1像素的操作的。因为反走样很难让你控制到1个像素。这不是 Photoshop画笔工具的缺陷,而是反走样算法的问题。如果你想了解为什么这样,请查阅计算机图形学里面关于反走样的原理部分。 反走样是图形学中的重要概念,用以防止“锯齿”现象的出现。很多系统的绘图API里面都会内置了反走样的算法,不过默认一般都是关闭的,Qt也不例外。下面我们来看看代码。这段代码仅仅给出了paintEvent函数,相信你可以很轻松地替换掉前面章节中的相关代码。 void PaintedWidget::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setPen(QPen(Qt::black, 5, Qt::DashDotLine, Qt::RoundCap)); painter.setBrush(Qt::yellow); painter.drawEllipse(50, 150, 200, 150); painter.setRenderHint(QPainter::Antialiasing, true); painter.setPen(QPen(Qt::black, 5, Qt::DashDotLine, Qt::RoundCap)); painter.setBrush(Qt::yellow); painter.drawEllipse(300, 150, 200, 150); } 声明:本文原创于yafeilinux的百度博客,http://hi.baidu.com/yafeilinux 转载请注明出处。 前面一节我们讲解了图片的显示,其中很多都用到了坐标的变化,这一节我们简单讲一下Qt的坐标系统,其实也还是主要讲上一节的那几个函数。这里我们先讲解一下Qt的坐标系,然后讲解那几个函数,它们分别是: translate()函数,进行平移变换;scale()函数,进行比例变换;rotate()函数,进行旋转变换;shear()函数,进行扭曲变换。 最后介绍两个有用的函数save()和restore(),利用它们来保存和弹出坐标系的状态,从而实现快速利用几个变换来绘图。 一、坐标系简介。 Qt中每一个窗口都有一个坐标系,默认的,窗口左上角为坐标原点,然后水平向右依次增大,水平向左依次减小,垂直向下依次增大,垂直向上依次减小。原点即为(0,0)点,然后以像素为单位增减。 例如: void Dialog::paintEvent(QPaintEvent *) { QPainter painter(this); painter.setBrush(Qt::red); painter.drawRect(0,0,100,100); painter.setBrush(Qt::yellow); painter.drawRect(-50,-50,100,100); } 我们先在原点(0,0)绘制了一个长宽都是100像素的红色矩形,又在(-50,-50)点绘制了一个同样大小的黄色矩形。可以看到,我们只能看到黄色矩形的一部分。效果如下图。 二、坐标系变换。 坐标系变换是利用变换矩阵来进行的,我们可以利用QTransform类来设置变换矩阵,因为一般我们不需要进行更改,所以这里不在涉及。下面我们只是对坐标系的平移,缩放,旋转,扭曲等应用进行介绍。 1.利用translate()函数进行平移变换。 void Dialog::paintEvent(QPaintEvent *) { QPainter painter(this); painter.setBrush(Qt::yellow); painter.drawRect(0,0,50,50); painter.translate(100,100); //将点(100,100)设为原点 painter.setBrush(Qt::red); painter.drawRect(0,0,50,50); painter.translate(-100,-100); painter.drawLine(0,0,20,20); } 效果如下。 这里将(100,100)点作为了原点,所以此时(100,100)就是(0,0)点,以前的(0,0)点就是 (-100,-100)点。要想使原来的(0,0)点重新成为原点,就是将(-100,-100)设为原点。 2.利用scale()函数进行比例变换,实现缩放效果。 void Dialog::paintEvent(QPaintEvent *) { QPainter painter(this); painter.setBrush(Qt::yellow); painter.drawRect(0,0,100,100); painter.scale(2,2); //放大两倍 painter.setBrush(Qt::red); painter.drawRect(50,50,50,50); } 效果如下。 可以看到,painter.scale(2,2),是将横纵坐标都扩大了两倍,现在的(50,50)点就相当于以前的 (100,100)点。 3.利用shear()函数就行扭曲变换。 void Dialog::paintEvent(QPaintEvent *) { QPainter painter(this); painter.setBrush(Qt::yellow); painter.drawRect(0,0,50,50); painter.shear(0,1); //纵向扭曲变形 painter.setBrush(Qt::red); painter.drawRect(50,0,50,50); } 效果如下。 这里,painter.shear(0,1),是对纵向进行扭曲,0表示不扭曲,当将第一个0更改时就会对横行进行扭曲,关于扭曲变换到底是什么效果,你观察一下是很容易发现的。 4.利用rotate()函数进行比例变换,实现缩放效果。 void Dialog::paintEvent(QPaintEvent *) { QPainter painter(this); painter.drawLine(0,0,100,0); painter.rotate(30); //以原点为中心,顺时针旋转30度 painter.drawLine(0,0,100,0); painter.translate(100,100); painter.rotate(30); painter.drawLine(0,0,100,0); } 效果如下。 因为默认的rotate()函数是以原点为中心进行顺时针旋转的,所以我们要想使其以其他点为中心进行旋转,就要先进行原点的变换。这里的painter.translate(100,100)将(100,100)设置为新的原点,想让直线以其为中心进行旋转,可是你已经发现效果并非如此。是什么原因呢?我们添加一条语句,如下: void Dialog::paintEvent(QPaintEvent *) { QPainter painter(this); painter.drawLine(0,0,100,0); painter.rotate(30); //以原点为中心,顺时针旋转30度 painter.drawLine(0,0,100,0); painter.rotate(-30); painter.translate(100,100); painter.rotate(30); painter.drawLine(0,0,100,0); } 效果如下。 这时就是我们想要的效果了。我们加的一句代码为painter.rotate(-30),这是因为前面已经将坐标旋转了30度,我们需要将其再旋转回去,才能是以前正常的坐标系统。不光这个函数如此,这里介绍的这几个函数均如此,所以很容易出错。下面我们将利用两个函数来很好的解决这个问题。 三、坐标系状态的保护。 我们可以先利用save()函数来保存坐标系现在的状态,然后进行变换操作,操作完之后,再用restore()函数将以前的坐标系状态恢复,其实就是一个入栈和出栈的操作。 例如: void Dialog::paintEvent(QPaintEvent *) { QPainter painter(this); painter.save(); //保存坐标系状态 painter.translate(100,100); painter.drawLine(0,0,50,50); painter.restore(); //恢复以前的坐标系状态 painter.drawLine(0,0,50,50);
当然,以下是一个使用SDL2库的简单C++程序示例,用于创建一个窗口并显示一个红色的矩形: ```cpp #include <SDL2/SDL.h> int main() { // 初始化SDL if (SDL_Init(SDL_INIT_VIDEO) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "无法初始化SDL: %s", SDL_GetError()); return 1; } // 创建窗口 SDL_Window* window = SDL_CreateWindow("SDL2 程序示例", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 600, SDL_WINDOW_SHOWN); if (!window) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "无法创建窗口: %s", SDL_GetError()); return 1; } // 创建渲染器 SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0); if (!renderer) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "无法创建渲染器: %s", SDL_GetError()); return 1; } // 设置绘制颜色为红色 SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); // 清空渲染器 SDL_RenderClear(renderer); // 创建矩形 SDL_Rect rect = { 100, 100, 200, 200 }; // 绘制矩形 SDL_RenderFillRect(renderer, &rect); // 刷新渲染器 SDL_RenderPresent(renderer); // 主循环 bool quit = false; SDL_Event event; while (!quit) { while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { quit = true; } } } // 清理资源 SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return 0; } ``` 请确保已经安装了SDL2库,并在编译时链接SDL2库。这个程序将创建一个窗口并显示一个红色的矩形,直到用户关闭窗口为止。希望这个示例能帮到你!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值