``` void OLED_DrawBitmap(uint8_t x, uint8_t y, const uint8_t *bitmap, uint8_t width, uint8_t height) { for (uint8_t i = 0; i < height/8; i++) { // 按页写入 OLED_WriteCommand(0xB0 + y + i); // 设置页地址 OLED_WriteCommand(x & 0x0F); // 列低4位 OLED_WriteCommand(0x10 | (x >> 4)); // 列高4位 for (uint8_t j = 0; j < width; j++) { SendData(bitmap[i * width + j]); } } }```SendData是什么意思

时间: 2025-03-15 17:09:55 浏览: 39
<think>嗯,我现在需要理解这个OLED_DrawBitmap函数里的SendData是什么意思。首先,函数的作用是绘制位图到OLED屏幕上,对吧?参数包括坐标x和y,位图数据指针bitmap,宽度和高度。 看代码结构,函数有一个外层循环,遍历height/8次。这可能是因为OLED屏幕的显存是按页(Page)组织的,每页8行。所以每次循环处理一页的数据。然后设置页地址,列地址的低4位和高4位,这些是控制显示位置的命令。 在内层循环里,遍历width次,每次调用SendData(bitmap[i * width + j])。这里的bitmap是一个指向位图数据的指针,每个元素应该对应一个字节,表示8个像素的垂直排列。i是页的索引,乘以宽度得到当前页的起始位置,加上j得到具体列的位置,取出该字节的数据。 那SendData的作用应该是将这个字节的数据发送到OLED的显存中。因为OLED通常通过某种接口(如I2C或SPI)接收数据,SendData可能负责将数据字节通过硬件接口传输到屏幕的对应位置。发送的数据是位图像素信息,每个bit代表一个像素点的亮灭状态。比如,bit0对应页中的第一行,bit1第二行,依此类推。这样,每次发送一个字节,就能更新屏幕上某一列的8个像素点。 不过,SendData的具体实现可能依赖于硬件和驱动方式。例如,如果是I2C通信,SendData可能需要将数据打包成正确的格式,并发送到正确的寄存器地址。如果是SPI,可能需要控制片选、数据/命令选择线,然后发送数据字节。总的来说,SendData的功能是将像素数据写入到OLED当前设置的地址处,从而在屏幕上显示出来。</think>这个`SendData`函数在OLED驱动中具有关键作用,其含义和实现原理如下: $$流程示意图$$ $$ \begin{array}{ccc} \text{位图数据} & \xrightarrow{SendData} & \text{OLED显存} \\ \text{(bitmap[])} & & \text{(GRAM)} \end{array} $$ 一、功能解析: 1. 数据搬运工:负责将显存数据字节通过硬件接口(SPI/I2C/8080并口)写入OLED控制器 2. 位到点的转换:每个字节对应$8\times1$像素矩阵(垂直排列),bit0对应最上方像素 二、技术细节: 1. 硬件接口示例(假设使用I2C): ```c void SendData(uint8_t data) { I2C_Start(); I2C_Write(0x78); // OLED地址 I2C_Write(0x40); // 数据模式标识 I2C_Write(data); // 实际像素数据 I2C_Stop(); } ``` 2. 显存布局示意图(假设128x64分辨率): ``` 页0:Y=0-7行 页1:Y=8-15行 ... 页7:Y=56-63行 ``` 三、工作流程: 1. 外层循环:按页处理(每页8行) - `height/8`计算总页数(如64高度对应8页) - `0xB0 + y + i`设置起始页地址 2. 内层循环:横向填充像素 - `i*width + j`计算当前字节在数组中的偏移 - 每个字节数据通过SendData写入当前列地址 四、位图格式示例: ```c const uint8_t bitmap[] = { 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF // 8x8方框 }; ``` 对应像素布局(每个字节的bit0在最上方): ``` 0xFF ████████ 0x81 █░░░░░█ 0x81 █░░░░░█ ... 0xFF ████████ ``` 五、调试建议: 1. 使用逻辑分析仪捕获实际发送的数据序列 2. 验证硬件接口时序(建立/保持时间) 3. 检查字节序(部分控制器要求LSB优先) 该函数实现需配合OLED控制器的GRAM更新机制,通常需要满足特定时序要求才能正确显示图像。
阅读全文

相关推荐

#include <stdio.h> #include <stdlib.h> #include <harfbuzz/hb.h> #include <harfbuzz/hb-ft.h> #include <ft2build.h> #include FT_FREETYPE_H #include FT_GLYPH_H #include <stdint.h> #pragma pack(push, 1) typedef struct { uint16_t bfType; uint32_t bfSize; uint16_t bfReserved1; uint16_t bfReserved2; uint32_t bfOffBits; } BITMAPFILEHEADER; typedef struct { uint32_t biSize; int32_t biWidth; int32_t biHeight; uint16_t biPlanes; uint16_t biBitCount; uint32_t biCompression; uint32_t biSizeImage; int32_t biXPelsPerMeter; int32_t biYPelsPerMeter; uint32_t biClrUsed; uint32_t biClrImportant; } BITMAPINFOHEADER; #pragma pack(pop) void save_bmp(const char* filename, uint8_t* data, int width, int height) { BITMAPFILEHEADER bfh = { 0x4D42, 0, 0, 0, sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) }; BITMAPINFOHEADER bih = { sizeof(BITMAPINFOHEADER), width, height, 1, 24, 0, 0, 0, 0, 0, 0 }; bfh.bfSize += width * height * 3; bih.biSizeImage = width * height * 3; FILE* f = fopen(filename, "wb"); fwrite(&bfh, 1, sizeof(bfh), f); fwrite(&bih, 1, sizeof(bih), f); // BMP 要求每行字节数为4的倍数 int padding = (4 - (width * 3) % 4) % 4; uint8_t pad[3] = { 0 }; for (int y = height - 1; y >= 0; y--) { for (int x = 0; x < width; x++) { uint8_t* pixel = data + (y * width + x) * 3; fwrite(pixel, 1, 3, f); } fwrite(pad, 1, padding, f); } fclose(f); } int main() { const char* font_path = "NotoSansDevanagari-Regular.otf"; // 印度语字体 const char* text = "नमस्ते"; // 印度语文本 int font_size = 48; const char* output_file = "hindi_output.bmp"; // 初始化 FreeType FT_Library ft_lib; FT_Init_FreeType(&ft_lib); FT_Face ft_face; FT_New_Face(ft_lib, font_path, 0, &ft_face); FT_Set_Char_Size(ft_face, 0, font_size * 64, 96, 96); // 64倍单位 // 初始化 HarfBuzz hb_font_t* hb_font = hb_ft_font_create(ft_face, NULL); hb_buffer_t* hb_buf = hb_buffer_create(); // 设置文本属性(印度语) hb_buffer_add_utf8(hb_buf, text, -1, 0, -1); hb_buffer_set_direction(hb_buf, HB_DIRECTION_LTR); hb_buffer_set_script(hb_buf, HB_SCRIPT_DEVANAGARI); hb_buffer_set_language(hb_buf, hb_language_from_string("hi", -1)); // 文本整形 hb_shape(hb_font, hb_buf, NULL, 0); // 获取整形结果 unsigned int glyph_count; hb_glyph_info_t* glyph_info = hb_buffer_get_glyph_infos(hb_buf, &glyph_count); hb_glyph_position_t* glyph_pos = hb_buffer_get_glyph_positions(hb_buf, &glyph_count); // 计算位图尺寸 int width = 0, height = 0, baseline = 0; for (unsigned int i = 0; i < glyph_count; i++) { width += glyph_pos[i].x_advance / 64; // 64倍单位转像素 } height = ft_face->size->metrics.height / 64; baseline = ft_face->size->metrics.ascender / 64; // 创建位图缓冲区 (RGB) uint8_t* bitmap = calloc(width * height * 3, 1); // 渲染每个字形 int x = 0; for (unsigned int i = 0; i < glyph_count; i++) { FT_Load_Glyph(ft_face, glyph_info[i].codepoint, FT_LOAD_DEFAULT); FT_Render_Glyph(ft_face->glyph, FT_RENDER_MODE_NORMAL); FT_Bitmap* ft_bitmap = &ft_face->glyph->bitmap; int x_offset = glyph_pos[i].x_offset / 64; int y_offset = baseline - ft_face->glyph->bitmap_top; // 将字形复制到位图 for (int row = 0; row < ft_bitmap->rows; row++) { for (int col = 0; col < ft_bitmap->width; col++) { int y_pos = row + y_offset; if (y_pos < 0 || y_pos >= height) continue; int src_idx = row * ft_bitmap->pitch + col; int dst_idx = ((y_pos * width) + (x + col + x_offset)) * 3; if (dst_idx >= 0 && dst_idx < width * height * 3 - 3) { uint8_t gray = ft_bitmap->buffer[src_idx]; bitmap[dst_idx] = gray; // B bitmap[dst_idx + 1] = gray; // G bitmap[dst_idx + 2] = gray; // R } } } x += glyph_pos[i].x_advance / 64; } // 保存为BMP save_bmp(output_file, bitmap, width, height); // 清理资源 free(bitmap); hb_buffer_destroy(hb_buf); hb_font_destroy(hb_font); FT_Done_Face(ft_face); FT_Done_FreeType(ft_lib); printf("BMP 文件已生成: %s\n", output_file); return 0; }

#define UNICODE #define _UNICODE #include <windows.h> #include <math.h> #include <wchar.h> #include “aip_common.h” // Constants for window dimensions, gravity, and animation #define U4_SIM_WINDOW_WIDTH 600U #define U4_SIM_WINDOW_HEIGHT 500U #define U1_SIM_BALL_RADIUS 10U #define S8_SIM_GRAVITY 0.0008 #define S8_SIM_REBOUND_FACTOR 0.5 #define U4_SIM_GROUND_Y (U4_SIM_WINDOW_HEIGHT - 150U) // UI control IDs and Timer ID #define U2_CTRL_HEIGHT_INPUT 201U #define U2_CTRL_REBOUNDS_INPUT 202U #define U2_CTRL_SUBMIT_BUTTON 203U #define U4_SIM_TIMER_ID 101U // Ball structure typedef struct { S8 S8_X_POSITION; S8 S8_Y_POSITION; S8 S8_VERTICAL_VELOCITY; S8 S8_CURRENT_HEIGHT; U2 U2_REBOUNDS_COUNT; U2 U2_MAX_REBOUNDS; U1 U1_SIM_ACTIVE; U1 U1_IS_FALLING; S8 S8_INITIAL_WAIT_TIME_MS; S8 S8_TOTAL_DISTANCE; } ST_SIM_BALL; // Global variables ST_SIM_BALL ST_SIM_BALL_INSTANCE = {0}; HWND HWND_CTRL_HEIGHT_INPUT; HWND HWND_CTRL_REBOUNDS_INPUT; HWND HWND_CTRL_SUBMIT_BUTTON; HWND HWND_SIM_MAIN_WINDOW = NULL; HWND HWND_RESULT_WINDOW = NULL; U4 U4_LAST_SIM_TICK_TIME = 0; S8 S8_SIM_REBOUND_HEIGHTS[100] = {0}; S8 S8_SIM_CUMULATIVE_DISTANCES[100] = {0}; // Function prototypes void FP_SIM_INITIALIZE_BALL(S8 S8_INITIAL_HEIGHT_METERS, U2 U2_MAX_REBOUNDS); void FP_SIM_UPDATE_BALL(U4 U4_DELTA_TIME_MS); void FP_SIM_RENDER_SCENE(HDC hdc); LRESULT CALLBACK FP_RESULTS_WINDOW_PROC(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); void FP_DISPLAY_SIMULATION_RESULTS(HINSTANCE HINSTANCE_CURRENT); LRESULT CALLBACK FP_MAIN_WINDOW_PROC(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); /* ------------------------------------------------------------------ Initialize ball properties ------------------------------------------------------------------ */ void FP_SIM_INITIALIZE_BALL(S8 S8_INITIAL_HEIGHT_METERS, U2 U2_MAX_REBOUNDS) { if (S8_INITIAL_HEIGHT_METERS <= 0 || U2_MAX_REBOUNDS <= 0) return; S8 S8_INITIAL_HEIGHT_PIXELS = S8_INITIAL_HEIGHT_METERS * 2.0; ST_SIM_BALL_INSTANCE.S8_X_POSITION = U4_SIM_WINDOW_WIDTH / 2.0; ST_SIM_BALL_INSTANCE.S8_Y_POSITION = U4_SIM_GROUND_Y - S8_INITIAL_HEIGHT_PIXELS; ST_SIM_BALL_INSTANCE.S8_VERTICAL_VELOCITY = 0; ST_SIM_BALL_INSTANCE.S8_CURRENT_HEIGHT = S8_INITIAL_HEIGHT_PIXELS; ST_SIM_BALL_INSTANCE.U2_REBOUNDS_COUNT = 0; ST_SIM_BALL_INSTANCE.U2_MAX_REBOUNDS = U2_MAX_REBOUNDS; ST_SIM_BALL_INSTANCE.U1_SIM_ACTIVE = TRUE; ST_SIM_BALL_INSTANCE.U1_IS_FALLING = FALSE; ST_SIM_BALL_INSTANCE.S8_INITIAL_WAIT_TIME_MS = 2000.0; ST_SIM_BALL_INSTANCE.S8_TOTAL_DISTANCE = 0.0; for (U2 U2_I = 0; U2_I < 100; U2_I++) { S8_SIM_REBOUND_HEIGHTS[U2_I] = 0; S8_SIM_CUMULATIVE_DISTANCES[U2_I] = 0; } } /* ------------------------------------------------------------------ Update ball physics ------------------------------------------------------------------ */ void FP_SIM_UPDATE_BALL(U4 U4_DELTA_TIME_MS) { if (!ST_SIM_BALL_INSTANCE.U1_SIM_ACTIVE) return; if (ST_SIM_BALL_INSTANCE.S8_INITIAL_WAIT_TIME_MS > 0) { ST_SIM_BALL_INSTANCE.S8_INITIAL_WAIT_TIME_MS -= U4_DELTA_TIME_MS; if (ST_SIM_BALL_INSTANCE.S8_INITIAL_WAIT_TIME_MS <= 0) { ST_SIM_BALL_INSTANCE.S8_INITIAL_WAIT_TIME_MS = 0; ST_SIM_BALL_INSTANCE.U1_IS_FALLING = TRUE; } return; } if (ST_SIM_BALL_INSTANCE.U1_IS_FALLING) { ST_SIM_BALL_INSTANCE.S8_VERTICAL_VELOCITY += S8_SIM_GRAVITY * U4_DELTA_TIME_MS; ST_SIM_BALL_INSTANCE.S8_Y_POSITION += ST_SIM_BALL_INSTANCE.S8_VERTICAL_VELOCITY * U4_DELTA_TIME_MS; if (ST_SIM_BALL_INSTANCE.S8_Y_POSITION >= U4_SIM_GROUND_Y - U1_SIM_BALL_RADIUS) { ST_SIM_BALL_INSTANCE.S8_Y_POSITION = U4_SIM_GROUND_Y - U1_SIM_BALL_RADIUS; ST_SIM_BALL_INSTANCE.U1_IS_FALLING = FALSE; S8_SIM_CUMULATIVE_DISTANCES[ST_SIM_BALL_INSTANCE.U2_REBOUNDS_COUNT] = ST_SIM_BALL_INSTANCE.S8_TOTAL_DISTANCE + ST_SIM_BALL_INSTANCE.S8_CURRENT_HEIGHT; S8_SIM_REBOUND_HEIGHTS[ST_SIM_BALL_INSTANCE.U2_REBOUNDS_COUNT] = ST_SIM_BALL_INSTANCE.S8_CURRENT_HEIGHT * S8_SIM_REBOUND_FACTOR; ST_SIM_BALL_INSTANCE.S8_TOTAL_DISTANCE += ST_SIM_BALL_INSTANCE.S8_CURRENT_HEIGHT; ST_SIM_BALL_INSTANCE.U2_REBOUNDS_COUNT++; if (ST_SIM_BALL_INSTANCE.U2_REBOUNDS_COUNT < ST_SIM_BALL_INSTANCE.U2_MAX_REBOUNDS) { ST_SIM_BALL_INSTANCE.S8_CURRENT_HEIGHT *= S8_SIM_REBOUND_FACTOR; ST_SIM_BALL_INSTANCE.S8_VERTICAL_VELOCITY = -sqrt(2.0 * S8_SIM_GRAVITY * ST_SIM_BALL_INSTANCE.S8_CURRENT_HEIGHT); } else { ST_SIM_BALL_INSTANCE.U1_SIM_ACTIVE = FALSE; } } } else { ST_SIM_BALL_INSTANCE.S8_Y_POSITION += ST_SIM_BALL_INSTANCE.S8_VERTICAL_VELOCITY * U4_DELTA_TIME_MS; ST_SIM_BALL_INSTANCE.S8_VERTICAL_VELOCITY += S8_SIM_GRAVITY * U4_DELTA_TIME_MS; if (ST_SIM_BALL_INSTANCE.S8_VERTICAL_VELOCITY >= 0) { ST_SIM_BALL_INSTANCE.U1_IS_FALLING = TRUE; } } } /* ------------------------------------------------------------------ Render simulation scene ------------------------------------------------------------------ */ void FP_SIM_RENDER_SCENE(HDC hdc) { // Draw ground HPEN HPEN_GROUND = CreatePen(PS_SOLID, 2, RGB(0, 128, 0)); HPEN HPEN_OLD = (HPEN)SelectObject(hdc, HPEN_GROUND); MoveToEx(hdc, 0, U4_SIM_GROUND_Y, NULL); LineTo(hdc, U4_SIM_WINDOW_WIDTH, U4_SIM_GROUND_Y); SelectObject(hdc, HPEN_OLD); DeleteObject(HPEN_GROUND); // Draw ball HBRUSH HBRUSH_BALL = CreateSolidBrush(RGB(139, 69, 19)); HBRUSH HBRUSH_OLD = (HBRUSH)SelectObject(hdc, HBRUSH_BALL); Ellipse(hdc, (int)(ST_SIM_BALL_INSTANCE.S8_X_POSITION - U1_SIM_BALL_RADIUS), (int)(ST_SIM_BALL_INSTANCE.S8_Y_POSITION - U1_SIM_BALL_RADIUS), (int)(ST_SIM_BALL_INSTANCE.S8_X_POSITION + U1_SIM_BALL_RADIUS), (int)(ST_SIM_BALL_INSTANCE.S8_Y_POSITION + U1_SIM_BALL_RADIUS)); SelectObject(hdc, HBRUSH_OLD); DeleteObject(HBRUSH_BALL); } /* ------------------------------------------------------------------ Result window procedure ------------------------------------------------------------------ */ LRESULT CALLBACK FP_RESULTS_WINDOW_PROC(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); wchar_t WSTRING_BUFFER[256]; U2 U2_OFFSET = 10; HFONT HFONT_RESULT = CreateFontW(18, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, L"Arial"); HFONT HFONT_OLD = (HFONT)SelectObject(hdc, HFONT_RESULT); swprintf(WSTRING_BUFFER, 256, L"Total Travel Distance: %.2f meters", ST_SIM_BALL_INSTANCE.S8_TOTAL_DISTANCE / 2.0); TextOutW(hdc, 10, U2_OFFSET, WSTRING_BUFFER, wcslen(WSTRING_BUFFER)); U2_OFFSET += 20; for (U2 U2_I = 0; U2_I < ST_SIM_BALL_INSTANCE.U2_REBOUNDS_COUNT; U2_I++) { swprintf(WSTRING_BUFFER, 256, L"Rebound %d Height: %.2f meters", U2_I + 1, S8_SIM_REBOUND_HEIGHTS[U2_I] / 2.0); TextOutW(hdc, 10, U2_OFFSET, WSTRING_BUFFER, wcslen(WSTRING_BUFFER)); U2_OFFSET += 20; swprintf(WSTRING_BUFFER, 256, L"Cumulative Distance: %.2f meters", S8_SIM_CUMULATIVE_DISTANCES[U2_I] / 2.0); TextOutW(hdc, 10, U2_OFFSET, WSTRING_BUFFER, wcslen(WSTRING_BUFFER)); U2_OFFSET += 20; } SelectObject(hdc, HFONT_OLD); DeleteObject(HFONT_RESULT); EndPaint(hwnd, &ps); return 0; } case WM_DESTROY: HWND_RESULT_WINDOW = NULL; return 0; default: return DefWindowProc(hwnd, msg, wParam, lParam); } return 0; } /* ------------------------------------------------------------------ Display simulation results ------------------------------------------------------------------ */ void FP_DISPLAY_SIMULATION_RESULTS(HINSTANCE HINSTANCE_CURRENT) { if (HWND_RESULT_WINDOW != NULL) { InvalidateRect(HWND_RESULT_WINDOW, NULL, TRUE); return; } const wchar_t CLASS_NAME[] = L"ResultWindow"; WNDCLASS WNDCLASS_RESULTS = {0}; WNDCLASS_RESULTS.lpfnWndProc = FP_RESULTS_WINDOW_PROC; WNDCLASS_RESULTS.hInstance = HINSTANCE_CURRENT; WNDCLASS_RESULTS.lpszClassName = CLASS_NAME; WNDCLASS_RESULTS.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); RegisterClass(&WNDCLASS_RESULTS); HWND_RESULT_WINDOW = CreateWindow(CLASS_NAME, L"Simulation Results", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, NULL, NULL, HINSTANCE_CURRENT, NULL); ShowWindow(HWND_RESULT_WINDOW, SW_SHOW); UpdateWindow(HWND_RESULT_WINDOW); } /* ------------------------------------------------------------------ Main window procedure ------------------------------------------------------------------ */ LRESULT CALLBACK FP_MAIN_WINDOW_PROC(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_CREATE: { // Create input controls CreateWindow(L"STATIC", L"Initial Height (meters):", WS_VISIBLE | WS_CHILD, 20, U4_SIM_WINDOW_HEIGHT - 140, 170, 30, hwnd, NULL, NULL, NULL); HWND_CTRL_HEIGHT_INPUT = CreateWindow(L"EDIT", L"100", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_NUMBER, 190, U4_SIM_WINDOW_HEIGHT - 140, 100, 30, hwnd, (HMENU)U2_CTRL_HEIGHT_INPUT, NULL, NULL); CreateWindow(L"STATIC", L"Max Rebounds:", WS_VISIBLE | WS_CHILD, 20, U4_SIM_WINDOW_HEIGHT - 100, 170, 30, hwnd, NULL, NULL, NULL); HWND_CTRL_REBOUNDS_INPUT = CreateWindow(L"EDIT", L"10", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_NUMBER, 190, U4_SIM_WINDOW_HEIGHT - 100, 100, 30, hwnd, (HMENU)U2_CTRL_REBOUNDS_INPUT, NULL, NULL); HWND_CTRL_SUBMIT_BUTTON = CreateWindow(L"BUTTON", L"Submit", WS_VISIBLE | WS_CHILD, 310, U4_SIM_WINDOW_HEIGHT - 120, 100, 40, hwnd, (HMENU)U2_CTRL_SUBMIT_BUTTON, NULL, NULL); // Initialize default ball position ST_SIM_BALL_INSTANCE.S8_X_POSITION = U4_SIM_WINDOW_WIDTH / 2.0; ST_SIM_BALL_INSTANCE.S8_Y_POSITION = U4_SIM_GROUND_Y - 50; SetTimer(hwnd, U4_SIM_TIMER_ID, 10, NULL); return 0; } case WM_COMMAND: { if (LOWORD(wParam) == U2_CTRL_SUBMIT_BUTTON) { wchar_t HEIGHT_BUFFER[32], REBOUNDS_BUFFER[32]; // Get input values GetWindowText(HWND_CTRL_HEIGHT_INPUT, HEIGHT_BUFFER, 32); double HEIGHT_VALUE = _wtof(HEIGHT_BUFFER); GetWindowText(HWND_CTRL_REBOUNDS_INPUT, REBOUNDS_BUFFER, 32); int REBOUNDS_VALUE = _wtoi(REBOUNDS_BUFFER); // Validate inputs if (HEIGHT_VALUE <= 0 || HEIGHT_VALUE > 150) { MessageBox(hwnd, L"Height out of range! (0-150 meters)", L"Invalid Input", MB_ICONERROR); return 0; } if (REBOUNDS_VALUE <= 0 || REBOUNDS_VALUE > 100) { MessageBox(hwnd, L"Rebound count must be between 1-100", L"Invalid Input", MB_ICONERROR); return 0; } // Stop current simulation KillTimer(hwnd, U4_SIM_TIMER_ID); // Initialize new simulation FP_SIM_INITIALIZE_BALL(HEIGHT_VALUE, REBOUNDS_VALUE); // Record start time U4_LAST_SIM_TICK_TIME = GetTickCount(); // Restart timer SetTimer(hwnd, U4_SIM_TIMER_ID, 10, NULL); // Force redraw InvalidateRect(hwnd, NULL, TRUE); } return 0; } case WM_TIMER: { if (wParam == U4_SIM_TIMER_ID) { // Calculate time delta U4 CURRENT_TIME = GetTickCount(); U4 DELTA_TIME = (U4_LAST_SIM_TICK_TIME == 0) ? 0 : (CURRENT_TIME - U4_LAST_SIM_TICK_TIME); U4_LAST_SIM_TICK_TIME = CURRENT_TIME; if (ST_SIM_BALL_INSTANCE.U1_SIM_ACTIVE) { // Update ball physics FP_SIM_UPDATE_BALL(DELTA_TIME); // Redraw only the necessary area RECT UPDATE_RECT = { 0, 0, U4_SIM_WINDOW_WIDTH, U4_SIM_GROUND_Y + U1_SIM_BALL_RADIUS }; InvalidateRect(hwnd, &UPDATE_RECT, TRUE); } else { // Simulation complete - show results KillTimer(hwnd, U4_SIM_TIMER_ID); FP_DISPLAY_SIMULATION_RESULTS( (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE) ); } } return 0; } case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); // Clear background RECT rect; GetClientRect(hwnd, &rect); FillRect(hdc, &rect, (HBRUSH)(COLOR_WINDOW+1)); // Draw simulation elements FP_SIM_RENDER_SCENE(hdc); // Display initial wait state if (ST_SIM_BALL_INSTANCE.S8_INITIAL_WAIT_TIME_MS > 0) { wchar_t STATUS[100]; swprintf(STATUS, 100, L"Initial state: %.1f seconds remaining", ST_SIM_BALL_INSTANCE.S8_INITIAL_WAIT_TIME_MS / 1000.0); HFONT HFONT_STATUS = CreateFont(18, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, L"Arial"); HFONT HFONT_OLD = (HFONT)SelectObject(hdc, HFONT_STATUS); TextOut(hdc, 20, 20, STATUS, wcslen(STATUS)); SelectObject(hdc, HFONT_OLD); DeleteObject(HFONT_STATUS); } EndPaint(hwnd, &ps); return 0; } case WM_DESTROY: { KillTimer(hwnd, U4_SIM_TIMER_ID); PostQuitMessage(0); return 0; } default: return DefWindowProc(hwnd, msg, wParam, lParam); } return 0; } /* ------------------------------------------------------------------ Application entry point ------------------------------------------------------------------ */ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { const wchar_t CLASS_NAME[] = L"BouncingBallSimulation"; WNDCLASS WNDCLASS_MAIN = {0}; WNDCLASS_MAIN.lpfnWndProc = FP_MAIN_WINDOW_PROC; WNDCLASS_MAIN.hInstance = hInstance; WNDCLASS_MAIN.lpszClassName = CLASS_NAME; WNDCLASS_MAIN.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); if (!RegisterClass(&WNDCLASS_MAIN)) { return 0; } // Calculate window size including non-client area RECT rect = {0, 0, U4_SIM_WINDOW_WIDTH, U4_SIM_WINDOW_HEIGHT}; AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, TRUE); int WIDTH = rect.right - rect.left; int HEIGHT = rect.bottom - rect.top; // Create main window HWND_SIM_MAIN_WINDOW = CreateWindow( CLASS_NAME, L"Bouncing Ball Simulation", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, WIDTH, HEIGHT, NULL, NULL, hInstance, NULL ); if (!HWND_SIM_MAIN_WINDOW) { return 0; } // Main message loop MSG MSG_SIM_LOOP; while (GetMessage(&MSG_SIM_LOOP, NULL, 0, 0)) { TranslateMessage(&MSG_SIM_LOOP); DispatchMessage(&MSG_SIM_LOOP); } return (int)MSG_SIM_LOOP.wParam; } 就在源代码上修改编码规范,刚刚的代码又逻辑不通了’

给我解释一下以下这些代码的作用:/* USER CODE BEGIN Header */ /***************************************************************************** Îļþ: main.c ×÷Õß: Zhengyu https://gzwelink.taobao.com °æ±¾: V1.0.0 ʱ¼ä: 20200401 ƽ̨:MINI-STM32F103C8T6 *******************************************************************************/ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "gpio.h" #include "oled.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config();//8MÍⲿ¾§Õñ£¬72MϵͳÖ÷Ƶ /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init();//OLEDÒº¾§¿ØÖÆÒý½Å³õʼ»¯ /* USER CODE BEGIN 2 */ OLED_Init();//OLED³õʼ»¯ OLED_Clear();//ÇåÆÁ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ OLED_Clear();//ÇåÆÁ OLED_ShowCHinese(18,0,0);//¹â OLED_ShowCHinese(36,0,1);//×Ó OLED_ShowCHinese(54,0,2);//Îï OLED_ShowCHinese(72,0,3);//Áª OLED_ShowCHinese(90,0,4);//Íø OLED_ShowString(6,3,"0.96' OLED TEST");//ÏÔʾ×Ö·û´® OLED_ShowString(0,6,"ASCII:"); OLED_ShowString(63,6,"CODE:"); HAL_Delay(5000);//µÈ´ý5000ms } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

/***************************************************************************** *函数名称:void SearchNeighbor_4(unsigned char *bitmap, unsigned char *labelmap,uint16 labelIndex,uint16 pixelIndex, Queue *queue) *功能说明:四邻域生长法 *参数说明:bitmap:代标记图像 labelmap:标记矩阵 *函数返回:无 *修改时间:2022年4月3日 *备 注: ******************************************************************************/ int area;//记录连通域的可连通点个数 int Connected_x[100]; int Connected_y[100]; int32_t Connected_light[100];//存每个区域的平均亮度 //四邻域生长 static int NeighborDirection[4][2] = {{0,1},{1,0},{0,-1},{-1,0}}; void SearchNeighbor_4(unsigned char *bitmap, unsigned char *labelmap,uint16_t labelIndex,uint16_t pixelIndex, Queue *queue,int x,int y,unsigned char *lightmap) { int searchIndex,i,length; int qx,qy; labelmap[pixelIndex]=labelIndex;//起始位置的标记数 length=LCDW*LCDH;//将二维数组转化为一维数组的总数据个数 for(i=0;i<4;i++) { searchIndex=pixelIndex+NeighborDirection[i][0]*LCDW+NeighborDirection[i][1];//二维数组转化为一维数组 if(searchIndex >= 0 && searchIndex <length && bitmap[searchIndex] == 1 && labelmap[searchIndex] == 0)//检测有无超图像范围,将8领域中未遍历的白点入队 { labelmap[searchIndex]=labelIndex;//将区域标记数赋给模板中的这个像素 area++; qy=y; qx=(int)((searchIndex-y)/(LCDW)); Connected_x[labelIndex]+=qx; Connected_y[labelIndex]+=qy; Connected_light[labelIndex]+=lightmap[searchIndex]; //防止数据溢出导致死机 if(area>=65535) area=65535; PushQueue(queue,searchIndex);//将四邻域中未被遍历过的白点入队 记下坐标为数据域 } } } /***************************************************************************** *函数名称:void ConnectedComponentLabeling(unsigned char *bitmap, unsigned char * labelmap) *功能说明:连通域计算,无信标灯判断 *参数说明:bitmap:代标记图像 labelmap:标记矩阵 *函数返回:返回总的区域计数 *修改时间:2022年4月3日 *备 注:无 *使用说明:只需调用输出数组即可,i从2开始 area_map[i]为每个独立区域的面积 Connected_x[i],Connected_y[i],为每个独立区域的坐标 Connected_light[i],为每个区域的亮度平均值 ******************************************************************************/ //设计的连通域最多十一个区域 uint16_t areamax=0;//最大范围连通域的可连通点个数 uint8_t labelmax=0,labeldel[100]={0},labelcount=0;//最大范围连通域标号 可连通点小于3的连通区域标号 总的连通域个数 //bitmap:代标记图像 labelmap:标记矩阵 void ConnectedComponentLabeling(unsigned char *bitmap, unsigned char * labelmap) { //1.创建队列(排队用的列表),用来临时存要检查的点 uint8_t i,j,labelIndex=1,del=0,x=1; int index,popIndex; Queue *queue = NULL; queue = (Queue*)malloc(sizeof(Queue)); queue->first = NULL; queue->last = NULL; // 2. 清空之前的记录(避免上次的数据干扰这次) memset(labelmap,0,LCDW*LCDH);//清空标记矩阵,每次清0很重要 memset(labeldel,0,100);//清空通点小于3的连通区域标号// 清空要删除的小区域记录 memset(area_map,0,100);// 清空每个区域的面积记录 memset(Connected_x,0,100); // 清空每个区域的中心X坐标 memset(Connected_y,0,100);// 清空每个区域的中心Y坐标 memset(Connected_light,0,100);// 清空每个区域的平均亮度 // 3. 初始化统计变量 areamax=0;// 最大区域面积归零 labelcount=0;// 总区域数归零 labelmax=1;// 最大区域的编号初始为1 // 【开始扫描图片】 //从左到右、从上到下一行行扫描每个像素点 int flag_1; int last_labelIndex=0; for(i = 0; i <=LCDH ; i++) //1 LCDH-1 { for(j = 0; j <=LCDW ; j++) // 1 LCDW-1 { index=i*(LCDW)+j;//索引的点 二维数组用一维数组存储 索引的方式 // 计算当前点在一维数组中的位置 // 【发现新区域】 if(bitmap[index]==1 && labelmap[index]==0)//白点且未被入队 { area = 0; // 清零,准备统计当前区域 labelIndex++;//区域计数// 给新区域分配编号 SearchNeighbor_4(bitmap,labelmap,labelIndex,index,queue,i,j,(unsigned char *)Image_Use);//将其八邻域点入队 并在其模板上记下区域标记 // 【处理队列中的点】 popIndex=PopQueue(queue);//确定第一个区域生长种子 while(popIndex>-1)// pop的同时 不断搜索其八邻域是否有新的白点并再入队一直重复 一直pop直到队列中没有结点此时这个区域为独立的// 只要队列不空就一直处理 { SearchNeighbor_4(bitmap,labelmap,labelIndex,popIndex,queue,i,j,(unsigned char *)Image_Use);//不再有白点与其连续一个领域标记完成 popIndex = PopQueue(queue);//弹出区域生长种子作为下一次的搜索起始位置,知道不再有区域生长种子,整块区域遍历完成 } } flag_1=labelIndex - last_labelIndex; last_labelIndex = labelIndex; if(flag_1==1) { area_map[labelIndex]=area; // 记录面积(比如这个区域有x个点) Connected_x[labelIndex]=(int)(Connected_x[labelIndex]/area); //连通域重心x// 计算中心坐标x Connected_y[labelIndex]=(int)(Connected_y[labelIndex]/area); //连通域重心y// 计算中心坐标y Connected_light[labelIndex]=(int)(Connected_light[labelIndex]/area);// 计算平均亮度 flag_1 = 0; } // 更新最大区域 if(x!=labelIndex)//不止一个连通区域 { x=labelIndex;//X代表了有几个连通区域 if(area>areamax)//确定最大范围的连通域 { areamax=area;// 更新最大面积 labelmax=labelIndex;// 记录最大区域的编号 } // 如果是杂点(比如面积只有1个点),记录下来稍后删除 //if(area<=1)// 噪音点判断,可有可无 // { //labeldel[del]=labelIndex; //del++; // if(del>=9) del=9; //} area=0;// 清零,准备统计下一个区域 } } } free(queue);//一幅图遍历完释放队列空间 //返回总的区域计数 labelcount=labelIndex-1; } /******************************************** 设置权重判断 面积--高度 亮度 形状 量级 0~1500 0~120 0~255 0~ 面积->小 高度->高 可信度->高 面积->大 高度->低 可信度->高 亮度->高->可信度高 形状->高->可信度高 *********************************************/ int quanzhi_weight[100]; // 存储每个区域的“可能性分数” void quanzhong_light_panduan(void) { float zz_return;// 形状检测的得分(比如是不是方形) memset(quanzhi_weight,0,sizeof(quanzhi_weight)); // 清空之前的分数 for(int i=2;i<=labelcount+2;i++) { zz_return=Square_detection(Connected_x[i],Connected_y[i]); //zz_return = Circle_detection(Connected_x[i], Connected_y[i]); // 1. 【形状检测】判断区域形状是否太瘦长(比如细长的杂斑) if(zz_return < 0.8)// 圆形度阈值可调(0.7~0.8) { quanzhi_weight[i]=0; continue; } // 2. 【位置与面积逻辑过滤】 // - 如果区域在远处(x坐标<30)但面积太大(>30),可能是干扰物 if(Connected_x[i]<20)//远处太大 { quanzhi_weight[i]=0; continue; } //- 如果区域在近处(x坐标>90)但面积太小(<10),可能是噪点 if(Connected_x[i]>85 && area_map[i]<20)//近处太小 { quanzhi_weight[i]=0; continue; } //权重值越大,是信标灯的概率越大 //120和1500为当前使用镜头下的参数,不同镜头需要自行修改 //120为图像上下高度,1500为图像默认情况下可能出现的最大面积 // 3. 【综合打分】(分数越高越可能是目标) // 公式 = 高度得分 + 面积得分 + 亮度加分 + 形状加分 //quanzhi_weight[i]= (int) //((Connected_x[i]*1.0/120)*// 位置越高(/120归一化)得分越高 //(1-area_map[i]*1.0/2000)*1000 // 面积越小(/2000归一化)得分越高 //+Connected_light[i]*2// 亮度越高,加2倍分 //+zz_return*100);// 形状越接近方形,加100倍分 quanzhi_weight[i]= (int)( ((1-Connected_x[i])*1.0/120)*// (/120归一化) ((area_map[i])*1.0/2000)*1000 // (/2000归一化) +Connected_light[i]*3// 亮度越高,加2倍分 +zz_return*200 // 形状越接近方形,加100倍分 // +distance1(94,120)*30 ); } } //寻找最大权重值 int find_max(void) { int maxx=quanzhi_weight[2]; int wei=2; for(int i=3;i<=labelcount+2;i++) { if(quanzhi_weight[i]>maxx) { maxx = quanzhi_weight[i]; wei=i; } } /* if((wei == 2) && (labelcount == 0)) { wei = 1; }*/ return wei; } //计算最大权重目标位置 void quanzhong_light(void) { dotcnt=0; int zxc=find_max(); if(quanzhi_weight[zxc]!=0) { sumlie = Connected_y[zxc];//y sumhang = Connected_x[zxc];//x dotcnt++; ips200_draw_line(sumlie,0,sumlie,119,RGB565_RED); ips200_draw_line(0,sumhang,187,sumhang,RGB565_RED); } }这是通过连通域算法寻找信标灯的算法,但是这个代码只能适用于一个信标灯的情况,就是只能识别一个灯,如果多个信标灯同时亮起,就会出现左右摇摆不定的情况,写一个识别多信标灯的算法一般是优先远处的,一定要考虑在运动过程中权重值的变化,因为运动过程中上一秒的最大权重连通域可能会变小之类的。写出算法

SSD1306初始化失败 无法找到OLED屏幕##include <SoftwareSerial.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define OLED_RESET 4 Adafruit_SSD1306 display(128, 64, &Wire, OLED_RESET); // 16x16 汉字字模 static const unsigned char PROGMEM str1[] = { 0x20,0x00,0x23,0xF0,0x40,0x10,0x40,0x10,0x93,0xF0,0xF0,0x10,0x20,0x10,0x47,0xFC, 0x80,0x40,0xF4,0x44,0x82,0xE8,0x01,0x50,0x32,0x48,0xC4,0x44,0x81,0x40,0x00,0x80 }; // "绿" static const unsigned char PROGMEM str2[] = { 0x20,0x00,0x20,0x00,0x43,0xF8,0x48,0x40,0x88,0x40,0xF0,0x40,0x20,0x40,0x40,0x40, 0x80,0x40,0xF8,0x40,0x80,0x40,0x00,0x40,0x38,0x40,0xC0,0x40,0x87,0xFC,0x00,0x00 }; // "红" static const unsigned char PROGMEM str3[] = { 0x10,0x40,0x10,0x40,0xFF,0xFC,0x10,0x40,0x09,0x00,0x49,0x00,0x49,0xF8,0x49,0x40, 0x4A,0x20,0x00,0x00,0x7F,0xF0,0x48,0x90,0x48,0x90,0x48,0x90,0xFF,0xFC,0x00,0x00 }; // "蓝" static const unsigned char PROGMEM plusSign[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80, 0x7F,0xFE,0x7F,0xFE,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x00,0x00 }; // "+" 符号 // 配置软串口引脚 SoftwareSerial gm865(A0, A1); // RX=A0, TX=A1 void setup() { Serial.begin(115200); // 电脑通信 gm865.begin(115200); // GM865通信 // 初始化OLED - 修复了这里的问题 if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println(F("SSD1306 allocation failed")); for(;;); // 卡死 } // 必须在begin()之后设置这些参数 display.setTextColor(WHITE); // 开像素点发光 display.clearDisplay(); // 清屏 display.display(); // 关键:将清屏操作刷新到屏幕 // 显示欢迎信息 display.setTextSize(1); display.setCursor(35, 5); display.println("-TonyCode-"); display.display(); // 关键:将文本刷新到屏幕 Serial.println("系统已启动,等待扫描二维码..."); delay(300); // 等待模块启动 // 发送初始化指令 gm865.println("ATE0"); // 关闭回显 delay(100); gm865.println("AT+QREPORT=1"); // 启用结果上报 delay(100); gm865.println("AT+QRSCAN=1"); // 开启扫码功能 Serial.println("GM865 Ready. Scan QR code..."); } void loop() { // 监听GM865数据 if (gm865.available()) { String qrData = gm865.readStringUntil('\n'); qrData.trim(); // 移除换行符 if (qrData.length() > 0) { Serial.print("QR Content: "); Serial.println(qrData); // 清屏准备显示新内容 display.clearDisplay(); if (qrData == "1+321") { // 绘制位图 display.drawBitmap(20, 20, str2, 16, 16, WHITE); // 红 display.drawBitmap(40, 20, plusSign, 16, 16, WHITE); // + display.drawBitmap(60, 20, str3, 16, 16, WHITE); // 蓝 display.drawBitmap(80, 20, str1, 16, 16, WHITE); // 绿 display.drawBitmap(100, 20, str2, 16, 16, WHITE); // 红 } if (qrData == "1+231") { // 绘制位图 display.drawBitmap(20, 20, str2, 16, 16, WHITE); // 红 display.drawBitmap(40, 20, plusSign, 16, 16, WHITE); // + display.drawBitmap(60, 20, str1, 16, 16, WHITE); // 绿 display.drawBitmap(80, 20, str3, 16, 16, WHITE); // 蓝 display.drawBitmap(100, 20, str2, 16, 16, WHITE); // 红 } if (qrData == "3+123") { // 绘制位图 display.drawBitmap(20, 20, str3, 16, 16, WHITE); // 蓝 display.drawBitmap(40, 20, plusSign, 16, 16, WHITE); // + display.drawBitmap(60, 20, str2, 16, 16, WHITE); // 红 display.drawBitmap(80, 20, str3, 16, 16, WHITE); // 蓝 display.drawBitmap(100, 20, str1, 16, 16, WHITE); // 绿 } if (qrData == "3+312") { // 绘制位图 display.drawBitmap(20, 20, str3, 16, 16, WHITE); // 蓝 display.drawBitmap(40, 20, plusSign, 16, 16, WHITE); // + display.drawBitmap(60, 20, str3, 16, 16, WHITE); // 蓝 display.drawBitmap(80, 20, str1, 16, 16, WHITE); // 绿 display.drawBitmap(100, 20, str2, 16, 16, WHITE); // 红 } if (qrData == "2+321") { // 绘制位图 display.drawBitmap(20, 20, str1, 16, 16, WHITE); // 绿 display.drawBitmap(40, 20, plusSign, 16, 16, WHITE); // + display.drawBitmap(60, 20, str3, 16, 16, WHITE); // 蓝 display.drawBitmap(80, 20, str1, 16, 16, WHITE); // 绿 display.drawBitmap(100, 20, str2, 16, 16, WHITE); // 蓝 } if (qrData == "2+312") { // 绘制位图 display.drawBitmap(20, 20, str2, 16, 16, WHITE); // 红 display.drawBitmap(40, 20, plusSign, 16, 16, WHITE); // + display.drawBitmap(60, 20, str3, 16, 16, WHITE); // 蓝 display.drawBitmap(80, 20, str2, 16, 16, WHITE); // 红 display.drawBitmap(100, 20, str1, 16, 16, WHITE); // 绿 } if (qrData == "2+123") { // 绘制位图 display.drawBitmap(20, 20, str1, 16, 16, WHITE); // 绿 display.drawBitmap(40, 20, plusSign, 16, 16, WHITE); // + display.drawBitmap(60, 20, str2, 16, 16, WHITE); // 红 display.drawBitmap(80, 20, str1, 16, 16, WHITE); // 绿 display.drawBitmap(100, 20, str3, 16, 16, WHITE); // 蓝 } if (qrData == "3+231") { // 绘制位图 display.drawBitmap(20, 20, str3, 16, 16, WHITE); // 蓝 display.drawBitmap(40, 20, plusSign, 16, 16, WHITE); // + display.drawBitmap(60, 20, str1, 16, 16, WHITE); // 绿 display.drawBitmap(80, 20, str3, 16, 16, WHITE); // 蓝 display.drawBitmap(100, 20, str2, 16, 16, WHITE); // 红 } // 关键:将绘制的内容刷新到屏幕 display.display(); // 保持显示2秒 delay(2000); } } }这是代码

// B2saomiaotuxiangchuangkou.cpp : 实现文件 // #include "stdafx.h" #include "Imageprocessing.h" #include "B2saomiaotuxiangchuangkou.h" #include "afxdialogex.h" #include <string> #include <chrono> #include <Gdiplus.h> #include <shlwapi.h> #pragma comment(lib, "Gdiplus.lib") #pragma comment(lib, "shlwapi.lib") #pragma comment(lib, "Shell32.lib") // B2saomiaotuxiangchuangkou 对话框 IMPLEMENT_DYNAMIC(B2saomiaotuxiangchuangkou, CDialogEx) B2saomiaotuxiangchuangkou::B2saomiaotuxiangchuangkou(CWnd* pParent /*=NULL*/) : CDialogEx(IDD_B2saomiaotuxiangchuangkou, pParent) { } B2saomiaotuxiangchuangkou::~B2saomiaotuxiangchuangkou() { Cleanup(); } void B2saomiaotuxiangchuangkou::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_EDIT_FOLDER_PATH, m_editFolderPath); DDX_Control(pDX, IDC_LIST_IMAGES, m_listImages); DDX_Control(pDX, IDC_COMBO_SAVE_OPTION, m_saveOption); DDX_Control(pDX, IDC_BUTTON_START, m_btnStart); DDX_Control(pDX, IDC_BUTTON_PAUSE, m_btnPause); DDX_Control(pDX, IDC_BUTTON_STOP, m_btnStop); DDX_Control(pDX, IDC_BUTTON_RESET, m_btnReset); DDX_Control(pDX, IDC_BUTTON_SELECT_FOLDER, m_btnSelectFolder); DDX_Control(pDX, IDC_PROGRESS_TOTAL, m_totalProgress); DDX_Control(pDX, IDC_PROGRESS_FILE, m_fileProgress); DDX_Control(pDX, IDC_STATIC_ORIGINAL_IMAGE, m_originalImage); DDX_Control(pDX, IDC_STATIC_PROCESSED_IMAGE, m_processedImage); DDX_Control(pDX, IDC_EDIT2, m_editSavePath); } BEGIN_MESSAGE_MAP(B2saomiaotuxiangchuangkou, CDialogEx) ON_WM_SIZE() ON_BN_CLICKED(IDC_BUTTON_SELECT_FOLDER, &B2saomiaotuxiangchuangkou::OnBnClickedButtonSelectFolder) ON_BN_CLICKED(IDC_BUTTON_START, &B2saomiaotuxiangchuangkou::OnBnClickedButtonStart) ON_BN_CLICKED(IDC_BUTTON_PAUSE, &B2saomiaotuxiangchuangkou::OnBnClickedButtonPause) ON_BN_CLICKED(IDC_BUTTON_STOP, &B2saomiaotuxiangchuangkou::OnBnClickedButtonStop) ON_BN_CLICKED(IDC_BUTTON_RESET, &B2saomiaotuxiangchuangkou::OnBnClickedButtonReset) ON_CBN_SELCHANGE(IDC_COMBO_SAVE_OPTION, &B2saomiaotuxiangchuangkou::OnCbnSelchangeComboSaveOption) ON_WM_TIMER() ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST_IMAGES, &B2saomiaotuxiangchuangkou::OnLvnItemchangedListImages) ON_MESSAGE(WM_LOAD_FIRST_IMAGE, &B2saomiaotuxiangchuangkou::OnLoadFirstImageMessage) ON_MESSAGE(WM_UPDATE_DISPLAY_IMAGE, &B2saomiaotuxiangchuangkou::OnUpdateDisplayImage) ON_WM_MOUSEWHEEL() // 添加鼠标滚轮消息映射 ON_WM_ERASEBKGND() END_MESSAGE_MAP() BOOL B2saomiaotuxiangchuangkou::OnInitDialog() { CDialogEx::OnInitDialog(); m_listImages.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES); m_listImages.InsertColumn(0, _T("文件名"), LVCFMT_LEFT, 200); m_listImages.InsertColumn(1, _T("大小"), LVCFMT_RIGHT, 100); m_listImages.InsertColumn(2, _T("类型"), LVCFMT_LEFT, 100); m_saveOption.AddString(_T("覆盖原图")); m_saveOption.AddString(_T("新路径")); m_saveOption.SetCurSel(0); m_totalProgress.SetRange(0, 100); m_fileProgress.SetRange(0, 100); m_totalProgress.SetPos(0); m_fileProgress.SetPos(0); m_btnPause.EnableWindow(FALSE); m_btnStop.EnableWindow(FALSE); m_btnReset.EnableWindow(FALSE); m_originalImage.ModifyStyle(0, SS_BITMAP | SS_CENTERIMAGE); m_processedImage.ModifyStyle(0, SS_BITMAP| SS_CENTERIMAGE); m_originalImage.GetWindowRect(&m_originalRect); ScreenToClient(&m_originalRect); m_processedImage.GetWindowRect(&m_processedRect); ScreenToClient(&m_processedRect); Gdiplus::GdiplusStartupInput gdiplusInput; ULONG_PTR gdiplusToken; Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusInput, NULL); if (!m_folderPath.IsEmpty()) { PostMessage(WM_LOAD_FIRST_IMAGE, 0, 0); } return TRUE; } void B2saomiaotuxiangchuangkou::SetControlPosition(CWnd* pCtrl, int x, int y, int width, int height) { if (pCtrl) { CRect rect(x, y, x + width, y + height); pCtrl->MoveWindow(rect); } } void B2saomiaotuxiangchuangkou::AdjustLayoutWhenResized() { CRect clientRect; GetClientRect(&clientRect); int cx = clientRect.Width(); int cy = clientRect.Height(); // 获取屏幕尺寸(可选,若需要基于屏幕比例布局) int sW = GetSystemMetrics(SM_CXSCREEN); int sH = GetSystemMetrics(SM_CYSCREEN); int pc_W1 = (cx - 530) / 3;//图像显示控件宽 int pc_H1;//图像显示控件高 int pc_W2 = (cy - 530) / 3 * 2;//图像显示控件宽 int pc_H2;//图像显示控件高 // 示例:调整子界面中控件的位置(类似你提供的代码) SetControlPosition(GetDlgItem(IDC_STATIC1), 0, 5, 80, 30); /**/ SetControlPosition(GetDlgItem(IDC_STATIC2), 0, 50, 80, 30); /**/SetControlPosition(GetDlgItem(IDC_EDIT_FOLDER_PATH), 85, 43, 280, 30); /**/SetControlPosition(GetDlgItem(IDC_BUTTON_SELECT_FOLDER), 366, 44, 70, 30); /**/SetControlPosition(GetDlgItem(IDC_BUTTON_START), 440, 43, 70, 30);/**/SetControlPosition(GetDlgItem(IDC_STATIC_ORIGINAL_IMAGE), 530, 5, pc_W1 - 1, 490);/**/SetControlPosition(GetDlgItem(IDC_STATIC_PROCESSED_IMAGE), 530 + pc_W1 + 2, 5, 2 * pc_W1 - 5, cy - 10); SetControlPosition(GetDlgItem(IDC_STATIC3), 0, 90, 80, 30); /**/SetControlPosition(GetDlgItem(IDC_EDIT2), 85, 83, 280, 30); /**/SetControlPosition(GetDlgItem(IDC_COMBO_SAVE_OPTION), 366, 89, 70, 30); /**/SetControlPosition(GetDlgItem(IDC_BUTTON_PAUSE), 440, 84, 70, 30); SetControlPosition(GetDlgItem(IDC_STATIC4), 0, 155, 80, 30); /**/ /**/SetControlPosition(GetDlgItem(IDC_BUTTON_STOP), 440, 124, 70, 30); SetControlPosition(GetDlgItem(IDC_STATIC6), 0, 200, 80, 30); /**/SetControlPosition(GetDlgItem(IDC_PROGRESS_TOTAL), 85, 197, 280, 20); /**/SetControlPosition(GetDlgItem(IDC_BUTTON_RESET), 440, 164, 70, 30); SetControlPosition(GetDlgItem(IDC_STATIC7), 0, 240, 80, 30); /**/SetControlPosition(GetDlgItem(IDC_PROGRESS_FILE), 85, 237, 180, 20); SetControlPosition(GetDlgItem(IDC_STATIC8), 0, 305, 80, 30); /**/ /*SetControlPosition(GetDlgItem(IDC_STATIC8), 0, 305, 80, 30); /**/ /*SetControlPosition(GetDlgItem(IDC_STATIC8), 0, 305, 80, 30);*/ /**/ SetControlPosition(GetDlgItem(IDC_LIST_IMAGES), 0, 500, 530 + pc_W1 - 1, cy - 500 - 5); /**/ } void B2saomiaotuxiangchuangkou::OnSize(UINT nType, int cx, int cy) { CDialogEx::OnSize(nType, cx, cy); if (nType != SIZE_MINIMIZED) { AdjustLayoutWhenResized(); } } // 在实现文件中添加函数实现 void B2saomiaotuxiangchuangkou::LoadFilesIntoListControl() { // 清空列表 m_listImages.DeleteAllItems(); m_imageFiles.clear(); m_imageLoaded = false; m_firstLoadCompleted = false; // 获取文件夹中的所有文件 std::vector<CString> files; CString searchPath = m_folderPath + _T("\\*.*"); WIN32_FIND_DATA findFileData; HANDLE hFind = FindFirstFile(searchPath, &findFileData); if (hFind != INVALID_HANDLE_VALUE) { do { if (!(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { CString fileName = findFileData.cFileName; CString ext = fileName.Right(4).MakeLower(); if (ext == _T(".jpg") || ext == _T(".jpeg") || ext == _T(".png") || ext == _T(".bmp") || ext == _T(".tif") || ext == _T(".tiff")) { files.push_back(fileName); } } } while (FindNextFile(hFind, &findFileData)); FindClose(hFind); } // 使用 StrCmpLogicalW 进行自然排序 std::sort(files.begin(), files.end(), [](const CString& a, const CString& b) { return StrCmpLogicalW(a, b) < 0; }); // 将排序后的文件添加到列表控件 for (size_t i = 0; i < files.size(); ++i) { CString fileName = files[i]; CString filePath = m_folderPath + _T("\\") + fileName; m_imageFiles.push_back(filePath); // 添加到列表控件 int nIndex = m_listImages.InsertItem(i, fileName); // 获取文件大小 WIN32_FILE_ATTRIBUTE_DATA fad; ULONGLONG fileSize = 0; if (GetFileAttributesEx(filePath, GetFileExInfoStandard, &fad)) { fileSize = ((ULONGLONG)fad.nFileSizeHigh << 32) | fad.nFileSizeLow; } CString sizeStr; if (fileSize < 1024) sizeStr.Format(_T("%d B"), fileSize); else if (fileSize < 1024 * 1024) sizeStr.Format(_T("%.2f KB"), fileSize / 1024.0); else sizeStr.Format(_T("%.2f MB"), fileSize / (1024.0 * 1024.0)); m_listImages.SetItemText(nIndex, 1, sizeStr); // 文件类型 CString typeStr = fileName.Right(4).Mid(1).MakeUpper(); m_listImages.SetItemText(nIndex, 2, typeStr); } } void B2saomiaotuxiangchuangkou::OnBnClickedButtonSelectFolder() { CFolderPickerDialog dlg; if (dlg.DoModal() == IDOK) { m_folderPath = dlg.GetFolderPath(); m_editFolderPath.SetWindowText(m_folderPath); // 使用新的方法加载文件到列表控件 LoadFilesIntoListControl(); // 更新保存路径显示 OnCbnSelchangeComboSaveOption(); // 设置按钮状态 m_btnStart.EnableWindow(!m_imageFiles.empty()); // 显示第一张图片 if (!m_imageFiles.empty()) { m_listImages.SetItemState(0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); m_listImages.EnsureVisible(0, FALSE); m_listImages.UpdateWindow(); PostMessage(WM_LOAD_FIRST_IMAGE, 0, 0); } } } LRESULT B2saomiaotuxiangchuangkou::OnLoadFirstImageMessage(WPARAM wParam, LPARAM lParam) { DisplayFirstImage(); return 0; } void B2saomiaotuxiangchuangkou::DisplayFirstImage() { if (m_imageFiles.empty()) return; CString filePath = m_imageFiles[0]; if (!LoadImageToControl(filePath, IDC_STATIC_ORIGINAL_IMAGE)) { m_loadMode = (m_loadMode == MODE_OPENCV) ? MODE_GDIPlus : MODE_OPENCV; if (!LoadImageToControl(filePath, IDC_STATIC_ORIGINAL_IMAGE)) { return; } } m_firstImageLoaded = true; m_firstLoadCompleted = true; m_imageLoaded = true; CWnd* pProcessed = GetDlgItem(IDC_STATIC_PROCESSED_IMAGE); if (pProcessed) { auto pStatic = reinterpret_cast<CStatic*>(pProcessed); HBITMAP hOldBmp = pStatic->GetBitmap(); if (hOldBmp) { DeleteObject(hOldBmp); pStatic->SetBitmap(NULL); } } } bool B2saomiaotuxiangchuangkou::LoadImageToControl(const CString& filePath, UINT controlID) { std::lock_guard<std::mutex> lock(m_imageMutex); DWORD fileAttr = GetFileAttributes(filePath); if (fileAttr == INVALID_FILE_ATTRIBUTES) { return false; } HBITMAP hBitmap = NULL; if (m_loadMode == MODE_OPENCV) { hBitmap = LoadImageWithOpenCV(filePath); } else { hBitmap = LoadImageWithGDIPlus(filePath); } if (!hBitmap) { return false; } CWnd* pWnd = GetDlgItem(controlID); if (pWnd) { CStatic* pStatic = dynamic_cast<CStatic*>(pWnd); if (pStatic) { HBITMAP oldBmp = pStatic->GetBitmap(); if (oldBmp) DeleteObject(oldBmp); pStatic->SetBitmap(hBitmap); pStatic->Invalidate(); return true; } } DeleteObject(hBitmap); return false; } // 禁用鼠标滚轮缩放功能 BOOL B2saomiaotuxiangchuangkou::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) { // 直接返回TRUE,阻止滚轮缩放 return TRUE; } // 修改 LoadImageWithOpenCV 函数 HBITMAP B2saomiaotuxiangchuangkou::LoadImageWithOpenCV(const CString& filePath) { cv::Mat img; #ifdef _UNICODE int len = WideCharToMultiByte(CP_UTF8, 0, filePath, -1, NULL, 0, NULL, NULL); std::vector<char> utf8Buf(len); WideCharToMultiByte(CP_UTF8, 0, filePath, -1, utf8Buf.data(), len, NULL, NULL); img = cv::imread(utf8Buf.data(), cv::IMREAD_COLOR); #else img = cv::imread(CW2A(filePath), cv::IMREAD_COLOR); #endif if (img.empty()) { return NULL; } // 关键修改:添加BGR到RGB的颜色空间转换 cv::cvtColor(img, img, cv::COLOR_BGR2RGB); CRect rect; m_originalImage.GetClientRect(&rect); int dstWidth = rect.Width(); int dstHeight = rect.Height(); // 计算缩放比例和位置 double ratio = std::min(static_cast<double>(dstWidth) / img.cols, static_cast<double>(dstHeight) / img.rows); int drawWidth = static_cast<int>(img.cols * ratio); int drawHeight = static_cast<int>(img.rows * ratio); int x = (dstWidth - drawWidth) / 2; int y = (dstHeight - drawHeight) / 2; // 创建目标位图 HDC hdcScreen = ::GetDC(NULL); HBITMAP hBitmap = CreateCompatibleBitmap(hdcScreen, dstWidth, dstHeight); HDC hdcMem = CreateCompatibleDC(hdcScreen); HBITMAP hOldBmp = (HBITMAP)SelectObject(hdcMem, hBitmap); // 填充白色背景 HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255)); RECT rc = { 0, 0, dstWidth, dstHeight }; FillRect(hdcMem, &rc, hBrush); DeleteObject(hBrush); // 创建缩放后的图像 - 使用高质量缩放算法 cv::Mat resized; cv::resize(img, resized, cv::Size(drawWidth, drawHeight), 0, 0, cv::INTER_AREA); // 转换为 BITMAPINFO BITMAPINFOHEADER bmiHeader = { 0 }; bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmiHeader.biWidth = resized.cols; bmiHeader.biHeight = -resized.rows; // 负号表示从上到下的位图 bmiHeader.biPlanes = 1; bmiHeader.biBitCount = 24; bmiHeader.biCompression = BI_RGB; // 在目标DC上绘制图像(确保居中) SetStretchBltMode(hdcMem, HALFTONE); StretchDIBits( hdcMem, x, y, drawWidth, drawHeight, // 目标位置和大小 0, 0, resized.cols, resized.rows, // 源位置和大小 resized.data, (BITMAPINFO*)&bmiHeader, DIB_RGB_COLORS, SRCCOPY ); // 清理资源 SelectObject(hdcMem, hOldBmp); DeleteDC(hdcMem); ::ReleaseDC(NULL, hdcScreen); return hBitmap; } //// 修改 MatToBitmap 函数(确保其他地方的转换也正确) //HBITMAP B2saomiaotuxiangchuangkou::MatToBitmap(cv::Mat& mat) //{ // if (mat.empty()) return NULL; // // cv::Mat rgb; // // 添加颜色空间转换 // if (mat.channels() == 1) { // cv::cvtColor(mat, rgb, cv::COLOR_GRAY2BGR); // } // else if (mat.channels() == 3) { // cv::cvtColor(mat, rgb, cv::COLOR_BGR2RGB); // 关键修改 // } // else if (mat.channels() == 4) { // cv::cvtColor(mat, rgb, cv::COLOR_BGRA2BGR); // } // else { // rgb = mat; // } // // BITMAPINFOHEADER bmiHeader; // bmiHeader.biSize = sizeof(BITMAPINFOHEADER); // bmiHeader.biWidth = rgb.cols; // bmiHeader.biHeight = -rgb.rows; // bmiHeader.biPlanes = 1; // bmiHeader.biBitCount = 24; // bmiHeader.biCompression = BI_RGB; // bmiHeader.biSizeImage = 0; // bmiHeader.biXPelsPerMeter = 0; // bmiHeader.biYPelsPerMeter = 0; // bmiHeader.biClrUsed = 0; // bmiHeader.biClrImportant = 0; // // HDC hDC = ::GetDC(NULL); // HBITMAP hBitmap = CreateDIBitmap(hDC, &bmiHeader, CBM_INIT, // rgb.data, (BITMAPINFO*)&bmiHeader, DIB_RGB_COLORS); // ::ReleaseDC(NULL, hDC); // // return hBitmap; //} // 添加背景擦除处理,防止闪烁 BOOL B2saomiaotuxiangchuangkou::OnEraseBkgnd(CDC* pDC) { // 获取客户区矩形 CRect rect; GetClientRect(&rect); // 用白色填充背景 CBrush brush(RGB(255, 255, 255)); pDC->FillRect(&rect, &brush); return TRUE; // 返回TRUE表示我们已经擦除了背景 } //HBITMAP B2saomiaotuxiangchuangkou::LoadImageWithGDIPlus(const CString& filePath) { // Gdiplus::Image image(filePath); // if (image.GetLastStatus() != Gdiplus::Ok) return NULL; // // // 获取控件尺寸 // CRect rect; // m_originalImage.GetClientRect(&rect); // int controlWidth = rect.Width(); // int controlHeight = rect.Height(); // // // 计算缩放比例 // int srcWidth = image.GetWidth(); // int srcHeight = image.GetHeight(); // double ratio = std::min( // static_cast<double>(controlWidth) / srcWidth, // static_cast<double>(controlHeight) / srcHeight // ); // int drawWidth = static_cast<int>(srcWidth * ratio); // int drawHeight = static_cast<int>(srcHeight * ratio); // // // 计算起始坐标(如需左上角,设为 0) // int x = (controlWidth - drawWidth) / 2; // 水平居中(可改为 0) // int y = (controlHeight - drawHeight) / 2; // 垂直居中(可改为 0) // // // 创建位图并绘制 // Gdiplus::Bitmap bitmap(controlWidth, controlHeight, PixelFormat32bppARGB); // Gdiplus::Graphics graphics(&bitmap); // graphics.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality); // graphics.Clear(Gdiplus::Color(255, 255, 255)); // 白色背景 // // // 关键:从 (x,y) 开始绘制图像 // graphics.DrawImage( // &image, // x, y, // drawWidth, drawHeight // ); // // // 获取 HBITMAP 句柄 // HBITMAP hBitmap = NULL; // bitmap.GetHBITMAP(Gdiplus::Color::White, &hBitmap); // return hBitmap; //} HBITMAP B2saomiaotuxiangchuangkou::LoadImageWithGDIPlus(const CString& filePath) { Gdiplus::Image image(filePath); if (image.GetLastStatus() != Gdiplus::Ok) { return NULL; } CRect rect; m_originalImage.GetClientRect(&rect); int ctrlWidth = rect.Width(); int ctrlHeight = rect.Height(); int srcWidth = image.GetWidth(); int srcHeight = image.GetHeight(); // 计算缩放比例(保持原图比例) double scale = std::min((double)ctrlWidth / srcWidth, (double)ctrlHeight / srcHeight); int drawWidth = (int)(srcWidth * scale); int drawHeight = (int)(srcHeight * scale); // 原图目标点(如中心点) int srcTargetX = srcWidth / 2; // 原图中心点X int srcTargetY = srcHeight / 2; // 原图中心点Y // 控件中心点 int ctrlCenterX = ctrlWidth / 2; int ctrlCenterY = ctrlHeight / 2; // 计算绘制起点(使原图目标点对齐控件中心点) int x = ctrlCenterX - srcTargetX * scale; int y = ctrlCenterY - srcTargetY * scale; Gdiplus::Bitmap bitmap(ctrlWidth, ctrlHeight, PixelFormat32bppARGB); Gdiplus::Graphics graphics(&bitmap); graphics.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality); // 绘制图像(从原图(0,0)开始,按新起点绘制) graphics.DrawImage(&image, x, y, drawWidth, drawHeight); HBITMAP hBitmap = NULL; bitmap.GetHBITMAP(Gdiplus::Color::White, &hBitmap); return hBitmap; } HBITMAP B2saomiaotuxiangchuangkou::MatToBitmap(cv::Mat& mat) { if (mat.empty()) return NULL; cv::Mat bgr; if (mat.channels() == 1) { cv::cvtColor(mat, bgr, cv::COLOR_GRAY2BGR); } else if (mat.channels() == 4) { cv::cvtColor(mat, bgr, cv::COLOR_BGRA2BGR); } else { bgr = mat; } // 确保图像尺寸正确 if (bgr.rows <= 0 || bgr.cols <= 0) { return NULL; } BITMAPINFOHEADER bmiHeader; bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmiHeader.biWidth = bgr.cols; bmiHeader.biHeight = -bgr.rows; // 负号表示从下往上绘制 bmiHeader.biPlanes = 1; bmiHeader.biBitCount = 24; bmiHeader.biCompression = BI_RGB; bmiHeader.biSizeImage = 0; bmiHeader.biXPelsPerMeter = 0; bmiHeader.biYPelsPerMeter = 0; bmiHeader.biClrUsed = 0; bmiHeader.biClrImportant = 0; HDC hDC = ::GetDC(NULL); HBITMAP hBitmap = CreateDIBitmap(hDC, &bmiHeader, CBM_INIT, bgr.data, (BITMAPINFO*)&bmiHeader, DIB_RGB_COLORS); ::ReleaseDC(NULL, hDC); return hBitmap; } void B2saomiaotuxiangchuangkou::OnCbnSelchangeComboSaveOption() { int option = m_saveOption.GetCurSel(); if (option == 0) { m_savePath = m_folderPath; m_editSavePath.SetWindowText(m_savePath); } else if (option == 1) { if (m_savePath.IsEmpty() || m_savePath == m_folderPath) { CFolderPickerDialog dlg; if (dlg.DoModal() == IDOK) { m_savePath = dlg.GetFolderPath(); m_editSavePath.SetWindowText(m_savePath); } else { m_saveOption.SetCurSel(0); m_savePath = m_folderPath; m_editSavePath.SetWindowText(m_savePath); } } } } void B2saomiaotuxiangchuangkou::OnBnClickedButtonStart() { if (m_folderPath.IsEmpty()) { AfxMessageBox(_T("请先选择文件夹!")); return; } if (m_imageFiles.empty()) { AfxMessageBox(_T("文件夹中没有图像文件!")); return; } if (m_saveOption.GetCurSel() == 1 && m_savePath.IsEmpty()) { AfxMessageBox(_T("请先选择保存路径!")); return; } if (m_saveOption.GetCurSel() == 0) { CreateBackup(); } m_processing = true; m_paused = false; m_stopped = false; m_currentIndex = 0; m_btnStart.EnableWindow(FALSE); m_btnPause.EnableWindow(TRUE); m_btnStop.EnableWindow(TRUE); m_btnReset.EnableWindow(FALSE); m_totalProgress.SetPos(0); m_fileProgress.SetPos(0); if (m_processingThread.joinable()) { m_processingThread.join(); } m_processingThread = std::thread(&B2saomiaotuxiangchuangkou::ProcessImages, this); SetTimer(1, 100, NULL); } void B2saomiaotuxiangchuangkou::OnBnClickedButtonPause() { m_paused = !m_paused; m_btnPause.SetWindowText(m_paused ? _T("继续") : _T("暂停")); } void B2saomiaotuxiangchuangkou::OnBnClickedButtonStop() { m_stopped = true; m_processing = false; m_btnStart.EnableWindow(TRUE); m_btnPause.EnableWindow(FALSE); m_btnStop.EnableWindow(FALSE); m_btnReset.EnableWindow(TRUE); if (m_processingThread.joinable()) { m_processingThread.join(); } KillTimer(1); } void B2saomiaotuxiangchuangkou::OnBnClickedButtonReset() { OnBnClickedButtonStop(); if (m_saveOption.GetCurSel() == 0 && !m_backupPath.IsEmpty()) { RestoreBackup(); } m_currentIndex = 0; m_totalProgress.SetPos(0); m_fileProgress.SetPos(0); m_backupPath.Empty(); m_btnReset.EnableWindow(FALSE); if (!m_imageFiles.empty()) { m_listImages.SetItemState(0, LVIS_SELECTED, LVIS_SELECTED); PostMessage(WM_LOAD_FIRST_IMAGE, 0, 0); } } void B2saomiaotuxiangchuangkou::OnTimer(UINT_PTR nIDEvent) { if (nIDEvent == 1) { UpdateProgressBars(); } CDialogEx::OnTimer(nIDEvent); } void B2saomiaotuxiangchuangkou::OnLvnItemchangedListImages(NMHDR* pNMHDR, LRESULT* pResult) { LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR); int nItem = -1; if (pNMLV) { if ((pNMLV->uChanged & LVIF_STATE) && (pNMLV->uNewState & LVIS_SELECTED)) { nItem = pNMLV->iItem; } } else { nItem = 0; } if (!m_firstLoadCompleted && nItem == 0) { if (pResult) *pResult = 0; return; } if (nItem >= 0 && nItem < (int)m_imageFiles.size()) { CString filePath = m_imageFiles[nItem]; LoadImageToControl(filePath, IDC_STATIC_ORIGINAL_IMAGE); CWnd* pProcessed = GetDlgItem(IDC_STATIC_PROCESSED_IMAGE); if (pProcessed) { auto pStatic = reinterpret_cast<CStatic*>(pProcessed); HBITMAP hOldBmp = pStatic->GetBitmap(); if (hOldBmp) { DeleteObject(hOldBmp); pStatic->SetBitmap(NULL); } } } if (pResult) { *pResult = 0; } } void B2saomiaotuxiangchuangkou::ProcessImages() { for (m_currentIndex = 0; m_currentIndex < (int)m_imageFiles.size(); ++m_currentIndex) { if (m_stopped) break; while (m_paused && !m_stopped) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } if (m_stopped) break; ProcessImage(m_imageFiles[m_currentIndex]); { std::lock_guard<std::mutex> lock(m_mutex); int totalProgress = static_cast<int>((static_cast<double>(m_currentIndex + 1) / m_imageFiles.size()) * 100); m_totalProgress.SetPos(totalProgress); m_fileProgress.SetPos(100); } std::this_thread::sleep_for(std::chrono::milliseconds(100)); } m_processing = false; m_stopped = true; PostMessage(WM_COMMAND, MAKEWPARAM(IDC_BUTTON_STOP, BN_CLICKED), (LPARAM)m_btnStop.m_hWnd); AfxMessageBox(_T("处理完成!")); } void B2saomiaotuxiangchuangkou::ProcessImage(const CString& filePath) { CT2CA filePathConverted(filePath); std::string filePathStr(filePathConverted); cv::Mat image = cv::imread(filePathStr); if (image.empty()) return; // 更新显示当前处理的图片 UpdateDisplayImage(filePath); cv::Mat processed; SimpleBinarization(image, processed); if (!processed.empty()) { CString tempPath = m_folderPath + _T("\\temp_processed.jpg"); SaveImage(processed, tempPath); LoadImageToControl(tempPath, IDC_STATIC_PROCESSED_IMAGE); DeleteFile(tempPath); } if (m_saveOption.GetCurSel() == 0) { SaveImage(processed, filePath); } else { CString fileName = GetFileNameFromPath(filePath); CString newPath = m_savePath + _T("\\") + fileName; SaveImage(processed, newPath); } for (int progress = 0; progress <= 100; progress += 10) { if (m_stopped) return; while (m_paused && !m_stopped) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } if (m_stopped) return; { std::lock_guard<std::mutex> lock(m_mutex); m_fileProgress.SetPos(progress); } std::this_thread::sleep_for(std::chrono::milliseconds(50)); } } // 更新显示图片的函数 void B2saomiaotuxiangchuangkou::UpdateDisplayImage(const CString& filePath) { // 使用PostMessage安全地更新UI PostMessage(WM_UPDATE_DISPLAY_IMAGE, (WPARAM)(LPCTSTR)filePath, 0); } // 处理更新显示的消息 LRESULT B2saomiaotuxiangchuangkou::OnUpdateDisplayImage(WPARAM wParam, LPARAM lParam) { CString filePath = (LPCTSTR)wParam; if (filePath.IsEmpty()) return 0; std::lock_guard<std::mutex> lock(m_bitmapMutex); // 先释放之前的位图资源 if (m_currentBitmap) { DeleteObject(m_currentBitmap); m_currentBitmap = NULL; } // 加载新的位图 HBITMAP hBitmap = NULL; if (m_loadMode == MODE_OPENCV) { hBitmap = LoadImageWithOpenCV(filePath); } else { hBitmap = LoadImageWithGDIPlus(filePath); } if (hBitmap) { m_currentBitmap = hBitmap; CStatic* pStatic = dynamic_cast<CStatic*>(GetDlgItem(IDC_STATIC_ORIGINAL_IMAGE)); if (pStatic) { HBITMAP oldBmp = pStatic->GetBitmap(); if (oldBmp) DeleteObject(oldBmp); pStatic->SetBitmap(hBitmap); pStatic->Invalidate(); } } return 0; } void B2saomiaotuxiangchuangkou::SimpleBinarization(cv::Mat& input, cv::Mat& output) { cv::Mat gray; if (input.channels() == 3) { cv::cvtColor(input, gray, cv::COLOR_BGR2GRAY); } else { gray = input.clone(); } cv::threshold(gray, output, 128, 255, cv::THRESH_BINARY); } void B2saomiaotuxiangchuangkou::SaveImage(cv::Mat& img, const CString& filePath) { if (img.empty()) return; CT2CA filePathConverted(filePath); std::string filePathStr(filePathConverted); std::vector<int> compression_params; compression_params.push_back(cv::IMWRITE_JPEG_QUALITY); compression_params.push_back(95); cv::imwrite(filePathStr, img, compression_params); } void B2saomiaotuxiangchuangkou::UpdateProgressBars() { int totalProgress = static_cast<int>((static_cast<double>(m_currentIndex) / m_imageFiles.size() * 100)); m_totalProgress.SetPos(totalProgress); } void B2saomiaotuxiangchuangkou::CreateBackup() { if (m_folderPath.IsEmpty()) return; CTime time = CTime::GetCurrentTime(); CString timeStr = time.Format(_T("%Y%m%d%H%M%S")); m_backupPath = m_folderPath + _T("\\backup_") + timeStr; if (!CreateDirectory(m_backupPath, NULL)) { return; } for (const auto& file : m_imageFiles) { CString fileName = GetFileNameFromPath(file); CString destPath = m_backupPath + _T("\\") + fileName; CopyFile(file, destPath, FALSE); } } void B2saomiaotuxiangchuangkou::RestoreBackup() { if (m_backupPath.IsEmpty()) return; for (const auto& file : m_imageFiles) { CString fileName = GetFileNameFromPath(file); CString sourcePath = m_backupPath + _T("\\") + fileName; CopyFile(sourcePath, file, FALSE); } RemoveDirectory(m_backupPath); m_backupPath.Empty(); } void B2saomiaotuxiangchuangkou::Cleanup() { m_stopped = true; m_processing = false; if (m_processingThread.joinable()) { m_processingThread.join(); } KillTimer(1); // 释放位图资源 std::lock_guard<std::mutex> lock(m_bitmapMutex); if (m_currentBitmap) { DeleteObject(m_currentBitmap); m_currentBitmap = NULL; } HBITMAP hBmp = m_originalImage.GetBitmap(); if (hBmp) DeleteObject(hBmp); hBmp = m_processedImage.GetBitmap(); if (hBmp) DeleteObject(hBmp); } CString B2saomiaotuxiangchuangkou::GetFileNameFromPath(const CString& filePath) { int pos = filePath.ReverseFind('\\'); if (pos == -1) pos = filePath.ReverseFind('/'); if (pos != -1) { return filePath.Mid(pos + 1); } return filePath; } 该代码IDC_STATIC_ORIGINAL_IMAGE控件绘图时,绘制的图片相对于源图发生了水平偏移

大家在看

recommend-type

.NET frxamework v2.0 64位

Microsoft .NET framework 2.0 64位可再发行组件包将安装 .NET framework 运行库,以及运行面向 .NET framework 2.0 版开发的 64 位应用程序所需的相关文件。
recommend-type

服务质量管理-NGBOSS能力架构

服务质量管理 二级能力名称 服务质量管理 二级能力编号 CMCM.5.4 概述 监测、分析和控制客户感知的服务表现 相关子能力描述 能够主动的将网络性能数据通告给前端客服人员; 能够根据按照客户价值来划分的客户群来制定特殊的SLA指标; 能够为最有价值的核心客户群进行网络优化; 对于常规的维护问题,QoS能够由网元设备自动完成,比如,对于网络故障的自恢复能力和优先客户的使用权; 能够把潜在的网络问题与客户进行主动的沟通; 能够分析所有的服务使用的质量指标; 能够根据关键的服务质量指标检测与实际的差距,提出改进建议; Service request 服务请求---请求管理。 客户的分析和报告:对关闭的请求、用户联系和相关的报告进行分析。 Marketing collateral的散发和marketing Collateral 的散发后的线索跟踪
recommend-type

AUTOSAR_MCAL_WDG.zip

This User Manual describes NXP Semiconductors AUTOSAR Watchdog ( Wdg ) for S32K14X . AUTOSAR Wdg driver configuration parameters and deviations from the specification are described in Wdg Driver chapter of this document. AUTOSAR Wdg driver requirements and APIs are described in the AUTOSAR Wdg driver software specification document.
recommend-type

MATLABSimulinkCommunicationSystemmaster_matlab_matlabsimulink_

MATLAB通信系统仿真历程,基于参考书《详解MATLAB/Simulink通信系统建模仿真》。都是里面的例子
recommend-type

multisim 实现四位二进制密码锁功能密码锁.rar

1、在锁的控制电路中储存一个可修改的四位二进制代码作为密码,当输入代码与锁的密码相等时,进入开锁状态使锁打开。开锁状态时绿灯亮。 2、从第一个按键触动后的5秒内未将锁打开,则电路进入自锁状态,使之无法再打开,并由扬声器发出持续10秒的报警信号。自锁状态时红灯亮。

最新推荐

recommend-type

logback-classic-1.4.11.jar中文文档.zip

1、压缩文件中包含: 中文文档、jar包下载地址、Maven依赖、Gradle依赖、源代码下载地址。 2、使用方法: 解压最外层zip,再解压其中的zip包,双击 【index.html】 文件,即可用浏览器打开、进行查看。 3、特殊说明: (1)本文档为人性化翻译,精心制作,请放心使用; (2)只翻译了该翻译的内容,如:注释、说明、描述、用法讲解 等; (3)不该翻译的内容保持原样,如:类名、方法名、包名、类型、关键字、代码 等。 4、温馨提示: (1)为了防止解压后路径太长导致浏览器无法打开,推荐在解压时选择“解压到当前文件夹”(放心,自带文件夹,文件不会散落一地); (2)有时,一套Java组件会有多个jar,所以在下载前,请仔细阅读本篇描述,以确保这就是你需要的文件。 5、本文件关键字: jar中文文档.zip,java,jar包,Maven,第三方jar包,组件,开源组件,第三方组件,Gradle,中文API文档,手册,开发手册,使用手册,参考手册。
recommend-type

基于Debian Jessie的Kibana Docker容器部署指南

Docker是一种开源的容器化平台,它允许开发者将应用及其依赖打包进一个可移植的容器中。Kibana则是由Elastic公司开发的一款开源数据可视化插件,主要用于对Elasticsearch中的数据进行可视化分析。Kibana与Elasticsearch以及Logstash一起通常被称为“ELK Stack”,广泛应用于日志管理和数据分析领域。 在本篇文档中,我们看到了关于Kibana的Docker容器化部署方案。文档提到的“Docker-kibana:Kibana 作为基于 Debian Jessie 的Docker 容器”实际上涉及了两个版本的Kibana,即Kibana 3和Kibana 4,并且重点介绍了它们如何被部署在Docker容器中。 Kibana 3 Kibana 3是一个基于HTML和JavaScript构建的前端应用,这意味着它不需要复杂的服务器后端支持。在Docker容器中运行Kibana 3时,容器实际上充当了一个nginx服务器的角色,用以服务Kibana 3的静态资源。在文档中提及的配置选项,建议用户将自定义的config.js文件挂载到容器的/kibana/config.js路径。这一步骤使得用户能够将修改后的配置文件应用到容器中,以便根据自己的需求调整Kibana 3的行为。 Kibana 4 Kibana 4相较于Kibana 3,有了一个质的飞跃,它基于Java服务器应用程序。这使得Kibana 4能够处理更复杂的请求和任务。文档中指出,要通过挂载自定义的kibana.yml文件到容器的/kibana/config/kibana.yml路径来配置Kibana 4。kibana.yml是Kibana的主要配置文件,它允许用户配置各种参数,比如Elasticsearch服务器的地址,数据索引名称等等。通过Docker容器部署Kibana 4,用户可以很轻松地利用Docker提供的环境隔离和可复制性特点,使得Kibana应用的部署和运维更为简洁高效。 Docker容器化的优势 使用Docker容器化技术部署Kibana,有几个显著的优势: - **一致性**:Docker容器确保应用在开发、测试和生产环境中的行为保持一致。 - **轻量级**:相比传统虚拟机,Docker容器更加轻量,启动快速,资源占用更少。 - **隔离性**:容器之间的环境隔离,确保应用之间互不干扰。 - **可移植性**:容器可以在任何支持Docker的环境中运行,提高了应用的可移植性。 - **易于维护**:通过Dockerfile可以轻松构建和分发应用镜像,便于维护和升级。 在文档中,我们还看到了文件名“docker-kibana-master”。这个名称很可能是指向了存放Docker相关文件的源代码仓库,其中可能包含Dockerfile、构建和运行脚本以及可能的配置模板文件等。开发者可以从这个仓库中克隆或下载所需的Docker相关文件,并根据这些文件来构建和部署Kibana的Docker容器。 根据以上信息,对于希望利用Docker容器部署和管理Kibana应用的用户,需要掌握Docker的基本使用方法,包括Docker的安装、镜像管理、容器的创建和配置等。同时,还需要了解Kibana的基本原理和配置方法,这样才能充分利用Docker容器化带来的便利,优化Kibana应用的部署和管理流程。
recommend-type

Coze智能体工作流:打造钦天监视频内容的创新与实践

# 1. Coze智能体工作流概述 Coze智能体工作流是业界领先的智能工作流程管理系统,它通过整合最新的智能技术与人工智能算法,旨在为视频内容创造与优化带来革命性的变革。本章将对Coze智能体工作流进行全面的概述,揭示其在IT行业中的应用范围、流程结构以及技术基础。通过了解Coze智能体工作流,读者将能够把握它在当前和未
recommend-type

使用git仓库的利与弊

<think>好的,我需要分析Git仓库的优缺点。首先,根据用户提供的引用,看看有哪些点需要覆盖。引用[1]提到了Git有本地和远程仓库,需要push命令来更新远程,这应该属于优点还是缺点呢?可能优点在于本地操作灵活,缺点是需要额外命令。 引用[2]指出Git的权限管理薄弱,学习曲线陡峭,这显然是缺点。另外,引用[4]提到Git使用SHA-1哈希,内容完整性更好,属于优点。引用[5]比较了Git的分支管理简单,而缺少全局版本号,这可以作为优点和缺点分别说明。 现在需要将这些信息整理成结构化的优缺点,可能分点列出。同时,用户要求回答结构清晰,逐步解决问题,所以可能需要先介绍Git的基本概念,
recommend-type

TextWorld:基于文本游戏的强化学习环境沙箱

在给出的文件信息中,我们可以提取到以下IT知识点: ### 知识点一:TextWorld环境沙箱 **标题**中提到的“TextWorld”是一个专用的学习环境沙箱,专为强化学习(Reinforcement Learning,简称RL)代理的训练和测试而设计。在IT领域中,尤其是在机器学习的子领域中,环境沙箱是指一个受控的计算环境,允许实验者在隔离的条件下进行软件开发和测试。强化学习是一种机器学习方法,其中智能体(agent)通过与环境进行交互来学习如何在某个特定环境中执行任务,以最大化某种累积奖励。 ### 知识点二:基于文本的游戏生成器 **描述**中说明了TextWorld是一个基于文本的游戏生成器。在计算机科学中,基于文本的游戏(通常被称为文字冒险游戏)是一种游戏类型,玩家通过在文本界面输入文字指令来与游戏世界互动。TextWorld生成器能够创建这类游戏环境,为RL代理提供训练和测试的场景。 ### 知识点三:强化学习(RL) 强化学习是**描述**中提及的关键词,这是一种机器学习范式,用于训练智能体通过尝试和错误来学习在给定环境中如何采取行动。在强化学习中,智能体在环境中探索并执行动作,环境对每个动作做出响应并提供一个奖励或惩罚,智能体的目标是学习一个策略,以最大化长期累积奖励。 ### 知识点四:安装与支持的操作系统 **描述**提到TextWorld的安装需要Python 3,并且当前仅支持Linux和macOS系统。对于Windows用户,提供了使用Docker作为解决方案的信息。这里涉及几个IT知识点: - **Python 3**:一种广泛使用的高级编程语言,适用于快速开发,是进行机器学习研究和开发的常用语言。 - **Linux**和**macOS**:两种流行的操作系统,分别基于Unix系统和类Unix系统。 - **Windows**:另一种广泛使用的操作系统,具有不同的软件兼容性。 - **Docker**:一个开源的应用容器引擎,允许开发者打包应用及其依赖环境为一个轻量级、可移植的容器,使得在任何支持Docker的平台上一致地运行。 ### 知识点五:系统库和依赖 **描述**提到在基于Debian/Ubuntu的系统上,可以安装一些系统库来支持TextWorld的本机组件。这里涉及的知识点包括: - **Debian/Ubuntu**:基于Debian的Linux发行版,是目前最流行的Linux发行版之一。 - **系统库**:操作系统中包含的一系列预编译的软件包和库,供应用程序在运行时使用。 - **包管理工具**,如**apt**(Advanced Package Tool),它是一个在Debian及其衍生系统中用于安装、删除和管理软件包的命令行工具。 ### 知识点六:与创建者联系方式 **描述**提供了与TextWorld创建者的联系方式,包括电子邮件地址和一个Gitter频道。这说明了如何与开源项目的维护者进行沟通与反馈: - **电子邮件**是常见的沟通方式,允许用户与开发者直接交流。 - **Gitter**是一个基于GitHub的即时消息工具,通常用于开源项目中的实时协作和交流。 ### 结语 综合以上信息,我们可以了解到TextWorld是一个专为强化学习设计的学习环境沙箱,它通过创建基于文本的游戏环境,让研究者和开发者训练和测试RL代理。它主要针对Linux和macOS系统,不过也有适合Windows用户的替代方案。此外,了解如何安装和配置TextWorld,以及如何与创建者沟通,对于开发者来说是十分重要的基础技能。
recommend-type

Coze智能体工作流全攻略

# 1. Coze智能体工作流概述 在现代企业中,工作流管理不仅是提高效率的关键因素,而且
recommend-type

64位小端转大端c语言函数起名

<think>好的,用户需要找一个C语言函数的名字,用来将64位小端格式转换为大端格式。首先,我得回顾一下C语言中字节序转换函数的命名惯例。通常,像htonl和htons这样的函数用于将主机字节序转换到网络字节序(大端)。其中,htonl处理32位,htons处理16位。不过,对于64位的情况,可能没有标准函数,所以需要自己实现,并且命名也要符合惯例。 接下来,用户提到的是64位的小端转大端。这时候,可能需要类似htonll这样的名字,因为htonl是host to network long(32位),那么htonll可能就是host to network long long(64位)。不过
recommend-type

upReveal.js: 利用鼠标移动揭示图像的创新技术

根据提供的文件信息,我们可以分析并生成以下知识点: ### upReveal.js技术知识点 #### 标题分析 标题 "upReveal.js:upReveal.js 通过鼠标在图像上的移动来显示图像!" 明确告诉我们,该技术是一个JavaScript库,它的核心功能是允许用户通过在图像上移动鼠标来揭示隐藏在图像下面的其他图像或内容。这样的功能特别适合用于创建富有互动性的网页设计。 #### 描述分析 描述中提到的“向上揭示 upReveal 效果”表明upReveal.js使用了一种特定的视觉效果来显示图像。这种效果可以让用户感觉到图像好像是从底层“向上”显现出来的,从而产生一种动态和引人入胜的视觉体验。描述还提到了版权信息,指出upReveal.js拥有版权所有,且该许可证伴随源代码提供。这表明开发者或公司可以使用这个库,但需要注意其许可证条款,以确保合法合规使用。 #### 标签分析 标签“HTML”意味着这个JavaScript库需要与HTML配合使用,具体可能涉及对HTML的img标签或其他元素进行操作,以实现图像揭示的效果。HTML是构建网页内容的基础,而JavaScript则是用来增加交互性和动态效果的脚本语言,upReveal.js正是在这个层面上发挥作用。 #### 压缩包子文件的文件名称列表分析 文件名称列表 "upReveal.js-master" 表明该JavaScript库可以通过一个名为“upReveal.js”的主文件来引入和使用。文件名中的“master”通常意味着这是主版本或主要代码分支,用户可以使用该文件作为起点来集成和应用这个效果。 ### upReveal.js的具体知识点 1. **图像揭示技术:** upReveal.js利用鼠标悬停(hover)事件来实现图像揭示效果。当用户将鼠标移动到指定图像上时,底层图像或内容会被逐渐显示出来。 2. **CSS和JavaScript交互:** 要实现这种效果,upReveal.js可能会结合使用CSS来设计图像覆盖层和动画效果,同时利用JavaScript来监听鼠标事件并控制图像的显示逻辑。 3. **跨浏览器兼容性:** 一个成功的JavaScript库应该能够在不同的浏览器上一致地工作。upReveal.js可能包含跨浏览器兼容性的代码,确保所有用户都能体验到相同的效果。 4. **许可证使用:** 虽然upReveal.js允许用户使用,但开发者需要阅读并理解伴随源代码提供的许可证条款。通常这会涉及对源代码的使用、修改和重新分发的限制。 5. **HTML集成:** 为了使用upReveal.js,开发者需要在HTML文件中通过脚本标签引入JavaScript文件。同时,可能需要准备相应的HTML结构来展示图像。 6. **自定义和配置:** upReveal.js很可能会提供一些配置选项,允许开发者调整效果的动画速度、触发区域大小等,以适应不同的设计需求。 7. **性能和优化:** 在设计交互式图像效果时,性能优化是一个关键考虑因素。upReveal.js可能会通过优化代码和资源使用,减少对页面加载和交互性能的影响。 8. **可访问性考虑:** 虽然描述中未提及,但在开发类似JavaScript库时,考虑可访问性是一个好的实践,确保所有用户,包括那些有视觉障碍的用户,都能够受益于这种技术。 通过上述分析,我们可以看到upReveal.js作为一个JavaScript库,不仅提供了动态的交互效果,还涉及到了前端开发的多个方面,包括但不限于HTML结构设计、CSS样式应用、JavaScript事件处理、跨浏览器兼容性、性能优化以及许可证协议的遵守等。开发者在使用upReveal.js时,应该综合考虑这些知识点,以实现最佳的用户体验。
recommend-type

金融服务中AI Agent的崛起:智能投资顾问与风险管理

# 1. 金融服务中的AI Agent概述 金融服务行业正经历数字化转型,其中AI Agent(人工智能代理)扮演着越来越重要的角色。AI Agent,一种能够通过学习和适应来执行复杂任务的软件代理,已经广泛应用于金融服务的多个领域,如智能投资顾问、风险管理和合规性监控等。 在这一章,
recommend-type

小端转大端c语言函数起名

<think>好的,我需要帮用户找到一个合适的C语言函数名,用于将小端模式转换为大端模式。首先,我应该回顾一下关于大小端的基本知识,确保自己理解正确。根据引用[1]和[3],大端模式是高位字节存储在低地址,小端则是低位字节在高地址。转换函数通常涉及字节顺序的调整。 接下来,考虑命名规范。C语言中,函数名通常使用小写字母和下划线组合,具有描述性。需要明确函数的作用是转换字节序,所以名称中应包含类似“endian”、“swap”或“convert”的关键词。例如,像htonl(host to network long)这样的标准函数,网络字节序是大端,所以类似的命名可能适用。 用户提供的引用中