/* * wjh_BMP.c * * Created on: 2024年7月25日 * Author: wu */ #include "wjh_BMP.h" // BMP 头文件 typedef struct { unsigned char header[100]; unsigned int width; unsigned int height; unsigned short bits_per_pixel; unsigned int data_offset; unsigned int data_size; } BMPHeader; // 从 BMP 数据中提取头信息 static void parse_bmp_header(unsigned char *bmp_data, BMPHeader *header) { u32 offset = bmp_data[13]; offset = offset * 256 + bmp_data[12]; offset = offset * 256 + bmp_data[11]; offset = offset * 256 + bmp_data[10]; memcpy(header->header, bmp_data, offset); header->width = bmp_data[21]; header->width = header->width * 256 + bmp_data[20]; header->width = header->width * 256 + bmp_data[19]; header->width = header->width * 256 + bmp_data[18]; header->height = bmp_data[25]; header->height = header->height * 256 + bmp_data[24]; header->height = header->height * 256 + bmp_data[23]; header->height = header->height * 256 + bmp_data[22]; header->bits_per_pixel = bmp_data[29]; header->bits_per_pixel = header->bits_per_pixel * 256 + bmp_data[28]; header->data_offset = offset; header->data_size = bmp_data[37]; header->data_size = header->data_size * 256 + bmp_data[36]; header->data_size = header->data_size * 256 + bmp_data[35]; header->data_size = header->data_size * 256 + bmp_data[34]; } // 从 BMP 数据中提取头信息 static void Set_bmp_header(u8 *scaled_data,u32 new_width,u32 new_height,u32 total) { scaled_data[18] = new_width % 256; new_width /= 256; scaled_data[19] = new_width % 256; new_width /= 256; scaled_data[20] = new_width % 256; new_width /= 256; scaled_data[21] = new_width % 256; scaled_data[22] = new_height % 256; new_height /= 256; scaled_data[23] = new_height % 256; new_height /= 256; scaled_data[24] = new_height % 256; new_height /= 256; scaled_data[25] = new_height % 256; scaled_data[34] = total % 256; total /= 256; scaled_data[35] = total % 256; total /= 256; scaled_data[36] = total % 256; total /= 256; scaled_data[37] = total % 256; } // 获取像素数据 static unsigned short get_pixel(unsigned char *data, int x, int y, int width,int bits_per_pixel) { int index = y * width + x; if (bits_per_pixel == 24) { // 24位BMP return ((unsigned short *)data)[index]; } else if (bits_per_pixel == 16) { // 16位R5G6B5 BMP unsigned short color; color = data[index * 2]; // 蓝色 color |= (data[index * 2 + 1] << 8); // 绿色 return color; } else { // 未知位深 return 0; } } // 设置像素数据 static void set_pixel(unsigned char *data, int x, int y, int width, int bits_per_pixel, unsigned short color) { int index = y * width + x; if (bits_per_pixel == 24) { // 24位BMP ((unsigned short *)data)[index] = color; } else if (bits_per_pixel == 16) { // 16位R5G6B5 BMP data[index * 2] = color; // 蓝色 data[index * 2 + 1] = color >> 8; // 绿色 } else { // 未知位深 return; } } // 线性插值函数 static unsigned short interpolate(unsigned short a, unsigned short b, float t) { unsigned short red = ((a >> 11) & 0x1F) * (1 - t) + ((b >> 11) & 0x1F) * t; unsigned short green = ((a >> 5) & 0x3F) * (1 - t) + ((b >> 5) & 0x3F) * t; unsigned short blue = (a & 0x1F) * (1 - t) + (b & 0x1F) * t; return (red << 11) | (green << 5) | blue; } // 图像缩放 static void scale_image_ByEqualRatio(unsigned char *src, unsigned char *dst, BMPHeader *header, float scale) { int new_width = header->width * scale; int new_height = header->height * scale; int y; for (y = 0; y < new_height; y++) { int x; for (x = 0; x < new_width; x++) { float src_x = x / scale; float src_y = y / scale; int x0 = (int)src_x; int y0 = (int)src_y; int x1 = x0 + 1; int y1 = y0 + 1; if (x1 >= header->width) x1 = header->width - 1; if (y1 >= header->height) y1 = header->height - 1; unsigned short p0 = get_pixel(src, x0, y0, header->width,header->bits_per_pixel); unsigned short p1 = get_pixel(src, x1, y0, header->width,header->bits_per_pixel); unsigned short p2 = get_pixel(src, x0, y1, header->width,header->bits_per_pixel); unsigned short p3 = get_pixel(src, x1, y1, header->width,header->bits_per_pixel); float tx = src_x - x0; float ty = src_y - y0; unsigned short interpolated_pixel = interpolate( interpolate(p0, p1, tx), interpolate(p2, p3, tx), ty ); set_pixel(dst, x, y, new_width,header->bits_per_pixel, interpolated_pixel); } } } u8* BMP_Scale_Change_ByEqualRatio(u8 *bmp_data,float scale) { BMPHeader header; parse_bmp_header(bmp_data, &header); int new_width = header.width * scale; int new_height = header.height * scale; return BMP_Scale_Change(bmp_data,new_width,new_height); // unsigned char *scaled_data = (unsigned char *)malloc(new_width * new_height * 2 + 54); // // for(u32 i = 0;i < 54;i ++){ // scaled_data[i] = header.header[i]; // } // // scale_image_ByEqualRatio(bmp_data + header.data_offset, scaled_data + 54, &header, scale); // // // 更新 BMP 头信息 // Set_bmp_header(scaled_data,new_width,new_height,new_width * new_height * 2); // // return scaled_data; } // 图像缩放 static void scale_image(unsigned char *src, unsigned char *dst, BMPHeader *header, int new_width, int new_height) { int y; for (y = 0; y < new_height; y++) { int x; for (x = 0; x < new_width; x++) { //算出新的这个左边颜色应该通过原图哪个位置的坐标颜色进行计算 float src_x = (float)x * header->width / new_width; float src_y = (float)y * header->height / new_height; int x0 = (int)src_x;//原图对应格子x轴 int y0 = (int)src_y;//原图对应格子y轴 int x1 = x0 + 1;//找四个格子进行算法扩张,所以找到下一个格子坐标 int y1 = y0 + 1; //如果格子超了范围,则用当前格子 if (x1 >= header->width) x1 = header->width - 1; if (y1 >= header->height) y1 = header->height - 1; //获取四个格子的RGB值 unsigned short p0 = get_pixel(src, x0, y0, header->width,header->bits_per_pixel); unsigned short p1 = get_pixel(src, x1, y0, header->width,header->bits_per_pixel); unsigned short p2 = get_pixel(src, x0, y1, header->width,header->bits_per_pixel); unsigned short p3 = get_pixel(src, x1, y1, header->width,header->bits_per_pixel); //算法 float tx = src_x - x0; float ty = src_y - y0; unsigned short interpolated_pixel = interpolate( interpolate(p0, p1, tx),//算出x轴对应的两个格子的中间值 interpolate(p2, p3, tx),//算出x轴对应的两个格子的中间值 ty );//算出y轴对应的两个格子的中间值 set_pixel(dst, x, y, new_width,header->bits_per_pixel, interpolated_pixel); } } } // 对齐每一行数据到4字节边界 void align_row(unsigned char *row, int width, int bytes_per_pixel) { int padding = (4 - (width % 4)) % 4; for (int i = 0; i < padding; ++i) { row[width + i] = row[width + i - 2]; } } //----BMP图根据宽高进行拉伸或缩小------------------------------------------------------------- //描述: 将目标bmp_data数组拉伸或缩小至width和heigh大小 //参数:bmp_data:图片数据 new_width:目标宽度 new_height:目标高度 //返回:等比缩放后的bmp图片数据(需要自行释放!!!!!!!!) //------------------------------------------------------------------------------ u8* BMP_Scale_Change(u8 *bmp_data,u32 new_width,u32 new_height){ BMPHeader header; parse_bmp_header(bmp_data, &header); // 计算每行需要的填充字节 int stride = header.width * 2; // 每个像素2字节 int padding = (4 - (stride % 4)) % 4; // 计算填充字节 stride += padding; // 添加填充字节 header.width = stride / 2; unsigned char *aligned_data = NULL; if(padding != 0){ // 创建一个新的图像数据数组,包括填充字节 aligned_data = (unsigned char *)malloc(header.height * (header.width + stride) * 2); for (int y = 0; y < header.height; ++y) { memcpy(aligned_data + y * stride, bmp_data + header.data_offset + y * header.width * 2, header.width * 2); align_row(aligned_data + y * stride, stride - padding, 2); } } unsigned char *scaled_data = (unsigned char *)malloc(new_width * new_height * 2 + header.data_offset); for(u32 i = 0;i < header.data_offset;i ++){ scaled_data[i] = header.header[i]; } scale_image(padding?aligned_data:(bmp_data + header.data_offset), scaled_data + header.data_offset, &header, new_width, new_height); if(aligned_data != NULL){ free(aligned_data); } // 更新 BMP 头信息 Set_bmp_header(scaled_data,new_width,new_height,new_width * new_height * 2); return scaled_data; }