Files
MXC_A59/app/double_pointer_halo/double_pointer_demo.c

2270 lines
69 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//#include <windows.h>
#include <vg/openvg.h>
#include <vg/vgu.h>
#include <EGL/egl.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include "double_pointer_demo.h"
#include "vg_font.h"
#include "vg_image.h"
#ifdef DOUBLE_POINTER_HALO
#define SCREEN_WIDTH 1280
#define SCREEN_HEIGHT 480
#define VG_W 1280
#define VG_H 480
#define VG_OSD_X 0
#define VG_OSD_Y 0
#define OSD_W 1280
#define OSD_H 480
#define HALO_W 350
#define HALO_H 299
#define CIRCLE_R (214)
#define CIRCLE_0_X (30+214)
#define CIRCLE_0_Y (26+214)
#define CIRCLE_1_X (1280 - (30+214))
#define CIRCLE_1_Y (26+214)
#define PT_OFF_Y 132
#define ROM_IMAGE_HALO_ARGB8888 "halo.ARGB8888"
#define ROM_IMAGE_BGRGB565_RGB565 "BGRGB565.RGB565"
#define ROM_IMAGE_OIL_LEVEL_1_ARGB8888 "oil_level_1.ARGB8888"
#define ROM_IMAGE_OIL_LEVEL_2_ARGB8888 "oil_level_2.ARGB8888"
#define ROM_IMAGE_OIL_LEVEL_3_ARGB8888 "oil_level_3.ARGB8888"
#define ROM_IMAGE_OIL_LEVEL_4_ARGB8888 "oil_level_4.ARGB8888"
#define ROM_IMAGE_OIL_LOW_ARGB8888 "oil_low.ARGB8888"
#define ROM_IMAGE_OIL_HIGH_ARGB8888 "oil_high.ARGB8888"
#define ROM_IMAGE_WATER_TEMP_LOW_ARGB8888 "water_temp_low.ARGB8888"
#define ROM_IMAGE_WATER_TEMP_HIGH_ARGB8888 "water_temp_high.ARGB8888"
#define ROM_IMAGE_CAR_ARGB8888 "car.ARGB8888"
#define ROM_IMAGE_LEFT_FRONT_ARGB8888 "Left_front.ARGB8888"
#define ROM_IMAGE_LEFT_REAR_ARGB8888 "Left_rear.ARGB8888"
#define ROM_IMAGE_RIGHT_FRONT_ARGB8888 "Right_front.ARGB8888"
#define ROM_IMAGE_RIGHT_REAR_ARGB8888 "Right_rear.ARGB8888"
#define ROM_IMAGE_LEFT_1_ARGB8888 "left_1.ARGB8888"
#define ROM_IMAGE_RIGHT_1_ARGB8888 "right_1.ARGB8888"
#define ROM_IMAGE_ABS_ARGB8888 "ABS.ARGB8888"
#define ROM_IMAGE_ENGINE_ARGB8888 "engine.ARGB8888"
#define ROM_IMAGE_ENGINE_OIL_ARGB8888 "engine_oil.ARGB8888"
#define ROM_IMAGE_LOWER_BEAM_ARGB8888 "lower_beam.ARGB8888"
#define ROM_IMAGE_STOP_LAMP_ARGB8888 "stop_lamp.ARGB8888"
#define ROM_IMAGE_WIDTH_LAMP_ARGB8888 "width_lamp.ARGB8888"
#define ROM_IMAGE_DOUBLE_FLASHING_ARGB8888 "double_flashing.ARGB8888"
#define ROM_IMAGE_FOGLIGHT_ARGB8888 "foglight.ARGB8888"
#define ROM_IMAGE_HIGH_BEAM_ARGB8888 "high_beam.ARGB8888"
// 将SVG画布坐标转换为OpenVG坐标(仅转换Y轴坐标), 从自顶向下转换为自底向上
// cy是基于SVG坐标(X轴自左向右, Y轴自顶向下, PC画笔也是)的某个画布的高度, y是该画布下的Y轴坐标
#define MAP_SVG_TO_OPENVG(y,cy) (cy - 1.0f - (y))
static VGImage maskImage;
static VGPaint maskPaint;
// 全画面输出次数
static int full_dial_output_count = 2; // 2个framebuffer
// 光晕底图aRGB8888
static unsigned char *halo;
static int process_dial_level_rectangle(int dial, VGint* coords, int cb_coords);
static void init_icons (void);
static void draw_icons (void);
static void draw_message (void);
static void icon_state_simulate (void);
extern unsigned int xm_vg_get_osd_fb (int *no);
extern void* XM_RomAddress (const char *src);
extern unsigned int XM_GetTickCount (void);
// 扫描所有被表盘绘制污染的ICON并标记
static void scan_and_mark_icons_polluted_by_dial_paint (VGint* coords, int cb_coords);
// 用于计算2个表盘的脏矩形区域
// 1) 对于每个FrameBuffer, 根据每个表盘的前一刻度值与当前刻度值, 计算刻度值差异形成的重新绘制区域(由一个或多个脏矩形构成).
// 2) 根据重新绘制区域设置Scissoring Rectangles, 约束VG的Surface裁减区域, 大幅减少VG的计算与带宽
static float dial_vehicle_speeds[2]; // 记录对应于2个FrameBuffer的表盘0的时速值
static VGint vehicle_speeds_dirty_rects_coords[2][4]; // 保存与指针相关的脏矩形
static int vehicle_speeds_dirty_rects_count[2]; // 记录脏矩形的个数, 固定为1
static float dial_rotate_speeds[2]; // 记录对应于2个FrameBuffer的表盘1的转速值
static VGint rotate_speeds_dirty_rects_coords[2][4]; // 保存与指针相关的脏矩形
static int rotate_speeds_dirty_rects_count[2]; // 记录脏矩形的个数, 固定为1
//#define BG_RGB565 // 背景图使用RGB565格式,节省内存及DRAM带宽
// 仪表背景图
static unsigned short *bkimg;
static VGPaint colorPaint;
unsigned int* get_halo_image(void)
{
return (unsigned int *) halo;
}
unsigned short* get_bk_image(void)
{
return (unsigned short *)bkimg;
}
//#define HALO_FILL_PURE_COLOR // 测试HALO区域的位置
// 2.2 第一个对应于指针扇区(从指针起始指向角度(0读数)延伸到当前指针读数)的pie行path
// 该path使用完整光晕的背景底板进行绘制,即得到仅包含有效区域的光晕
// 定义起始角度 (笛卡尔坐标系)
// pointer_x, floater_y 指针圆盘的中心点坐标(屏幕坐标)
void drawHalo(float startAngle, float angleExtent, float pointer_x, float floater_y)
{
VGPath path;
// 光晕角度低于0.002时, 不再绘制扇形光晕, 避免底层vguArc算法的浮点计算异常
// 测试中0.001不会导致浮点计算异常. 此处将范围放宽一倍.
if ( fabs(angleExtent) < 0.002)
return;
#ifdef HALO_FILL_PURE_COLOR
// 测试代码,
// 使用红色填充扇形区域, 用于测试扇形区域的中心位置,扇形区域的开始/结束角度
VGfloat col[4];
col[0] = 0xff / 255.0f;
col[1] = 0x00/ 255.0f;
col[2] = 0x00 / 255.0f;
col[3] = 1.00f;
vgSetParameterfv(colorPaint, VG_PAINT_COLOR, 4, col);
vgSetPaint(colorPaint, VG_FILL_PATH); // PATH填充使用
#else
// 设置光晕图为扇形填充所用画笔的底图
vgPaintPattern(maskPaint, maskImage);
// 设置图像填充模式
vgSetPaint( maskPaint, VG_FILL_PATH ); // PATH填充使用
// 光晕图像与扇形path使用相同的空间尺寸(HALO_W, HALO_W)
// 定义转换矩阵, 将光晕底图(矩形包络)每个像素的颜色一一映射到扇形的整个包络矩形区域的每个点
vgSeti(VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER);
vgLoadIdentity(); // 单位矩阵
// 将光晕填充图像(HALO_W*HALO_W)的中心点映射到扇形包络区域(HALO_W*HALO_W)的中心点
vgTranslate (-HALO_W / 2, -HALO_W / 2);
#endif
// 使用画笔(光晕图)填充一个指定角度的扇形区域, 该扇形的中心点与指针圆的中心吻合, 半径与指针圆的半径相同
// 定位到扇形path的中心点(相对于屏幕坐标)
vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
vgLoadIdentity();
vgTranslate (pointer_x, floater_y);
// 设置非0路径填充模式
vgSeti ( VG_FILL_RULE, VG_NON_ZERO );
// 创建一个扇区路径
path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_ALL);
// 创建一个长宽相同的扇形矢量路径(参考vguArc文档说明)
vguArc( path, 0.0f, 0.0f, HALO_W, HALO_W, startAngle, angleExtent, VGU_ARC_PIE );
// 删去以下属性可确保资源使用完毕后立刻被释放
vgRemovePathCapabilities(path, VG_PATH_CAPABILITY_APPEND_FROM | VG_PATH_CAPABILITY_APPEND_TO |
VG_PATH_CAPABILITY_MODIFY | VG_PATH_CAPABILITY_TRANSFORM_FROM |
VG_PATH_CAPABILITY_TRANSFORM_TO | VG_PATH_CAPABILITY_INTERPOLATE_FROM |
VG_PATH_CAPABILITY_INTERPOLATE_TO);
// 使用图像填充扇区
vgDrawPath( path, VG_FILL_PATH );
// 马上释放资源(资源占有内存资源较大), 否则后续资源分配易失败
//vgClearPath (path, VG_PATH_CAPABILITY_ALL);
vgDestroyPath( path );
// 此处将一个无效句柄置为填充用途, 标记画笔对象maskPaint不再使用用于填充用途, 以便将画笔对象maskPaint的资源释放.
vgSetPaint (VG_INVALID_HANDLE, VG_FILL_PATH);
// 标记画笔对象maskPaint不再使用image对象maskImage, 以便maskImage资源释放
vgPaintPattern(maskPaint, VG_INVALID_HANDLE);
}
// ***************************************************************************************************
//
// ************** 使用以下宏定义可以选择不同的指针外形,指针填充方式,指针填充颜色 *************************
//
// ***************************************************************************************************
//
// 指针外形选择
// 以下2个指针的外观尺寸(外框)一致
//#define POINTER_SHAPE_1 // 类三角形指针
#define POINTER_SHAPE_2 // 类圆柱形指针
// 指针填充方式选择("使用纯色填充"刷新效率优于"使用线性梯度填充"
#define POINTER_FILL_TYPE_LINEAR_GRADIENT // 使用线性梯度填充
//#define POINTER_FILL_TYPE_COLOR // 使用纯色填充
#ifdef POINTER_SHAPE_1
#undef POINTER_FILL_TYPE_LINEAR_GRADIENT
#define POINTER_FILL_TYPE_COLOR // 使用纯色填充
#endif
// 指针纯色填充时颜色设置
#define FILL_COLOR(r,g,b,a) ((r<<24)|(g<<16)|(b<<8)|(a<<0))
// fill="#FFFFFF" // 白色
#define POINTER_FILL_COLOR FILL_COLOR(0xFF,0xFF,0xFF,0xFF)
// fill="#F44336" // 红色
//#define POINTER_FILL_COLOR FILL_COLOR(0xF4,0x43,0x36,0xFF)
// fill="#00D2FF" // 蓝色
//#define POINTER_FILL_COLOR FILL_COLOR(0x00,0xD2,0xFF,0xFF)
// ************************************* 表盘指针 ***************************
//
// 1) 表盘的指针共用一个SVG设计, 参考".\image\原始设计数据\指针\指针.svg", 使用UltraEdit可以打开并编辑SVG文件. 或者使用矢量绘图工具AI打开.
// 2) 使用SVG2OPENVG.exe可以将SVG格式的指针数据提取并生成如下初始的格式
// 2.1 将SVG2OPENVG.exe与指针.svg拷贝到相同的目录下, 如 "e:\proj\HMI\SW\OPENVG_DEMO\double_pointer\bin"
// 2.2 在命令行模式下, 执行 "e:\>cd \proj\HMI\SW\OPENVG_DEMO\double_pointer\bin"
// 2.3 在命令行模式下, 执行"SVG2OPENVG.exe 指针.svg"
// 2.4 输出如下矢量路径的文本信息
// size: 8.000000 x 76.000000
// static const VGubyte 指针_path_0_commands[] = {
// VG_MOVE_TO, VG_LINE_TO, VG_LINE_TO, VG_LINE_TO, VG_CLOSE_PATH
// };
// 2.5 或直接执行".\image\原始设计数据\指针\run.bat"
//
// static const VGfloat 指针_path_0_coordinates[] = {
// 1.603790f, 2.002140f, 7.496920f, 75.243500f, 0.000000f, 75.030098f, 1.603790f, 2.002140f
// };
// 3) 将其粘贴到代码中并修改. 加入表盘中心点的偏移(CIRCLE_0_X, CIRCLE_0_Y), 加入指针Y方向的偏移(PT_OFF_Y), 修改后代码如下
#ifdef POINTER_SHAPE_1
// 三角形指针
// size: 8.000000 x 76.000000
// 指针0的数据定义,
// svg file (pointer.svg)
// svg image's size: 8.000000 x 76.000000
#define pointer_cx 8.000000f
#define pointer_cy 76.000000f
static const VGubyte pointer_path_0_commands[] = {
VG_MOVE_TO, VG_LINE_TO, VG_LINE_TO, VG_LINE_TO, VG_CLOSE_PATH
};
// PT_OFF_Y表示将环形指针顶点向上移动的距离, 将其环形指针的顶部延伸到指针圆盘的外圆上.
// 可以将该值改为其他值测试其效果
static const VGfloat pointer_path_0_coordinates[] = {
1.603790f , 2.002140f ,
7.496920f , 75.243500f,
0.000000f , 75.030098f,
1.603790f , 2.002140f
};
// 指针1的数据定义
static const VGubyte pointer_path_1_commands[] = {
VG_MOVE_TO, VG_LINE_TO, VG_LINE_TO, VG_LINE_TO, VG_CLOSE_PATH
};
static const VGfloat pointer_path_1_coordinates[] = {
1.603790f , 2.002140f ,
7.496920f , 75.243500f ,
0.000000f , 75.030098f ,
1.603790f , 2.002140f
};
// 指针旋转中心在SVG坐标系中的位置(将pointer.svg在Adobe Illustrator中打开, 测量其底部旋转中心线的2个端点坐标, 得到中心点如下坐标)
// 指针旋转中心在SVG坐标系中的位置
// (0, 75.03), (7.5, 75.24)
#define POINTER_CENTER_OF_ROTATION_X (0 + 7.5f) / 2.0f
#define POINTER_CENTER_OF_ROTATION_Y (75.03f + 75.24f) / 2.0f
#elif defined(POINTER_SHAPE_2)
// 类圆柱形指针
// svg file (pointer01_2.svg)
// svg image's size: 8.000000 x 74.000000
#define pointer01_2_cx 8.000000f
#define pointer01_2_cy 74.000000f
static const VGubyte pointer_path_0_commands[] = {
VG_MOVE_TO, VG_CUBIC_TO, VG_VLINE_TO, VG_CUBIC_TO, VG_LINE_TO, VG_LINE_TO, VG_LINE_TO, VG_CLOSE_PATH
};
static const VGfloat pointer_path_0_coordinates[] = {
1.707420f, 70.863373f, 1.735730f, 72.051544f, 2.707060f, 73.000000f, 3.895560f, 73.000000f,
73.000000f, 5.081660f, 73.000000f, 6.051940f, 72.055252f, 6.083560f, 70.869568f, 8.000000f,
-1.000000f, 0.000000f, -0.790100f, 1.707420f, 70.863373f
};
static const VGubyte pointer_path_1_commands[] = {
VG_MOVE_TO, VG_CUBIC_TO, VG_VLINE_TO, VG_CUBIC_TO, VG_LINE_TO, VG_LINE_TO, VG_LINE_TO, VG_CLOSE_PATH
};
static const VGfloat pointer_path_1_coordinates[] = {
1.707420f, 70.863373f, 1.735730f, 72.051544f, 2.707060f, 73.000000f, 3.895560f, 73.000000f,
73.000000f, 5.081660f, 73.000000f, 6.051940f, 72.055252f, 6.083560f, 70.869568f, 8.000000f,
-1.000000f, 0.000000f, -0.790100f, 1.707420f, 70.863373f
};
#define pointer_cx pointer01_2_cx
#define pointer_cy pointer01_2_cy
// 指针旋转中心在SVG坐标系中的位置(将pointer01_2.svg在Adobe Illustrator中打开, 测量其底部旋转中心线的2个端点坐标, 得到中心点如下坐标)
// 底部旋转中心线的2个端点坐标
// (0.0f, 73.79f), (8.0f, 74.0f)
// 中心点坐标为2个端点取平均
#define POINTER_CENTER_OF_ROTATION_X (0 + 8.0f) / 2.0f
#define POINTER_CENTER_OF_ROTATION_Y (73.79f + 74.0f) / 2.0f
// 以下数据来源于pointer01_2.svg, 使用文本方式打开该文件. <linearGradient>在SVG中描述一个线性梯度渐变的定义
//<linearGradient id="paint0_linear_1987:6021" x1="4" y1="0" x2="4" y2="74" gradientUnits="userSpaceOnUse">
//<stop stop-color="#FF001E"/>
//<stop offset="1" stop-color="#FFF06F"/>
//</linearGradient>
// 指针路径的线性梯度渐变(linearGradien)填充的起点(x1,y1))/终点坐标(x2, y2) (SVG画布坐标)
// 起点x1y1到终点x2y2的连线是线性渐变的径向。
static VGfloat fill_linear_gradient_pointer[] = {
// x1 y1 x2 y2
4, 0, 4, 74,
};
// 指针路径的线性梯度渐变填充的关键点(2个)
// 可以修改offset及RGBA的值观察stop特性对指针颜色的影响
static VGfloat rampStops_pointer[10] = {
0.0f, 0xff / 255.0f, 0x00 / 255.0f, 0x1e / 255.0f, 1.0f, // <stop stop-color="#FF001E"/>
1.0f, 0xff / 255.0f, 0xf0 / 255.0f, 0x6f / 255.0f, 1.0f, // <stop offset="1" stop-color="#FFF06F"/>
};
#endif
// ************************************* 表盘指针 ***************************
// 环形指针矢量路径对象
static VGPath pointerPath_0; // 时速指针对象
static VGPath pointerPath_1; // 转速指针对象
static void genPointerPaths (void)
{
// 创建2个环形指针的矢量路径对象
pointerPath_0 = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_ALL);
vgAppendPathData(pointerPath_0, sizeof(pointer_path_0_commands)/sizeof(pointer_path_0_commands[0]), pointer_path_0_commands, pointer_path_0_coordinates);
pointerPath_1 = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_ALL);
vgAppendPathData(pointerPath_1, sizeof(pointer_path_1_commands)/sizeof(pointer_path_1_commands[0]), pointer_path_1_commands, pointer_path_1_coordinates);
// 定义一个光晕图像的正方形背景底板(带有alpha效果), 该正方形与扇形的包络区域完全匹配.
maskImage = vgCreateImage( VG_sRGBA_8888, HALO_W, HALO_W, VG_IMAGE_QUALITY_BETTER );
// halo图的宽度为HALO_W, 高度为HALO_H. HALO_W > HALO_H
// 填充部分区域 (0, HALO_W - HALO_H) (HALO_W-1, HALO_W-1) , 注意不是完整区域, 光晕的设计底图是一个长方形区域(HALO_W * HALO_H)
// 从最后一行开始(halo + HALO_W * (HALO_H - 1)), 自底向上填充 (-HALO_W * 4)
vgImageSubData (maskImage,
VG_IMAGE_DATA(halo) + VG_IMAGE_STRIDE(halo) * (VG_IMAGE_HEIGHT(halo) - 1),
0 - VG_IMAGE_STRIDE(halo),
VG_IMAGE_FORMAT(halo),
0, VG_IMAGE_WIDTH(halo) - VG_IMAGE_HEIGHT(halo), VG_IMAGE_WIDTH(halo), VG_IMAGE_HEIGHT(halo));
// 创建一个图形填充画笔对象
maskPaint = vgCreatePaint();
// 设置画笔为图形填充模式
vgSetParameteri (maskPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_PATTERN);
vgSetParameteri (maskPaint, VG_PAINT_PATTERN_TILING_MODE, VG_TILE_PAD);
// 将画笔置为图案填充
vgPaintPattern (maskPaint, maskImage);
// 创建一个纯色画笔对象
colorPaint = vgCreatePaint();
vgSetParameteri(colorPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
}
// SVG坐标变换
// t 转换矩阵
// sx, sy 原始坐标(转换前)
// dx, dy 保存转换后的坐标
static void svgTransformPoint(float* dx, float* dy, const float* t, float sx, float sy)
{
*dx = sx*t[0] + sy*t[2] + t[4];
*dy = sx*t[1] + sy*t[3] + t[5];
}
static void svgTransformIdentity (float* t)
{
t[0] = 1.0f; t[1] = 0.0f;
t[2] = 0.0f; t[3] = 1.0f;
t[4] = 0.0f; t[5] = 0.0f;
}
static void svgTransformTranslate (float* t, float tx, float ty)
{
t[0] = 1.0f; t[1] = 0.0f;
t[2] = 0.0f; t[3] = 1.0f;
t[4] = tx; t[5] = ty;
}
static void svgTransformMultiply (float* t, const float* s)
{
float t0 = t[0] * s[0] + t[1] * s[2];
float t2 = t[2] * s[0] + t[3] * s[2];
float t4 = t[4] * s[0] + t[5] * s[2] + s[4];
t[1] = t[0] * s[1] + t[1] * s[3];
t[3] = t[2] * s[1] + t[3] * s[3];
t[5] = t[4] * s[1] + t[5] * s[3] + s[5];
t[0] = t0;
t[2] = t2;
t[4] = t4;
}
//static void svgTransformScale(float* t, float sx, float sy)
//{
// t[0] = sx; t[1] = 0.0f;
// t[2] = 0.0f; t[3] = sy;
// t[4] = 0.0f; t[5] = 0.0f;
//}
#define vgvPI 3.141592653589793238462643383279502f
static void svgTransformRotate (float* t, float angle)
{
VGfloat a = angle * vgvPI / 180.0f;
float cs = cosf(a);
float sn = sinf(a);
t[0] = cs; t[1] = sn;
t[2] = -sn; t[3] = cs;
t[4] = 0.0f; t[5] = 0.0f;
}
static void svgTransformPremultiply (float* t, const float* s)
{
float s2[6];
memcpy (s2, s, sizeof(float) * 6);
svgTransformMultiply(s2, t);
memcpy (t, s2, sizeof(float) * 6);
}
static int svgTranslate (float* xform, float x, float y)
{
float t[6];
if (isnan(x) || isnan(y))
return -1;
svgTransformTranslate (t, x, y);
svgTransformPremultiply (xform, t);
return 0;
}
static int svgRotate(float* xform, float angle)
{
float t[6];
if (isnan(angle))
return -1;
svgTransformRotate(t, angle);
svgTransformPremultiply (xform, t);
return 0;
}
//static int svgScale(float* xform, float x, float y)
//{
// float t[6];
// if (isnan(x) || isnan(y))
// return -1;
//
// svgTransformScale (t, x, y);
// svgTransformPremultiply (xform, t);
// return 0;
//}
//
//static void svgTransformSkewX (float* t, float a)
//{
// t[0] = 1.0f; t[1] = 0.0f;
// t[2] = tanf(a); t[3] = 1.0f;
// t[4] = 0.0f; t[5] = 0.0f;
//}
//
//static void svgTransformSkewY (float* t, float a)
//{
// t[0] = 1.0f; t[1] = tanf(a);
// t[2] = 0.0f; t[3] = 1.0f;
// t[4] = 0.0f; t[5] = 0.0f;
//}
//
//static int svgSkewX(float* xform, float angle)
//{
// float t[6];
// if (isnan(angle))
// return -1;
//
// svgTransformSkewX (t, angle);
// svgTransformPremultiply (xform, t);
// return 0;
//}
//
//static int svgSkewY(float* xform, float angle)
//{
// float t[6];
// if (isnan(angle))
// return -1;
//
// svgTransformSkewY (t, angle);
// svgTransformPremultiply (xform, t);
// return 0;
//}
// 指针外包络矩形 (指针SVG坐标空间)
// <svg width="8" height="74" viewBox="0 0 8 74"
static const float pointer_region[] = { 0.0f, 0.0f, 8.0f, 74.0f };
// 计算指针的包络路径
static float sub_xmin;
static float sub_xmax;
static float sub_ymin;
static float sub_ymax;
// 计算指针的包络矩形区域
static void calcPointerBoundingRect(void)
{
float xmin, ymin, xmax, ymax;
// 根据指针的外形定义xmin, ymin, xmax, ymax
// 本实例的指针由外径/内径/圆盘3部分构成, 此处使用外径及圆盘的位置信息构建xmin, ymin, xmax, ymax
// 此处假设SVG设计文件中指针的径向沿着Y轴向上, 给出的坐标使用SVG坐标系(自顶向下).
xmin = pointer_region[0];
ymin = pointer_region[1];
xmax = pointer_region[2];
ymax = pointer_region[3];
// 将SVG坐标系(自顶向下)转换为OpenVG坐标系(自底向上), 仅修改Y轴, X轴保持不变
ymin = MAP_SVG_TO_OPENVG(ymin, pointer_cy);
ymax = MAP_SVG_TO_OPENVG(ymax, pointer_cy);
sub_xmin = xmin;
sub_xmax = xmax;
sub_ymin = ymin ;
sub_ymax = ymax;
}
// 根据角度及旋转中心点位置计算转换矩阵, 并保存在xform中(SVG格式转换矩阵)
static void calc_magic_transform(float angleExtent, float cx, float cy, float xform[6])
{
float svg_x, svg_y;
//float xform[6];
//加载单位矩阵, 将坐标原点移至绘制面的原点(屏幕左下角)
svgTransformIdentity(xform);
// 将坐标原点平移至指针SVG的坐标原点
// 根据2个中心点(指针圆盘中心点在SVG平面中的偏移, 指针圆盘中心点在OpenVG中的偏移)的差异计算指针SVG平面相对于OpenVG原点的偏移值
svg_x = cx - POINTER_CENTER_OF_ROTATION_X;
svg_y = cy - MAP_SVG_TO_OPENVG(POINTER_CENTER_OF_ROTATION_Y, pointer_cy);
// 将OpenVG坐标原点平移至SVG平面的原点
svgTranslate(xform, svg_x, svg_y);
// 将OpenVG坐标原点继续平移至指针的旋转中心点(指针圆盘的中心点)
svgTranslate(xform, POINTER_CENTER_OF_ROTATION_X, MAP_SVG_TO_OPENVG(POINTER_CENTER_OF_ROTATION_Y, pointer_cy));
// 将坐标轴旋转至指定的角度
svgRotate(xform, angleExtent);
// 将指针向径向(Y轴)延伸(指针需要在外环中旋转, 该外环内圈至圆环中心点的距离表示为PT_OFF_Y, 移至该位置)
svgTranslate (xform, 0, PT_OFF_Y);
}
// 根据指针指向的角度及旋转中心点计算指针的最小脏矩形区域
static int scanSubBoundingRect(float angleExtent, float cx, float cy, VGint *coords)
{
float xform[6];
// 根据角度及旋转中心点位置计算转换矩阵, 并保存在xform中(SVG格式转换矩阵),
// 该矩阵描述如何将指针SVG空间的点坐标映射到OpenVG空间
calc_magic_transform (angleExtent, cx, cy, xform);
// 将SVG空间的指针包络矩形映射到OpenVG空间, 并求解在OpenVG空间中包含该指针包络矩形的最小矩形
{
// 将SVG空间的指针包络矩形(sub_xmin, sub_ymin, sub_xmax, sub_ymax)映射到OpenVG空间(rect_x, rect_y)
float sx, sy;
float dx, dy;
float xmin, xmax, ymin, ymax;
float rect_x[4], rect_y[4];
sx = sub_xmin; // 左下角
sy = sub_ymin;
// 坐标系变换(包含旋转/平移变换), 将(sx,sy)对应的SVG坐标值转换到(dx,dy)对应的OpenVG空间
svgTransformPoint (&dx, &dy, xform, sx, sy);
rect_x[0] = dx;
rect_y[0] = dy;
sx = sub_xmax; // 右下角
sy = sub_ymin;
svgTransformPoint(&dx, &dy, xform, sx, sy);
rect_x[1] = dx;
rect_y[1] = dy;
sx = sub_xmax; // 右上角
sy = sub_ymax;
svgTransformPoint(&dx, &dy, xform, sx, sy);
rect_x[2] = dx;
rect_y[2] = dy;
sx = sub_xmin; // 右上角
sy = sub_ymax;
svgTransformPoint(&dx, &dy, xform, sx, sy);
rect_x[3] = dx;
rect_y[3] = dy;
// 求解在OpenVG空间中包含该指针包络矩形的最小矩形
// 求解包含rect的最小长方形
xmin = 9999999.0f;
xmax = -9999999.0f;
ymin = 9999999.0f;
ymax = -9999999.0f;
for (int j = 0; j < 4; j++)
{
if (rect_x[j] > xmax)
xmax = rect_x[j];
if(rect_x[j] < xmin)
xmin = rect_x[j];
if (rect_y[j] > ymax)
ymax = rect_y[j];
if (rect_y[j] < ymin)
ymin = rect_y[j];
}
// 考虑计算误差, 避免角度水平/垂直时计算误差
coords[0] = (VGint)(xmin - 1);
coords[1] = (VGint)(ymin - 1);
coords[2] = (VGint)(xmax + 1.0f) - (VGint)xmin + 1;
coords[3] = (VGint)(ymax + 1.0f) - (VGint)ymin + 1;
}
return 1;
}
//static void drawBoundingRect(int coords[4])
//{
// VGfloat col[4];
// float xmin, xmax, ymin, ymax;
// xmin = coords[0];
// ymin = coords[1];
// xmax = coords[0] + coords[2] - 1;
// ymax = coords[1] + coords[3] - 1;
//
// VGPaint stroke; // 描边画笔对象
//
//
// // 调试用途, 绘制剪切矩形
// stroke = vgCreatePaint();
// col[0] = 0;// 0xff / 255.0f; // R
// col[1] = 0xff / 255.0f; // G
// col[2] = 0;// 0xff / 255.0f; // B
// col[3] = 1.00f; // Alpha
// // 纯色类型的画笔
// vgSetParameterfv(stroke, VG_PAINT_COLOR, 4, col);
// vgSetf(VG_STROKE_LINE_WIDTH, 1);
// vgSetPaint(stroke, VG_STROKE_PATH);
// vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
// vgLoadIdentity();
//
//
// // 绘制矩形
// VGubyte sub_bounding_commands[5] = { VG_MOVE_TO, VG_LINE_TO, VG_LINE_TO, VG_LINE_TO, VG_CLOSE_PATH };
// VGfloat sub_bounding_coordinates[8];
// sub_bounding_coordinates[0] = xmin;
// sub_bounding_coordinates[1] = ymin;
// sub_bounding_coordinates[2] = xmax;
// sub_bounding_coordinates[3] = ymin;
// sub_bounding_coordinates[4] = xmax;
// sub_bounding_coordinates[5] = ymax;
// sub_bounding_coordinates[6] = xmin;
// sub_bounding_coordinates[7] = ymax;
//
// VGPath boundingRectPath = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_ALL);
// vgAppendPathData(boundingRectPath, sizeof(sub_bounding_commands) / sizeof(VGubyte), sub_bounding_commands, sub_bounding_coordinates);
// vgDrawPath(boundingRectPath, VG_STROKE_PATH);
// vgDestroyPath(boundingRectPath);
//
// vgDestroyPaint(stroke);
//
//}
// 光晕起始角度定义
#define START_ANGLE (212.0f + 13.0f + 0.1f) // 光晕起始位置对应的角度 (参考vguArc说明)
#define END_ANGLE (-(244.0f + 13.0f*2.0f - 0.1f)) // 光晕结束位置对应的角度 (参考vguArc说明)
// 将仪表时速的刻度映射为光晕扇区展开的角度
// 时速 0.0f ~ 240.0f
// angleExtent 0.0f ~ (END_ANGLE)
static float vehicle_speed_to_angle(float vehicle_speed)
{
VGfloat angleExtent;
angleExtent = 0.0f + (END_ANGLE - 0.0f) * (vehicle_speed - 0.0f) / (240.0f - 0.0f);
return angleExtent;
}
// 将仪表转速的刻度映射为光晕扇区展开的角度
// 转速 0.0f ~ 8.0f
// angleExtent 0.0f ~ (END_ANGLE)
static float rotate_speed_to_angle(float rotate_speed)
{
VGfloat angleExtent;
angleExtent = 0.0f + (END_ANGLE - 0.0f) * (rotate_speed - 0.0f) / (8.0f - 0.0f);
return angleExtent;
}
// 指针 135度 ~ -135度
// angleExtent 与 rotate_angle对应关系
// angleExtent 0.0f ~ (END_ANGLE)
// rotate_angle 135.0f ~ (-135.0f)
// cx, cy为圆形表盘的中心位置坐标
static void drawPointer (VGPath pointerPath, float angleExtent, float cx, float cy)
{
VGfloat rotate_angle;
#ifdef POINTER_FILL_TYPE_COLOR
VGfloat col[4];
#endif
VGPaint fill; // 填充画笔对象
//vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_BETTER);
//vgSeti(VG_IMAGE_QUALITY, VG_IMAGE_QUALITY_BETTER);
// 将扇区张开的角度转换为指针旋转的角度
rotate_angle = 135.0f + ((-135.0f) - 135.0f) * (angleExtent - 0.0f) / (END_ANGLE - 0.0f);
#ifdef POINTER_FILL_TYPE_LINEAR_GRADIENT
// 指针使用线性填充方案
float x0, y0, x1, y1;
// fill_linear_gradient 为SVG画布坐标系的坐标值
x0 = fill_linear_gradient_pointer[0];
y0 = fill_linear_gradient_pointer[1];
x1 = fill_linear_gradient_pointer[2];
y1 = fill_linear_gradient_pointer[3];
// 将SVG画布坐标转换到OpenVG坐标系(自顶向下表示)
y0 = MAP_SVG_TO_OPENVG(y0, pointer_cy);
y1 = MAP_SVG_TO_OPENVG(y1, pointer_cy);
float fill_linear_gradient[4] = { x0, y0, x1, y1 };
// 创建用于指针路径填充使用的画笔对象fill
fill = vgCreatePaint();
// 设置线性梯度渐变填充的转换矩阵(单位矩阵), 因为开始/结束点坐标已转换为基于SVG画布原点的坐标值
vgSeti(VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER);
vgLoadIdentity();
// 设置画笔使用线性梯度渐变填充模式
vgSetParameteri( fill, VG_PAINT_TYPE, VG_PAINT_TYPE_LINEAR_GRADIENT );
// 设置线性梯度渐变的开始/结束点坐标(已转换至OpenVG空间)
vgSetParameterfv(fill, VG_PAINT_LINEAR_GRADIENT, 4, fill_linear_gradient);
//vgSetParameterfv(fill, VG_PAINT_LINEAR_GRADIENT, 4, fill_linear_gradient_pointer);
// 设置画笔对象fill的线性梯度渐变填充的关键点(2个), 每个关键点包含偏移(0表示渐变线上的起始位置1为终点位置),颜色, 透明度
vgSetParameterfv( fill, VG_PAINT_COLOR_RAMP_STOPS, 10, rampStops_pointer );
vgSetParameteri( fill, VG_PAINT_COLOR_RAMP_SPREAD_MODE, VG_COLOR_RAMP_SPREAD_PAD );
// 设置画笔对象fill用于后续的路径填充
vgSetPaint(fill, VG_FILL_PATH);
#elif defined(POINTER_FILL_TYPE_COLOR)
// 指针使用纯色填充方案
// 使用白色绘制指针
// 设置画笔颜色
col[0] = ((POINTER_FILL_COLOR >> 24) & 0xFF) / 255.0f;
col[1] = ((POINTER_FILL_COLOR >> 16) & 0xFF) / 255.0f;
col[2] = ((POINTER_FILL_COLOR >> 8) & 0xFF) / 255.0f;
col[3] = ((POINTER_FILL_COLOR >> 0) & 0xFF) / 255.0f;
vgSetParameterfv (colorPaint, VG_PAINT_COLOR, 4, col);
// 标记画笔对象colorPaint用于下面的路径填充与描边操作
vgSetPaint (colorPaint, VG_FILL_PATH);
#endif
//
vgSeti (VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
vgLoadIdentity();
// 将坐标原点平移至指针SVG的坐标原点
// 根据2个中心点(指针圆盘中心点在SVG平面中的偏移, 指针圆盘中心点在OpenVG中的偏移)的差异计算指针SVG平面相对于OpenVG原点的偏移值
float svg_x = cx - POINTER_CENTER_OF_ROTATION_X;
float svg_y = cy - MAP_SVG_TO_OPENVG(POINTER_CENTER_OF_ROTATION_Y, pointer_cy);
// 将OpenVG坐标原点平移至SVG平面的原点
vgTranslate (svg_x, svg_y);
// 将OpenVG坐标原点继续平移至指针的旋转中心点(指针圆盘的中心点)
vgTranslate (POINTER_CENTER_OF_ROTATION_X, MAP_SVG_TO_OPENVG(POINTER_CENTER_OF_ROTATION_Y, pointer_cy));
// 无缩放
vgScale(1.0f, 1.0f);
// 旋转指针
vgRotate (rotate_angle);
// 将指针向径向(Y轴)延伸(指针需要在外环中旋转, 该外环内圈至圆环中心点的距离表示为PT_OFF_Y, 移至该位置)
vgTranslate (0, PT_OFF_Y);
// 绘制指针(填充模式)
vgDrawPath (pointerPath, VG_FILL_PATH);
// 此处将一个无效句柄置为填充用途, 标记画笔对象colorPaint不再使用用于填充用途, 以便将画笔对象colorPaint的资源释放.
vgSetPaint (VG_INVALID_HANDLE, VG_FILL_PATH);
#ifdef POINTER_FILL_TYPE_LINEAR_GRADIENT
// 删除画笔对象
vgDestroyPaint (fill);
#endif
}
#include "vg_font.h"
#ifdef __cplusplus
extern "C" {
#endif
// 参考".\字体\readme.txt"了解如何从TTF文件中提取指定字符集的字形数据文件
extern Font arial_font; // 运行".\字体\run.bat"创建该demo所需的字形C文件
// 参考".\字体\font_utf8.txt"了解已创建的字形编码
extern Font courbd_font; // 运行".\字体\run_courbd.bat"创建该demo所需的courbd字形C文件
#define my_font arial_font // 使用arial_font字体
#ifdef __cplusplus
}
#endif
// font_w 字形的像素宽度
// font_h 字形的像素高度
// speed 显示的字符串文本
// centre_x centre_y 为效果图中对应的速度文本其显示区域的中心点坐标(相对于效果图左上角原点的偏移)
// color为字符的颜色
static void DrawSpeed(char *speed, int font_w, int font_h, int centre_x, int centre_y, unsigned int color)
{
VGfloat col[4];
int str_size;
VGfloat glyphOrigin[2] = { 0.0f, 0.0f };
float speed_scale_x, speed_scale_y;
// 将效果图中的坐标值转换为VG区域中的坐标值
int vg_offset_x = centre_x;
int vg_offset_y = centre_y;
vg_offset_y = VG_H - 1 - vg_offset_y;
str_size = strlen(speed);
(void)(str_size);
// 计算未缩放前字符串的逻辑总宽度/逻辑高度值信息 (逻辑高度值总是1.0)
vgTextSize (&my_font, speed, &speed_scale_x, &speed_scale_y);
// 字符串像素宽度(按高度值为基准值计算得到的字符串宽度)
float str_width = font_h * (float)speed_scale_x;
// 字符串像素高度
float str_height = font_h / speed_scale_y; // speed_scale_y为1.0
// 获取数字字符'0'的逻辑宽度/高度
vgTextSize (&my_font, "0", &speed_scale_x, &speed_scale_y);
// 计算字体实际宽度/高度设置值引入的缩放系数
float scale_rato = (font_w / speed_scale_x) / (font_h / speed_scale_y);
str_width *= scale_rato;
// 设置车速显示时的文字颜色 (白色)
col[0] = ((color >> 0) & 0xff) / 255.0f; // R
col[1] = ((color >> 8) & 0xff) / 255.0f; // G
col[2] = ((color >> 16) & 0xff) / 255.0f; // B
col[3] = 1.00f;
// 纯色画笔用于路径填充
vgSetParameterfv (colorPaint, VG_PAINT_COLOR, 4, col);
vgSetPaint (colorPaint, VG_FILL_PATH );
vgSeti (VG_FILL_RULE, VG_NON_ZERO);
vgSetfv (VG_GLYPH_ORIGIN, 2, glyphOrigin);
vgSeti (VG_MATRIX_MODE, VG_MATRIX_GLYPH_USER_TO_SURFACE);
vgLoadIdentity();
// 定位显示的原点
vgTranslate (vg_offset_x - str_width / 2, vg_offset_y - str_height / 2);
// 计算字形的放大倍数
// 计算数字字符'0'的逻辑宽度/高度信息及实际显示字符的像素宽度/像素高度信息计算宽度/高度的缩放系数
vgScale(font_w / speed_scale_x, font_h / speed_scale_y);
// arial_font为车速显示使用的字形数据
vgTextOut(&my_font, speed, VG_FILL_PATH);
vgSetPaint(VG_INVALID_HANDLE, VG_FILL_PATH);
}
// 计算表盘0的时速/表盘1的转速脏矩形区域
// coords 脏矩形缓冲区
// cb_coords 脏矩形缓冲区个数
static int process_dial_level_rectangle (int dial, VGint* coords, int cb_coords)
{
if(cb_coords <= 0)
return 0;
if (dial == 0)
{
// (143, 167) (203*116)
coords[0] = 143;
coords[1] = VG_H - (167 + 116);
coords[2] = 203; // W
coords[3] = 116; // H
}
else
{
// (944, 166) (181*113)
coords[0] = 944;
coords[1] = VG_H - (166 + 113);
coords[2] = 181; // W
coords[3] = 113; // H
}
return 1;
}
#define RGB(r,g,b) ((r) | ((g) << 8) | ((b) << 16))
// 绘制仪表盘的速度值
static void draw_dial_speed(float vehicle_speed, float rotate_speed)
{
char text[8];
sprintf (text, "%d", (int)vehicle_speed);
// 时速
DrawSpeed (text, 64, 96, CIRCLE_0_X, CIRCLE_0_Y - 16, RGB(255, 255, 255));
sprintf (text, "%2.1f", rotate_speed);
// 转速
DrawSpeed (text, 64, 96, CIRCLE_1_X, CIRCLE_1_Y - 16, RGB(255, 255, 255));
}
#define MAX_DIRTY_RECTS 16 // 产生的最大脏矩形个数
// 表盘绘制
// vehicle_speed 时速刻度值
// rotate_speed 转速刻度值
// 20211030 NEW_DIRTY_METHOD比较老的脏矩形算法,每帧刷新时间减少2ms左右
#define NEW_DIRTY_METHOD
#ifndef NEW_DIRTY_METHOD
static int process_dial_dirty_rectangles (int dial_no, float old_th, float new_th, VGint* coords, int cb_coords);
#endif
void do_dials_paint (float vehicle_speed, float rotate_speed)
{
VGint coords[MAX_DIRTY_RECTS*4];
int dirty_rects = 0;
#ifdef NEW_DIRTY_METHOD
int total_dirty_count = 0;
VGint *dirty_coords = coords;
int dirty_count;
float rotate_angle;
#endif
float angleExtent_0 = vehicle_speed_to_angle(vehicle_speed);
float angleExtent_1 = rotate_speed_to_angle(rotate_speed);
float startAngle = START_ANGLE;
float old_vehicle_speed, old_rotate_speed;
//vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_BETTER);
//vgSeti(VG_IMAGE_QUALITY, VG_IMAGE_QUALITY_BETTER);
// 获取当前FrameBuffer对应的序号
int current_fb_no = -1;
xm_vg_get_osd_fb (&current_fb_no);
memset (coords, 0, sizeof(coords));
// 保存当前的刻度值
old_vehicle_speed = dial_vehicle_speeds[current_fb_no];
old_rotate_speed = dial_rotate_speeds[current_fb_no];
dial_vehicle_speeds[current_fb_no] = vehicle_speed;
dial_rotate_speeds[current_fb_no] = rotate_speed;
#ifdef NEW_DIRTY_METHOD
total_dirty_count = 0;
dirty_coords = coords;
// 脏矩形列表包含2部分, 1) 最近指针对应的脏矩形(重绘上一个指针区域) 2) 当前指针对应的脏矩形区域
// 计算指针0(vehicle_speeds)的脏矩形区域
//int last_fb = (current_fb_no + 1) & 1;
// 复制上一次的脏矩形列表
if( vehicle_speeds_dirty_rects_count[current_fb_no])
{
// 复制该FB(FrameBuffer)保存的上一次的脏矩形列表, 因为该区域需要被重绘(上一次的指针在此位置显示)
dirty_count = vehicle_speeds_dirty_rects_count[current_fb_no];
memcpy (dirty_coords, vehicle_speeds_dirty_rects_coords[current_fb_no], dirty_count * 4 * sizeof(VGint));
dirty_coords += dirty_count * 4;
total_dirty_count += dirty_count;
}
// 计算新位置的脏矩形列表
// dirty_coords指向保存当前脏矩形区域的缓冲区
// 计算当前角度指针的脏矩形
// 将扇区张开的角度转换为指针旋转的角度
rotate_angle = 135.0f + ((-135.0f) - 135.0f) * (angleExtent_0 - 0.0f) / (END_ANGLE - 0.0f);
dirty_count = scanSubBoundingRect (rotate_angle, CIRCLE_0_X, CIRCLE_0_Y, dirty_coords);
total_dirty_count += dirty_count;
// 更新当前FrameBuffer对应的脏矩形区域, 以便下次重绘该FrameBuffer时将该脏矩形区域包含并重绘
memcpy (vehicle_speeds_dirty_rects_coords[current_fb_no], dirty_coords, dirty_count * 4 * sizeof(VGint));
vehicle_speeds_dirty_rects_count[current_fb_no] = dirty_count;
dirty_coords += dirty_count * 4;
// 计算指针1(rotate_speeds)的脏矩形区域
// 复制上一次的脏矩形列表
if( rotate_speeds_dirty_rects_count[current_fb_no])
{
// 复制该FB(FrameBuffer)保存的上一次的脏矩形列表, 因为该区域需要被重绘(上一次的指针在此位置显示)
dirty_count = rotate_speeds_dirty_rects_count[current_fb_no];
memcpy (dirty_coords, rotate_speeds_dirty_rects_coords[current_fb_no], dirty_count * 4 * sizeof(VGint));
dirty_coords += dirty_count * 4;
total_dirty_count += dirty_count;
}
// 计算新位置的脏矩形列表
// dirty_coords指向保存当前脏矩形区域的缓冲区
// 计算当前角度指针的脏矩形
// 将扇区张开的角度转换为指针旋转的角度
rotate_angle = 135.0f + ((-135.0f) - 135.0f) * (angleExtent_1 - 0.0f) / (END_ANGLE - 0.0f);
dirty_count = scanSubBoundingRect (rotate_angle, CIRCLE_1_X, CIRCLE_1_Y, dirty_coords);
total_dirty_count += dirty_count;
// 更新当前FrameBuffer对应的脏矩形区域, 以便下次重绘该FrameBuffer时将该脏矩形区域包含并重绘
memcpy (rotate_speeds_dirty_rects_coords[current_fb_no], dirty_coords, dirty_count * 4 * sizeof(VGint));
rotate_speeds_dirty_rects_count[current_fb_no] = dirty_count;
// 累加表盘0时速值显示区域的矩形区域
total_dirty_count += process_dial_level_rectangle(0, coords + total_dirty_count * 4, MAX_DIRTY_RECTS - total_dirty_count);
// 累加表盘1转速值显示区域的矩形区域
total_dirty_count += process_dial_level_rectangle(1, coords + total_dirty_count * 4, MAX_DIRTY_RECTS - total_dirty_count);
#else
// 脏矩形计算, 使用脏矩形优化GPU显示
// 复位脏矩形统计
memset (coords, 0, sizeof(coords));
dirty_rects = 0;
// 统计时速表光晕/指针变化导致的脏矩形
dirty_rects = process_dial_dirty_rectangles (0, old_vehicle_speed, vehicle_speed, coords, MAX_DIRTY_RECTS);
// 累加转速表光晕/指针变化导致的脏矩形
dirty_rects += process_dial_dirty_rectangles (1, old_rotate_speed, rotate_speed, coords + dirty_rects * 4, MAX_DIRTY_RECTS - dirty_rects);
// 累加表盘0时速值显示区域的矩形区域
dirty_rects += process_dial_level_rectangle(0, coords + dirty_rects * 4, MAX_DIRTY_RECTS - dirty_rects);
// 累加表盘1转速值显示区域的矩形区域
dirty_rects += process_dial_level_rectangle(1, coords + dirty_rects * 4, MAX_DIRTY_RECTS - dirty_rects);
#endif
// 计算scissoring rectangles(裁减区)
if (full_dial_output_count > 0)
{
// 第一次/第二次输出, 全区域元素输出
// VG关闭裁剪区
vgSeti(VG_SCISSORING, VG_FALSE);
full_dial_output_count --;
}
else if(vehicle_speed == old_vehicle_speed && rotate_speed == old_rotate_speed)
{
// 转速未改变时无需重绘
return;
}
else
{
// 部分区域输出
// 设置当前GPU渲染的裁剪区(输出仅在裁剪区中的被处理),优化GPU存取效率
// 使能裁剪区
vgSeti(VG_SCISSORING, VG_TRUE);
// 定义4个裁减矩形
#ifdef NEW_DIRTY_METHOD
vgSetiv(VG_SCISSOR_RECTS, total_dirty_count * 4, coords);
#else
vgSetiv(VG_SCISSOR_RECTS, dirty_rects * 4, coords);
#endif
}
// 计算表盘脏矩形对ICON状态的影响
// 若表盘脏矩形与某个ICON存在交叉, 则该ICON需要重新绘制
scan_and_mark_icons_polluted_by_dial_paint (coords, dirty_rects);
// 1 绘制指针的背景图
// 设置绘制图像与绘制表面为1:1映射模式(完全覆盖)
vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL);
VGfloat m[9];
vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
vgLoadIdentity(); // 单位矩阵
// 将自底向上转换为自顶向下, 因为vgDrawImageDirect使用的image是自顶向下
vgGetMatrix(&m[0]);
m[4] = -1;
m[7] = VG_H;
vgLoadMatrix(&m[0]);
vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL);
// 绘制背景Image对象
// 直接绘制, 注意源图像必须满足地址64字节对齐, stride 满足16像素对齐
vgDrawImageDirect ((char *)VG_IMAGE_DATA(bkimg), // 背景源图像基地址, 必须为64字节对齐
VG_IMAGE_WIDTH(bkimg),
VG_IMAGE_HEIGHT(bkimg),
VG_IMAGE_STRIDE(bkimg),
// 源图像开窗设置(全画面输出)
0,
0,
VG_IMAGE_WIDTH(bkimg),
VG_IMAGE_HEIGHT(bkimg),
VG_IMAGE_QUALITY_BETTER,
VG_IMAGE_FORMAT(bkimg)
);
// 绘制表盘0
vgLoadIdentity(); // 单位矩阵
// 叠加光晕效果
drawHalo (startAngle, angleExtent_0, CIRCLE_0_X, MAP_SVG_TO_OPENVG(CIRCLE_0_Y,VG_H));
// 叠加指针
drawPointer (pointerPath_0, angleExtent_0 , CIRCLE_0_X, CIRCLE_0_Y);
// 20211028 Bug修复
// halo底图(同一个Image对象)在两处(表盘0与表盘1)被引用绘制, 因为其坐标映射存在不同, 需要串行使用, 否则VG底层检查该异常并报告"gcoOS_DebugBreak".
// 此时插入vgFinish确保表盘1在表盘0绘制后开始绘制, 避免IMAGE对象资源被同时使用. (绘制时间会有少许增加)
// 或者使用2个不同的halo Image对象规避资源共享异常
vgFinish ();
// 绘制表盘1
// 叠加光晕效果
drawHalo (startAngle, angleExtent_1, CIRCLE_1_X, MAP_SVG_TO_OPENVG(CIRCLE_1_Y,VG_H));
// 叠加指针1
// 使用与表盘0相同的指针对象
//drawPointer (pointerPath_0, angleExtent_1, CIRCLE_1_X, CIRCLE_1_Y);
// 使用与表盘0不同的指针对象
drawPointer (pointerPath_1, angleExtent_1, CIRCLE_1_X, CIRCLE_1_Y);
// 绘制时速/转速信息
draw_dial_speed(vehicle_speed, rotate_speed);
#ifdef NEW_DIRTY_METHOD
//drawBoundingRect (vehicle_speeds_dirty_rects_coords[current_fb_no]);
//drawBoundingRect (rotate_speeds_dirty_rects_coords[current_fb_no]);
#endif
}
int double_pointer_halo_draw (void)
{
static float vehicle_speed = 0.0f;
static int v_dir = 0;
static float rotate_speed = 1.0;
static int r_dir = 0;
// 绘制双表盘
do_dials_paint (vehicle_speed, rotate_speed);
// 此处需要加入vgFinish指令刷新等待上述双仪表绘制时未完成的或者未释放的资源(不等待会导致内存资源需求更大或者画面刷新存在异常)
//vgFinish();
// 绘制ICONs
draw_icons();
draw_message();
vgFinish();
// for (angleExtent = 0.0; angleExtent >= -244.0f; angleExtent -= 1.0f)
//while(1)
{
//for (angleExtent = -2.0; angleExtent >= -242.0f; angleExtent -= 1.0f)
{
//vehicle_speed = 118.0f;
//if ( vgGetError() == VG_NO_ERROR )
// eglSwapBuffers( egldisplay, eglsurface );
//Sleep (10);
if(v_dir == 0 && vehicle_speed < 240.0f)
{
// 顺时针
vehicle_speed += 0.9f;
if (vehicle_speed >= 240.0f)
{
vehicle_speed = 240.0f;
v_dir = 1;
}
}
else if (v_dir == 1 && vehicle_speed > 0.0f)
{
// 逆时针
vehicle_speed -= 0.9f;
if (vehicle_speed <= 0.0f)
{
vehicle_speed = 0.0f;
v_dir = 0;
}
}
if (r_dir == 0 && rotate_speed < 8.0f)
{
rotate_speed += 0.02f;
if (rotate_speed >= 8.0f)
{
rotate_speed = 8.0f;
r_dir = 1;
}
}
else if (r_dir == 1 && rotate_speed > 0.0f)
{
rotate_speed -= 0.02f;
if (rotate_speed <= 0.0f)
{
rotate_speed = 0.0f;
r_dir = 0;
}
}
}
}
icon_state_simulate ();
return 0;
}
int double_pointer_halo_init (int width, int height)
{
vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_BETTER);
vgSeti(VG_IMAGE_QUALITY, VG_IMAGE_QUALITY_BETTER);
vgSeti(VG_PIXEL_LAYOUT, VG_PIXEL_LAYOUT_RGB_VERTICAL );
// ARGB8888格式光晕底图
//
// PNG格式设计图
// ".\image\原始设计数据\背景\halo.png"
// 运行".\image\原始设计数据\背景\run.bat", 将PNG格式的设计底图转换为ARGB8888格式
// ARGB8888格式BIN文件 ".\image\原始设计数据\背景\PNG2VG\ARGB8888\ROM_IMAGE_HALO_ARGB8888.ARGB8888"
// 将该文件复制至 ".\rom\image\"用于ROM.BIN文件制作
// ARGB8888格式BIN文件 ".\rom\image\ROM_IMAGE_HALO_ARGB8888.ARGB8888"
halo = (unsigned char *)XM_RomAddress(ROM_IMAGE_HALO_ARGB8888); // ARGB8888格式
// RGB565格式背景图
//
// PNG格式设计图
// 1) 原始设计底图 32位PNG格式
// ".\image\原始设计数据\背景\BG_RGB32.png"
// 2) 已转换到RGB16的PNG格式
// .\image\原始设计数据\背景\BGRGB565.png"
// 参考"\image\原始设计数据\背景\readme.txt"了解如何将图像颜色从RGB32转换到RGB16,且尽可能保留细节
//
// 运行".\image\原始设计数据\背景\run.bat", 将PNG格式的设计底图转换为RGB565格式
// RGB565格式BIN文件 ".\image\原始设计数据\背景\PNG2VG\RGB565\BGRGB565.RGB565"
// 将该文件复制至 ".\rom\image\"用于ROM.BIN文件制作
// RGB565格式BIN文件 ".\rom\image\BGRGB565.RGB565"
bkimg = (unsigned short *)XM_RomAddress(ROM_IMAGE_BGRGB565_RGB565);
genPointerPaths();
// 计算指针的包络矩形区域
calcPointerBoundingRect();
vgFontInit();
init_icons();
dial_vehicle_speeds[0] = 0.0f;
dial_vehicle_speeds[1] = 0.0f;
dial_rotate_speeds[0] = 0.0f;
dial_rotate_speeds[1] = 0.0f;
full_dial_output_count = 2;
return 0;
}
int double_pointer_halo_exit (void)
{
vgFontExit();
return 0;
}
// 脏区域矩形定义, 主要用于通过限定某个区域输出, 大幅较少GPU对DDR的访问
typedef struct {
// 与该脏区域矩形相关的参数值的限定范围. 如转速表 (1000 ~ 2000), 时速表 (0 ~ 40)
float th_min; // 最小值
float th_max; // 最大值
// 脏区域矩形坐标(屏幕坐标)及尺寸, (自顶向下坐标系中定义, Windows的画笔程序使用)
VGint x;
VGint y;
VGint w;
VGint h;
} VG_DIRTY_RECT;
#ifndef NEW_DIRTY_METHOD
// 表盘0脏矩形区域定义(与该表盘时速刻度值关联)
// 定义了6个脏矩形区域 (可以细化,定义更多的刻度范围与矩形区域对应关系)
// 每次刻度值变化时, 仅绘制对应的矩形区域
// 定义脏矩形时应包含该次光晕与指针的变化
// 表盘0的刻度范围为(0.0 ~ 240.0)
static const VG_DIRTY_RECT dial_0_rects[] = {
// 按照th_min的递增顺序制作
{
0.0, 40.0, // 时速刻度值的刻度范围(从刻度0到刻度40)
8, 211, 173, 202 // 包含上述刻度范围(从刻度0到刻度40)的矩形区域
},
{
40.0, 80.0, // 时速刻度值的刻度范围(从刻度40到刻度80)
9, 50, 182, 228 // 包含上述刻度范围(从刻度40到刻度80)的矩形区域
},
{
80.0, 120.0,
65, 8, 215, 167
},
{
120.0, 160.0,
220, 8, 187, 157
},
{
160.0, 200.0,
312, 76, 177, 185
},
{
200.0, 240.0,
319, 223, 157, 184
},
};
// 表盘1脏矩形区域定义(与该表盘转速刻度值关联)
// 定义了8个脏矩形区域 (可以细化,定义更多的刻度范围与矩形区域对应关系)
// 表盘1的刻度范围为(0.0 ~ 8.0)
static const VG_DIRTY_RECT dial_1_rects[] = {
// 按照th_min的递增顺序制作
{
0.0, 1.0, // 转速刻度值的限定区域(从刻度0到刻度1)
798, 249, 161, 148 // 包含上述刻度范围(从刻度0到刻度1)的矩形区域
},
{
1.0, 2.0,
811, 133, 126, 161
},
{
2.0, 3.0,
824, 32, 164, 175
},
{
3.0, 4.0,
857, 13, 198, 137
},
{
4.0, 5.0,
1017, 13, 154, 145
},
{
5.0, 6.0,
1095, 53, 157, 147
},
{
6.0, 7.0,
1138, 145, 122, 156
},
{
7.0, 8.0,
1119, 246, 135, 153
},
};
static int get_dirty_index (int dial_no, float th)
{
const VG_DIRTY_RECT *rects = NULL;
int index, count;
if(dial_no == 0) // 时速, 以40为等分, 参考 dial_0_rects
{
rects = dial_0_rects;
count = sizeof(dial_0_rects) / sizeof(dial_0_rects[0]);
}
else // 转速, 以1.0为等分, 参考 dial_1_rects
{
rects = dial_1_rects;
count = sizeof(dial_1_rects) / sizeof(dial_1_rects[0]);
}
for (index = 0; index < count; index++)
{
if (th >= rects[index].th_min && th < rects[index].th_max)
return index;
}
return count - 1;
}
// 根据指针的新旧刻度值计算最少数量的脏矩形区域
// dial_no 表盘序号
// old_th, new_th 为新旧2个刻度值
// coords 保存求解出的一个或多个脏矩形区域集 (x,y,w,h)
// count 脏矩形集缓冲区的个数
// 返回值
// 已计算的脏矩形个数
static int process_dial_dirty_rectangles (int dial_no, float old_th, float new_th, VGint* coords, int cb_coords)
{
int count;
int dirty_counts = 0;
const VG_DIRTY_RECT *rects = NULL;
const VG_DIRTY_RECT *rect;
int old_index, new_index;
if(old_th > new_th)
{
float t = new_th;
new_th = old_th;
old_th = t;
}
if(dial_no == 0) // 时速, 以40为等分, 参考 dial_0_rects
{
rects = dial_0_rects;
count = sizeof(dial_0_rects) / sizeof(dial_0_rects[0]);
}
else // 转速, 以1.0为等分, 参考 dial_1_rects
{
rects = dial_1_rects;
count = sizeof(dial_1_rects) / sizeof(dial_1_rects[0]);
}
(void)(count);
old_index = get_dirty_index (dial_no, old_th);
new_index = get_dirty_index (dial_no, new_th);
// 遍历并输出新/旧值之间的每个脏矩形, 这些区域均需要刷新
while (cb_coords > 0 && old_index <= new_index)
{
rect = rects + old_index;
coords[0] = rect->x;;
coords[1] = VG_H - (rect->y + rect->h); // 自顶向下-->自底向上
coords[2] = rect->w;
coords[3] = rect->h;
coords += 4;
cb_coords -= 1;
dirty_counts ++;
old_index ++;
}
return dirty_counts;
}
#endif
typedef struct _icon_image {
unsigned int id; // 资源的唯一标识
char name[32]; // rom 文件名
float x; // 显示位置
float y;
unsigned int state[2]; // 保存与VG的2个FrameBuffer对应的ICON的最近状态值.
// 比较该值与ICON的最新状态值, 决定是否刷新该ICON对应的Surface区域并更新为ICON的最新状态值
} ICON_IMAGE;
enum {
ICON_OIL = 0,
ICON_WATER_TEMP,
ICON_CAR,
ICON_LEFT_FRONT,
ICON_LEFT_REAR,
ICON_RIGHT_FRONT,
ICON_RIGHT_REAR,
ICON_LEFT,
ICON_RIGHT,
ICON_ABS,
ICON_ENGINE, // 发动机
ICON_ENGINE_OIL, // 机油
ICON_LOWER_BEAM, // 近光
ICON_STOP_LAMP, // 刹车灯
ICON_WIDTH_LAMP, // 示宽灯
ICON_DOUBLE_FLASHING, // 双闪
ICON_FOGLIGHT, // 雾灯
ICON_HIGH_BEAM, // 远光
ICON_OIL_LEVEL, // 油耗标尺
ICON_WATER_LEVEL, // 水温标尺
ICON_PERCENT,
ICON_MODE, // 模式
ICON_GEAR, // 档位
ICON_ODO, // 里程
ICON_TEMP,
ICON_COUNT
};
static ICON_IMAGE icon_image[ICON_COUNT] = {
{
ICON_OIL,
ROM_IMAGE_OIL_HIGH_ARGB8888,
234, 395,
-1, -1,
},
{
ICON_WATER_TEMP,
ROM_IMAGE_WATER_TEMP_HIGH_ARGB8888,
1024, 395,
-1, -1,
},
{
ICON_CAR,
ROM_IMAGE_CAR_ARGB8888,
586, 154.94,
-1, -1,
},
{
ICON_LEFT_FRONT,
ROM_IMAGE_LEFT_FRONT_ARGB8888,
552, 214,
-1, -1,
},
{
ICON_LEFT_REAR,
ROM_IMAGE_LEFT_REAR_ARGB8888,
552, 268,
-1, -1,
},
{
ICON_RIGHT_FRONT,
ROM_IMAGE_RIGHT_FRONT_ARGB8888,
676, 214,
-1, -1,
},
{
ICON_RIGHT_REAR,
ROM_IMAGE_RIGHT_REAR_ARGB8888,
676, 268,
-1, -1,
},
{
ICON_LEFT,
ROM_IMAGE_LEFT_1_ARGB8888,
20, 20,
-1, -1,
},
{
ICON_RIGHT,
ROM_IMAGE_RIGHT_1_ARGB8888,
1190, 20,
-1, -1,
},
// ABS
{
ICON_ABS,
ROM_IMAGE_ABS_ARGB8888,
662, 93,
-1, -1,
},
// 发动机
{
ICON_ENGINE,
ROM_IMAGE_ENGINE_ARGB8888,
480, 93,
-1, -1,
},
// 机油
{
ICON_ENGINE_OIL,
ROM_IMAGE_ENGINE_OIL_ARGB8888,
571, 93,
-1, -1,
},
// 近光
{
ICON_LOWER_BEAM,
ROM_IMAGE_LOWER_BEAM_ARGB8888,
430, 22,
-1, -1,
},
// 刹车灯
{
ICON_STOP_LAMP,
ROM_IMAGE_STOP_LAMP_ARGB8888,
616, 22,
-1, -1,
},
// 示宽灯
{
ICON_WIDTH_LAMP,
ROM_IMAGE_WIDTH_LAMP_ARGB8888,
709, 22,
-1, -1,
},
// 双闪
{
ICON_DOUBLE_FLASHING,
ROM_IMAGE_DOUBLE_FLASHING_ARGB8888,
753, 93,
-1, -1,
},
// 雾灯
{
ICON_FOGLIGHT,
ROM_IMAGE_FOGLIGHT_ARGB8888,
523, 22,
-1, -1,
},
// 远光
{
ICON_HIGH_BEAM,
ROM_IMAGE_HIGH_BEAM_ARGB8888,
802, 22,
-1, -1,
},
// 油耗标尺
{
ICON_OIL_LEVEL,
ROM_IMAGE_OIL_LEVEL_1_ARGB8888,
156, 408,
-1, -1
},
// 水温标尺
{
ICON_WATER_LEVEL,
ROM_IMAGE_OIL_LEVEL_1_ARGB8888,
948, 408,
-1, -1
},
// PERCENT
{
ICON_PERCENT,
"\0",
13 + 81/2, 430 + 26/2,
-1, -1
},
// MODE
{
ICON_MODE,
"\0",
0, 0,
-1, -1
},
// GEARE
{
ICON_GEAR,
"\0",
0, 0,
-1, -1
},
// ODO
{
ICON_ODO,
"\0",
0, 0,
-1, -1
},
// TEMP
{
ICON_TEMP,
"\0",
0, 0,
-1, -1
}
};
// 保存ICON对应的最新状态值
static unsigned int icon_states[ICON_COUNT];
// 设置icon的最新状态值
void set_icon_state (int icon, unsigned int state)
{
if(icon < 0 || icon >= ICON_COUNT)
return;
icon_states[icon] = state;
}
// 复位ICON的显示状态
void reset_icon_state (void)
{
int i;
for (i = 0; i < sizeof(icon_image)/sizeof(icon_image[0]); i ++)
{
icon_image[i].state[0] = -1; // 与FB0(FrameBuffer 0)对应的ICON状态值
icon_image[i].state[1] = -1; // 与FB1对应的ICON状态值
}
}
// 扫描所有被表盘绘制过程污染的ICON并标记
static void scan_and_mark_icon (ICON_IMAGE *icon_image, int fb_no, VGint* coords, int cb_coords)
{
int i;
// 转换为OpenVG坐标
char *image = (char *)XM_RomAddress(icon_image->name);
if(image == NULL)
return;
float ix = icon_image->x;
float iy = VG_H - (icon_image->y + VG_IMAGE_HEIGHT(image));
float iw = VG_IMAGE_WIDTH(image);
float ih = VG_IMAGE_HEIGHT(image);
if(iw == 0 || ih == 0)
return;
for (i = 0; i < cb_coords; i ++)
{
float dx = coords[0];
float dy = coords[1];
float dw = coords[2];
float dh = coords[3];
if( ((ix + iw) <= dx) // icon在脏区的左侧
|| (ix >= (dx + dw)) // icon在脏区的右侧
|| ((iy + ih) <= dy) // icon在脏区的上侧
|| (iy >= (dy + dh)) // icon在脏区的下侧
)
{
// 无交集
}
else
{
// 有交集
// 标记该ICON的最近状态值为-1, 强制其重新绘制
icon_image->state[fb_no] = -1;
return;
}
coords += 4;
}
}
// 重新绘制icon对应位置的背景图
static void draw_icon_background (int icon_id)
{
int i;
for (i = 0; i < sizeof(icon_image) / sizeof(icon_image[0]); i++)
{
if (icon_image[i].id == icon_id)
{
vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
// 将OpenVG坐标系转换到Windows的画笔坐标系(自左向右 自顶向下, 与SVG相同)
vgLoadIdentity();
VGfloat m[9];
vgGetMatrix(&m[0]);
m[4] = -1;
m[7] = VG_H;
vgLoadMatrix(&m[0]);
// 平移至ICON待显示区域的左上角(该坐标为该ICON在LCD显示屏上的起始坐标)
vgTranslate(icon_image[i].x, icon_image[i].y );
vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL);
char *image = (char *)XM_RomAddress(icon_image[i].name);
if(image == NULL)
return;
// // 从背景图中挖出与ICON位置对应的区域重新填充背景, 背景图为RGB565格式
// 直接绘制, 注意源图像必须满足地址64字节对齐, stride 满足16像素对齐
vgDrawImageDirect ((char *)VG_IMAGE_DATA(bkimg), // 背景源图像基地址, 必须为64字节对齐
VG_IMAGE_WIDTH(bkimg), // 源图像像素宽度
VG_IMAGE_HEIGHT(bkimg), // 源图像像素高度
VG_IMAGE_STRIDE(bkimg), // 源图像行字节长度, 必须满足16像素对齐
(VGint)icon_image[i].x, // 源图像开窗参数,
(VGint)icon_image[i].y,
(VGint)VG_IMAGE_WIDTH(image),
(VGint) VG_IMAGE_HEIGHT(image),
VG_IMAGE_QUALITY_BETTER, // 图像渲染质量
VG_IMAGE_FORMAT(bkimg) // 源图像格式
);
return;
}
}
}
// 绘制icon前景图
static void draw_icon_foreground (int icon_id)
{
int i;
for (i = 0; i < sizeof(icon_image) / sizeof(icon_image[0]); i++)
{
if (icon_image[i].id == icon_id)
{
vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
// 将OpenVG坐标系转换到Windows的画笔坐标系(自左向右 自顶向下, 与SVG相同)
vgLoadIdentity();
VGfloat m[9];
vgGetMatrix(&m[0]);
m[4] = -1;
m[7] = VG_H;
vgLoadMatrix(&m[0]);
// 平移至ICON待显示区域的左上角(该坐标为该ICON在LCD显示屏上的起始坐标)
vgTranslate(icon_image[i].x, icon_image[i].y );
vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL);
// 获取待绘制的ICON图像基址
char *image = (char *)XM_RomAddress(icon_image[i].name);
if(image == NULL)
return;
// 直接绘制, 注意源图像必须满足地址64字节对齐, stride 满足16像素对齐
vgDrawImageDirect ((char *)VG_IMAGE_DATA(image), // 源图像基地址, 必须为64字节对齐
VG_IMAGE_WIDTH(image), // 源图像像素宽度
VG_IMAGE_HEIGHT(image), // 源图像像素高度
VG_IMAGE_STRIDE(image), // 源图像行字节长度, 必须满足16像素对齐
(VGint)0, // 源图像开窗参数,
(VGint)0,
(VGint)VG_IMAGE_WIDTH(image),
(VGint)VG_IMAGE_HEIGHT(image),
VG_IMAGE_QUALITY_BETTER, // 图像渲染质量
// 源图像格式
VG_IMAGE_FORMAT(image)
);
return;
}
}
}
// 扫描所有被表盘绘制污染的ICON并标记
static void scan_and_mark_icons_polluted_by_dial_paint (VGint* coords, int cb_coords)
{
int i;
// 获取当前FrameBuffer对应的序号
int current_fb_no = -1;
xm_vg_get_osd_fb (&current_fb_no);
for (i = 0; i < sizeof(icon_image)/sizeof(icon_image[0]); i ++)
{
scan_and_mark_icon (icon_image + i, current_fb_no, coords, cb_coords);
}
}
// 油耗/水温标尺
static const char *imageOilLevel[4] = {
ROM_IMAGE_OIL_LEVEL_1_ARGB8888,
ROM_IMAGE_OIL_LEVEL_2_ARGB8888,
ROM_IMAGE_OIL_LEVEL_3_ARGB8888,
ROM_IMAGE_OIL_LEVEL_4_ARGB8888
};
// 绘制油耗/水温标尺
static void draw_oil_level_icon (int icon_id)
{
// 获取当前FrameBuffer对应的序号
int current_fb_no = -1;
xm_vg_get_osd_fb (&current_fb_no);
ICON_IMAGE *icon = icon_image + icon_id;
// 检查ICON的最新状态值是否与FB相关的最近状态值相同
unsigned int last_state = icon_states[icon_id];
if(last_state != icon_image[icon_id].state[current_fb_no])
{
icon_image[icon_id].state[current_fb_no] = last_state;
// 将原点移至图片的左上角
vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
vgLoadIdentity();
VGfloat m[9];
vgGetMatrix(&m[0]);
m[4] = -1;
m[7] = VG_H;
vgLoadMatrix(&m[0]);
vgTranslate(icon->x, icon->y);
// 覆盖模式
vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL);
// 绘制标尺状态 (此处使用的是背景色为黑色的图片,绘制时对应的图标被污染, 因此绘制后需要重绘标尺图标)
// 改为背景色为透明色的图片, 可以不需要重绘时对应的图标
//draw_icon_background (icon_id);
if (icon_id == ICON_OIL_LEVEL)
{
// 重新填充背景
draw_icon_background (icon_id);
// PNG绘制
char *image = (char *)XM_RomAddress(imageOilLevel[last_state]);
if(image == NULL)
return;
// 直接绘制, 注意源图像必须满足地址64字节对齐, stride 满足16像素对齐
vgDrawImageDirect ((char *)VG_IMAGE_DATA(image),
VG_IMAGE_WIDTH(image),
VG_IMAGE_HEIGHT(image),
VG_IMAGE_STRIDE(image),
// 源图像开窗设置
0,
0,
VG_IMAGE_WIDTH(image),
VG_IMAGE_HEIGHT(image),
VG_IMAGE_QUALITY_BETTER,
VG_IMAGE_FORMAT(image)
);
}
else if(icon_id == ICON_WATER_LEVEL)
{
// 重新填充背景
draw_icon_background (icon_id);
// PNG绘制
char *image = (char *)XM_RomAddress(imageOilLevel[last_state]);
if(image == NULL)
return;
// 直接绘制, 注意源图像必须满足地址64字节对齐, stride 满足16像素对齐
vgDrawImageDirect ((char *)VG_IMAGE_DATA(image),
VG_IMAGE_WIDTH(image),
VG_IMAGE_HEIGHT(image),
VG_IMAGE_STRIDE(image),
// 源图像开窗设置
0,
0,
VG_IMAGE_WIDTH(image),
VG_IMAGE_HEIGHT(image),
VG_IMAGE_QUALITY_BETTER,
VG_IMAGE_FORMAT(image)
);
}
// 强制油温图标重绘
if(icon_id == ICON_OIL_LEVEL)
{
draw_icon_foreground (ICON_OIL);
}
// 强制水温图标重绘
else if(icon_id == ICON_WATER_LEVEL)
{
draw_icon_foreground (ICON_WATER_TEMP);
}
}
}
static void init_icons (void)
{
int i;
for (i = 0; i < ICON_COUNT; i ++)
{
icon_states[i] = 1;
}
// 复位icon状态
reset_icon_state ();
}
static void draw_icon (int icon_id)
{
int i;
// 获取当前FrameBuffer对应的序号
int current_fb_no = -1;
xm_vg_get_osd_fb(&current_fb_no);
for (i = 0; i < sizeof(icon_image) / sizeof(icon_image[0]); i++)
{
if (icon_image[i].id == icon_id)
{
// 检查ICON的最新状态值是否与FB相关的最近状态值相同
unsigned int last_state = icon_states[i];
int fill_back_and_then_fill_fore = 0; // 填充背景再绘制前景模式, 当icon被表盘刷新过程污染时
if (icon_image[i].state[current_fb_no] == -1)
fill_back_and_then_fill_fore = 1;
if (last_state != icon_image[i].state[current_fb_no])
{
// ICON状态值存在变化
// 更新与当前FB相关的ICON最近状态值
icon_image[i].state[current_fb_no] = last_state;
// 是否填充背景
if (last_state == 0 || fill_back_and_then_fill_fore) // 填充背景
{
draw_icon_background (icon_id);
}
if(last_state == 1)
{
draw_icon_foreground (icon_id);
}
}
else
{
// ICON状态值未变化
}
return;
}
}
}
// 空格字符(空格/TAB)的数据请手工加入到字体C文件(按照字符的Unicode编码顺序), 其中宽度信息(0.556152f)请按照参考其他字符定义
// { 32, { 0.556152f, 0.000000f }, 0, NULL, 0, NULL, VG_NON_ZERO },
// 加入后请修改number of glyphs
// number of glyphs
// 65+1,
static void draw_message (void)
{
// 获取当前FrameBuffer对应的序号
int current_fb_no = -1;
xm_vg_get_osd_fb (&current_fb_no);
if(icon_image[ICON_PERCENT].state[current_fb_no] != icon_states[ICON_PERCENT])
{
char info[5];
icon_image[ICON_PERCENT].state[current_fb_no] = icon_states[ICON_PERCENT];
sprintf (info, "%d%%", icon_image[ICON_PERCENT].state[current_fb_no]);
//
VGfloat clrcolor[4] = {0.0f, 0.0f, 0.0f, 1.0f}; // 设置clear颜色(黑色)
vgSetfv (VG_CLEAR_COLOR, 4, clrcolor);
vgClear (13, VG_H - (430 + 26 + 3), 81, 26); // 清除背景 (13,430) (81, 26)
vgFlush();
DrawSpeed (info, 16, 22, 13 + 81/2, 430 + 26/2, RGB(255, 255, 255));
}
// 模式
if(icon_image[ICON_MODE].state[current_fb_no] != icon_states[ICON_MODE])
{
icon_image[ICON_MODE].state[current_fb_no] = icon_states[ICON_MODE];
VGfloat clrcolor[4] = {0.0f, 0.0f, 0.0f, 1.0f};
vgSetfv (VG_CLEAR_COLOR, 4, clrcolor);
vgClear (476, VG_H - (429+34), 119, 34);
vgFlush();
DrawSpeed ("SNOW", 18, 24, 476 + 119/2, 429 + 34/2, RGB(4, 193, 252));
}
if(icon_image[ICON_GEAR].state[current_fb_no] != icon_states[ICON_GEAR])
{
icon_image[ICON_GEAR].state[current_fb_no] = icon_states[ICON_GEAR];
VGfloat clrcolor[4] = {0.0f, 0.0f, 0.0f, 1.0f};
vgSetfv (VG_CLEAR_COLOR, 4, clrcolor);
vgClear (597, VG_H - (411+59), 86, 59);
vgFlush();
DrawSpeed ("D", 28, 36, 597 + 86/2, 411 + 59/2, RGB(255, 255, 255));
}
if(icon_image[ICON_ODO].state[current_fb_no] != icon_states[ICON_ODO])
{
icon_image[ICON_ODO].state[current_fb_no] = icon_states[ICON_ODO];
VGfloat clrcolor[4] = {0.0f, 0.0f, 0.0f, 1.0f};
vgSetfv (VG_CLEAR_COLOR, 4, clrcolor);
vgClear (685, VG_H - (430+34), 247, 34);
vgFlush();
char info[24];
// 注意: 以下文字中的空格字符 ' ' 需手工在字形代码文件将其加入
//
// 空格字符(空格/TAB)的数据请手工加入到字体C文件(按照字符的Unicode编码顺序), 其中宽度信息(0.556152f)请按照参考其他字符定义
// 如字体的宽度为16, 此处宽度信息(0.556152f), 则空格的像素宽度 = 16 * 0.556152f = 8
// { 32, { 0.556152f, 0.000000f }, 0, NULL, 0, NULL, VG_NON_ZERO },
// 加入后请修改字体数据结构的number of glyphs, 将其值加1
// number of glyphs
sprintf (info, "ODO%6d km", icon_image[ICON_ODO].state[current_fb_no]);
DrawSpeed (info, 16, 22, 685 + 247/2, 430 + 34/2, RGB(128, 128, 128));
}
if(icon_image[ICON_TEMP].state[current_fb_no] != icon_states[ICON_TEMP])
{
icon_image[ICON_TEMP].state[current_fb_no] = icon_states[ICON_TEMP];
VGfloat clrcolor[4] = {0.0f, 0.0f, 0.0f, 1.0f};
vgSetfv (VG_CLEAR_COLOR, 4, clrcolor);
vgClear (1187, VG_H - (430+26), 81, 26); // (1187, 430, 81, 26)
vgFlush();
char info[24];
// \xE2\x84\x83是摄氏度符号的UTF8编码
// 注意: 测试用例摄氏度符号的字形数据来自 ARIALUNI.TTF, 因为arial.ttf无此字形
sprintf (info, "%3d\xE2\x84\x83", icon_image[ICON_TEMP].state[current_fb_no]);
DrawSpeed (info, 16, 22, 1187 + 81/2, 430 + 26/2, RGB(255, 255, 255));
}
}
// 绘制车门状态
static void draw_card_door_status (void)
{
int do_refresh = 0;
// 获取当前FrameBuffer对应的序号
int current_fb_no = -1;
xm_vg_get_osd_fb (&current_fb_no);
// 检查车门状态是否存在变化
unsigned int last_state;
do
{
// 读取最新的状态
last_state = icon_states[ICON_LEFT_FRONT];
if(last_state != icon_image[ICON_LEFT_FRONT].state[current_fb_no])
{
icon_image[ICON_LEFT_FRONT].state[current_fb_no] = last_state;
do_refresh = 1;
}
last_state = icon_states[ICON_RIGHT_FRONT];
if(last_state != icon_image[ICON_RIGHT_FRONT].state[current_fb_no])
{
icon_image[ICON_RIGHT_FRONT].state[current_fb_no] = last_state;
do_refresh = 1;
}
last_state = icon_states[ICON_LEFT_REAR];
if(last_state != icon_image[ICON_LEFT_REAR].state[current_fb_no])
{
icon_image[ICON_LEFT_REAR].state[current_fb_no] = last_state;
do_refresh = 1;
}
last_state = icon_states[ICON_RIGHT_REAR];
if(last_state != icon_image[ICON_RIGHT_REAR].state[current_fb_no])
{
icon_image[ICON_RIGHT_REAR].state[current_fb_no] = last_state;
do_refresh = 1;
}
} while (0);
if(do_refresh == 0)
return;
// 全部重新绘制
// 清除背景
// (539, 149) (199,215)
VGfloat clrcolor[4] = {0.0f, 0.0f, 0.0f, 1.0f}; // 设置clear颜色(黑色)
vgSetfv (VG_CLEAR_COLOR, 4, clrcolor);
vgClear (539, VG_H - (149 + 215), 199, 215); // 清除背景
vgFlush();
// 绘制车模型
draw_icon_foreground (ICON_CAR);
if(icon_image[ICON_LEFT_FRONT].state[current_fb_no])
{
draw_icon_foreground (ICON_LEFT_FRONT);
}
if(icon_image[ICON_RIGHT_FRONT].state[current_fb_no])
{
draw_icon_foreground (ICON_RIGHT_FRONT);
}
if(icon_image[ICON_LEFT_REAR].state[current_fb_no])
{
draw_icon_foreground (ICON_LEFT_REAR);
}
if(icon_image[ICON_RIGHT_REAR].state[current_fb_no])
{
draw_icon_foreground (ICON_RIGHT_REAR);
}
}
// ICON输出时未使用裁减区域加速GPU显示, 用户可根据ICON的状态变化创建相应的裁减区域来优化GPU访问
static void draw_icons (void)
{
VGint coords[4];
coords[0] = 0;
coords[1] = 0;
coords[2] = VG_W;
coords[3] = VG_H;
vgSeti(VG_SCISSORING, VG_TRUE);
vgSetiv(VG_SCISSOR_RECTS, 4, coords);
draw_icon (ICON_LEFT);
draw_icon (ICON_RIGHT);
draw_card_door_status ();
draw_icon (ICON_ABS);
draw_icon (ICON_ENGINE);
draw_icon (ICON_ENGINE_OIL);
draw_icon (ICON_LOWER_BEAM);
draw_icon (ICON_STOP_LAMP);
draw_icon (ICON_WIDTH_LAMP);
draw_icon (ICON_DOUBLE_FLASHING);
draw_icon (ICON_FOGLIGHT);
draw_icon (ICON_HIGH_BEAM);
// 水温坐标948 408 油耗坐标156 408
// 水温标尺
draw_oil_level_icon (ICON_OIL_LEVEL);
// 水温标尺的绘制会污染水温图标
draw_icon (ICON_OIL);
draw_oil_level_icon (ICON_WATER_LEVEL);
// 油耗标尺的绘制会污染油耗图标
draw_icon (ICON_WATER_TEMP);
}
#define ICON_TEST
static int refresh_count = 2;
static unsigned int start_ticket;
static void icon_state_simulate (void)
{
// 模拟ICON状态变化
if(refresh_count > 0)
{
// 用于保证2帧FB初始为相同的ICON状态
refresh_count --;
start_ticket = XM_GetTickCount();
return;
}
#ifdef ICON_TEST
// 模拟ICON测试
// 修改转速
unsigned int time = XM_GetTickCount () - start_ticket;
//icon_values[ICON_TYPE_ROTATE_SPEED] = (time / 400) % 13;
icon_states[ICON_LEFT] = (time / 2000) % 2;
icon_states[ICON_RIGHT] = (time / 3000) % 2;
icon_states[ICON_LEFT_FRONT] = 1 - ((time / 2500) % 2);
icon_states[ICON_LEFT_REAR] = 1 - ((time / 3000) % 2);
icon_states[ICON_RIGHT_FRONT] = 1 - ((time / 4000) % 2);
icon_states[ICON_RIGHT_REAR] = 1 - ((time / 4200) % 2);
icon_states[ICON_OIL_LEVEL] = (time / 2000) % 4;
icon_states[ICON_WATER_LEVEL] = (time / 3000) % 4;
icon_states[ICON_WIDTH_LAMP] = 1 - ((time / 7600) % 2);
icon_states[ICON_FOGLIGHT] = 1 - ((time / 4600) % 2);
icon_states[ICON_HIGH_BEAM] = 1 - ((time / 5800) % 2);
icon_states[ICON_DOUBLE_FLASHING] = 1 - ((time / 5400) % 2);
icon_states[ICON_HIGH_BEAM] = 1 - ((time / 5700) % 2);
icon_states[ICON_PERCENT] = (time / 1500) % 100;
icon_states[ICON_ODO] = (time / 1000) ;
icon_states[ICON_TEMP] = (time / 1300) % 150;
#endif
}
#endif