我编写这段代码是为了熟悉C中的结构,它允许创建和导出24位TGA图像,并更改单个像素的颜色。
我关心的是setPixel函数,以及如何改进它的性能。
首先,从x,y到数组索引的转换,我如何最小化/消除这方面的需要?我想我可以用一个二维数组。
第二,像素的RGB值的设置。如何才能对其进行优化?一次为所有三个人分配一项任务会改善这一状况吗?我该怎么做?
最后,我想知道是否有办法减少makeImage和saveImage的冗长性。我没能找到更好的解决办法。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define HEADER_BYTES 18 // Ammount of bytes header should take up
typedef struct TGAImg_t{
uint8_t idLength; // Length of image ID field (0-255)
uint8_t colorMapType; // If a color map is present (1) or not (0)
uint8_t imageType; // Compression and color types (0-3 and 9-11)
uint16_t colorMapOrigin; // Index of first color map entry
uint16_t colorMapLength; // Count of color map entries
uint8_t colorMapEntrySize; // Number of bits in each color map entry. 16 for Targa 16, 24 for Targa 24...
uint16_t xOrigin; // X coord of the lower left corner of the image
uint16_t yOrigin; // Y coord of the lower left corner of the image
uint16_t width; // Width of the image in pixels
uint16_t height; // Height of the image in pixels
uint8_t imagePixelSize; // Number of bits in a stored pixel index
uint8_t imageDescriptorByte;// Document says to just keep this byte as 0
uint8_t imageDataField[0]; // Array of image pixels.
} TGAImg;
typedef struct RGB_t{
uint8_t red;
uint8_t green;
uint8_t blue;
} RGB;
TGAImg* makeImage(uint8_t idLength, uint8_t colorMapType,
uint8_t imageType, uint16_t colorMapOrigin,
uint16_t colorMapLength, uint8_t colorMapEntrySize,
uint16_t xOrigin, uint16_t yOrigin,
uint16_t width, uint16_t height,
uint8_t imagePixelSize, uint8_t imageDescriptorByte){
// Allocate memory for header + image pixels (using info from header params)
uint32_t dataFieldBytes = width * height * (imagePixelSize / 8);
TGAImg* img = calloc(HEADER_BYTES + dataFieldBytes, 1 );
if (!img){
printf("ERROR: calloc fail for img @ makeImg");
exit(EXIT_FAILURE);
}
// Set header values
img->idLength = idLength;
img->colorMapType = colorMapType;
img->imageType = imageType;
img->colorMapOrigin = colorMapOrigin;
img->colorMapLength = colorMapLength;
img->colorMapEntrySize = colorMapEntrySize;
img->xOrigin = xOrigin;
img->yOrigin = yOrigin;
img->width = width;
img->height = height;
img->imagePixelSize = imagePixelSize;
img->imageDescriptorByte = imageDescriptorByte;
return img;
}
// Writes contents of a TGAImg struct to a image of name imageName
void saveImage(char imageName[], TGAImg* img){
FILE* imageFile = fopen(imageName, "wb");
fwrite(&img->idLength, sizeof(img->idLength), 1, imageFile);
fwrite(&img->colorMapType, sizeof(img->colorMapType), 1, imageFile);
fwrite(&img->imageType, sizeof(img->imageType), 1, imageFile);
fwrite(&img->colorMapOrigin, sizeof(img->colorMapOrigin), 1, imageFile);
fwrite(&img->colorMapLength, sizeof(img->colorMapLength), 1, imageFile);
fwrite(&img->colorMapEntrySize, sizeof(img->colorMapEntrySize), 1, imageFile);
fwrite(&img->xOrigin, sizeof(img->xOrigin), 1, imageFile);
fwrite(&img->yOrigin, sizeof(img->yOrigin), 1, imageFile);
fwrite(&img->width, sizeof(img->width), 1, imageFile);
fwrite(&img->height, sizeof(img->height), 1, imageFile);
fwrite(&img->imagePixelSize, sizeof(img->imagePixelSize), 1, imageFile);
fwrite(&img->imageDescriptorByte, sizeof(img->imageDescriptorByte), 1, imageFile);
fwrite(&img->imageDataField, img->width * img->height * (img->imagePixelSize / 8), 1, imageFile);
fclose(imageFile);
}
void setPixel(TGAImg* img, RGB color, int x, int y){
uint32_t index = ((y * img->width) + x) * 3; // Convert x and y to index for array
// Apply pixel change to all colors
img->imageDataField[index] = color.blue;
img->imageDataField[index+1] = color.green;
img->imageDataField[index+2] = color.red;
}发布于 2022-03-22 11:22:51
性能
除了慢setPixel()之外,还提供了一个setLine(),在设置多个相同颜色的像素时提供了减少重复计算的功能。
除了慢setPixel()之外,还提供了一个copyBlock(),在设置像素矩形时提供了减少重复计算的功能。在可能的情况下使用memcpy()。
便携性
TGAImg是包含特定于实现的填充的应用程序,TARGA文件有一个固定的(可能没有填充)定义。
如果允许使用像packed这样的扩展语言限定符,这在C代码中很容易实现。
// typedef struct TGAImg_t{
typedef struct packed TGAImg_t{ // example如果没有这样的语言扩展,那么编写可移植代码就是一项工作。在大多数情况下,代码需要以字节数组的形式读取文件头,然后一次形成一个TGAImg成员--就像OP编写saveImage()时所做的那样。
字节顺序可能与代码从文件格式不同。存在着各种endian函数。
// fwrite(&img->colorMapOrigin, sizeof(img->colorMapOrigin), 1, imageFile);
uint16_t u16 = endian_host_to_file16(img->colorMapOrigin);
fwrite(&u16, sizeof u16, 1, imageFile);int可能是16位。各种代码都需要对此进行解释。
// void setPixel(TGAImg* img, RGB color, int x, int y){
void setPixel(TGAImg* img, RGB color, int_fast32_t, x, int_fast32_t y){最后,我想知道是否有办法减少makeImage和saveImage的冗长性。我没能找到更好的解决办法。
不清楚为什么代码不简单使用,让调用者填写大多数成员。
TGAImg *makeImage(const TGAImg *init_values)当imagePixelSize不是8.像素深度15 允许的的倍数时会有麻烦。
// uint32_t dataFieldBytes = width * height * (imagePixelSize / 8);
// Possible solution - needs review.
uint32_t dataFieldBytes = width * height * ((imagePixelSize + 7) / 8);发布于 2022-03-21 23:41:43
与其对结构的“头”应该占用的字节数进行硬编码,您只需使用sizeof()来获得它。当应用于结构时,sizeof()将返回其大小,就好像零长度数组不存在一样。
零长度数组是一个过时的GCC扩展。相反,您应该使用灵活的数组成员。您可以通过省略括号之间的零来实现这一点。
由于您typedef您的结构,您不需要给他们的名字。你可以把他们排除在外,像这样:
typedef struct {
...
} TGAImg;https://codereview.stackexchange.com/questions/275149
复制相似问题