cook-c100/src/app/Module/wjh_BMP.c
2024-08-22 22:13:20 +08:00

285 lines
9.5 KiB
C
Raw 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.

/*
* 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 * bytes_per_pixel % 4)) % 4;
// for (int i = 0; i < padding; ++i) {
// row[width + i] = 0xFF;
// }
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, header.width * 2, 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];
}
//不需要拉伸
if(header.width == new_width&&header.height == new_height){
memcpy(scaled_data + header.data_offset,padding?aligned_data:(bmp_data + header.data_offset),new_width * new_height * 2);
}else{
scale_image(padding?aligned_data:(bmp_data + header.data_offset), scaled_data + header.data_offset, &header, new_width, new_height);
}
// 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;
}