285 lines
9.5 KiB
C
285 lines
9.5 KiB
C
/*
|
||
* 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;
|
||
}
|