本文最后更新于 159 天前,其中的信息可能已经有所发展或是发生改变。
#include <stdio.h>
#include <setjmp.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include </home/gec/software/installed/jepg/include/jpeglib.h>
// ...(保持你的结构体定义不变)...
#pragma pack(push, 1) // 禁用内存对齐
typedef struct
{
uint16_t bfType; // 文件标识,必须为 "BM"(0x4D42)
uint32_t bfSize; // 文件总大小(字节)
uint16_t bfReserved1; // 保留字段(必须为0)
uint16_t bfReserved2; // 保留字段(必须为0)
uint32_t bfOffBits; // 像素数据偏移量(从文件头到像素数据的字节数)
} BITMAPFILEHEADER;
#pragma pack(pop)
typedef struct
{
uint32_t biSize; // 本结构体大小(通常为40)
int32_t biWidth; // 图像宽度(像素)
int32_t biHeight; // 图像高度(像素),正数表示从下到上存储
uint16_t biPlanes; // 颜色平面数(必须为1)
uint16_t biBitCount; // 每像素位数(1/4/8/16/24/32)
uint32_t biCompression; // 压缩方式(0表示不压缩)
uint32_t biSizeImage; // 像素数据大小(字节),压缩时需填写
int32_t biXPelsPerMeter; // 水平分辨率(像素/米)
int32_t biYPelsPerMeter; // 垂直分辨率(像素/米)
uint32_t biClrUsed; // 实际使用的调色板颜色数(0表示全部)
uint32_t biClrImportant; // 重要颜色数(0表示所有颜色均重要)
} BITMAPINFOHEADER;
/*
* Sample routine for JPEG compression. We assume that the target file name
* and a compression quality factor are passed in.
*/
GLOBAL(void)
write_JPEG_file (char * filename, int quality, unsigned char *image_buffer, int image_width, int image_height)
{
/* This struct contains the JPEG compression parameters and pointers to
* working space (which is allocated as needed by the JPEG library).
* It is possible to have several such structures, representing multiple
* compression/decompression processes, in existence at once. We refer
* to any one struct (and its associated working data) as a "JPEG object".
*/
struct jpeg_compress_struct cinfo;
/* This struct represents a JPEG error handler. It is declared separately
* because applications often want to supply a specialized error handler
* (see the second half of this file for an example). But here we just
* take the easy way out and use the standard error handler, which will
* print a message on stderr and call exit() if compression fails.
* Note that this struct must live as long as the main JPEG parameter
* struct, to avoid dangling-pointer problems.
*/
struct jpeg_error_mgr jerr;
/* More stuff */
FILE * outfile; /* target file */
JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
int row_stride; /* physical row width in image buffer */
/* Step 1: allocate and initialize JPEG compression object */
/* We have to set up the error handler first, in case the initialization
* step fails. (Unlikely, but it could happen if you are out of memory.)
* This routine fills in the contents of struct jerr, and returns jerr's
* address which we place into the link field in cinfo.
*/
cinfo.err = jpeg_std_error(&jerr);
/* Now we can initialize the JPEG compression object. */
jpeg_create_compress(&cinfo);
/* Step 2: specify data destination (eg, a file) */
/* Note: steps 2 and 3 can be done in either order. */
/* Here we use the library-supplied code to send compressed data to a
* stdio stream. You can also write your own code to do something else.
* VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
* requires it in order to write binary files.
*/
if ((outfile = fopen(filename, "wb")) == NULL) {
fprintf(stderr, "can't open %s\n", filename);
exit(1);
}
jpeg_stdio_dest(&cinfo, outfile);
/* Step 3: set parameters for compression */
/* First we supply a description of the input image.
* Four fields of the cinfo struct must be filled in:
*/
cinfo.image_width = image_width; /* image width and height, in pixels */
cinfo.image_height = image_height;
cinfo.input_components = 3; /* # of color components per pixel */
cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
/* Now use the library's routine to set default compression parameters.
* (You must set at least cinfo.in_color_space before calling this,
* since the defaults depend on the source color space.)
*/
jpeg_set_defaults(&cinfo);
/* Now you can set any non-default parameters you wish to.
* Here we just illustrate the use of quality (quantization table) scaling:
*/
jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
/* Step 4: Start compressor */
/* TRUE ensures that we will write a complete interchange-JPEG file.
* Pass TRUE unless you are very sure of what you're doing.
*/
jpeg_start_compress(&cinfo, TRUE);
/* Step 5: while (scan lines remain to be written) */
/* jpeg_write_scanlines(...); */
/* Here we use the library's state variable cinfo.next_scanline as the
* loop counter, so that we don't have to keep track ourselves.
* To keep things simple, we pass one scanline per call; you can pass
* more if you wish, though.
*/
row_stride = image_width * 3; /* JSAMPLEs per row in image_buffer */
while (cinfo.next_scanline < cinfo.image_height) {
/* jpeg_write_scanlines expects an array of pointers to scanlines.
* Here the array is only one element long, but you could pass
* more than one scanline at a time if that's more convenient.
*/
row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
(void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
jpeg_finish_compress(&cinfo);
fclose(outfile);
jpeg_destroy_compress(&cinfo);
}
int main(int argc, const char* argv[])
{
if(argc != 2)
{
printf("用法: %s <bmp图片路径> \n", argv[0]);
return 0;
}
FILE* bmp = fopen(argv[1], "rb");
if(bmp == NULL)
{
printf("bmp图片加载失败!\n");
return 0;
}
BITMAPFILEHEADER fileheader;
BITMAPINFOHEADER infoheader;
fread(&fileheader, 14, 1, bmp);
fread(&infoheader, 40, 1, bmp);
int width = infoheader.biWidth;
int height = infoheader.biHeight;
// 有些少见的BMP是从上到下的,高度为负数,做个绝对值保护
if (height < 0) height = -height;
// 1. 计算 BMP 每行带补齐的实际字节数
int bmp_row_stride = ((width * 3 + 3) / 4) * 4;
// 2. JPEG 需要的纯净行字节数(无补齐)
int jpeg_row_stride = width * 3;
// 申请两块内存
unsigned char* bmp_raw_buf = (unsigned char*)malloc(bmp_row_stride * height);
unsigned char* jpeg_rgb_buf = (unsigned char*)malloc(jpeg_row_stride * height);
if(!bmp_raw_buf || !jpeg_rgb_buf) {
printf("内存申请失败!\n");
return 0;
}
// 严谨点:跳过可能的调色板,直接从像素偏移量开始读
fseek(bmp, fileheader.bfOffBits, SEEK_SET);
// 一口气把包含废字节的所有BMP数据读进 raw_buf
fread(bmp_raw_buf, 1, bmp_row_stride * height, bmp);
// ================= 开始“清洗”数据 =================
for (int y = 0; y < height; y++)
{
// JPEG 的行:从上到下 (0 到 height-1)
int jpeg_y = y;
// BMP 的行:从下到上 (height-1 到 0)
int bmp_y = height - 1 - y;
// 算出这两行在各自内存里的起始地址
unsigned char* jpeg_row = jpeg_rgb_buf + (jpeg_y * jpeg_row_stride);
unsigned char* bmp_row = bmp_raw_buf + (bmp_y * bmp_row_stride);
// 遍历这一行的每一个像素,做 BGR -> RGB 的转换
for (int x = 0; x < width; x++)
{
// jpeg_row 是目标,bmp_row 是源
jpeg_row[x * 3 + 0] = bmp_row[x * 3 + 2]; // Red <- 提取源的 R
jpeg_row[x * 3 + 1] = bmp_row[x * 3 + 1]; // Green <- 提取源的 G
jpeg_row[x * 3 + 2] = bmp_row[x * 3 + 0]; // Blue <- 提取源的 B
}
}
// ================= 洗完收工 =================
// 把纯净的 RGB 数据喂给 libjpeg
write_JPEG_file("destjpeg.jpeg", 90, jpeg_rgb_buf, width, height);
// 释放资源
fclose(bmp);
free(bmp_raw_buf);
free(jpeg_rgb_buf);
printf("BMP转JPEG成功!\n");
return 0;
}


