You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Libpng读取调色板图片时出现索引越界问题排查

问题排查:Libpng读取调色板图片时索引越界问题

问题背景

使用Libpng库开发PNG相关软件,读取部分调色板图片后,发现生成的二维数组存在大量调色板索引越界情况,无法定位原因。使用调试代码测试时,出现异常输出:

Index = 96
Pixel at (319, 96): Palette index out of range

调试代码

read_and_write_png函数

void read_and_write_png(const char* input_filename, const char* output_filename) {
    FILE* input_file = fopen(input_filename, "rb");
    if(!input_file) {
        printf("Can't open file %s for reading\n", input_filename);
        return;
    }

    // Initialize the reading structures.
    png_structp read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    png_infop read_info_ptr = png_create_info_struct(read_ptr);
    png_init_io(read_ptr, input_file);
    png_read_info(read_ptr, read_info_ptr);

    png_uint_32 height = png_get_image_height(read_ptr, read_info_ptr);
    png_bytepp row_pointers = (png_bytepp)malloc(sizeof(png_bytep) * height);

    for (png_uint_32 i = 0; i < height; i++) {
        row_pointers[i] = (png_bytep)malloc(png_get_rowbytes(read_ptr, read_info_ptr));
    }

    png_read_image(read_ptr, row_pointers);

    // Now that we've read the image, let's write it to another file.
    FILE* output_file = fopen(output_filename, "wb");
    if(!output_file) {
        printf("Can't open file %s for writing\n", output_filename);
        return;
    }

    // Initialize the writing structures.
    png_structp write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    png_infop write_info_ptr = png_create_info_struct(write_ptr);

    // Set up the output.
    png_init_io(write_ptr, output_file);

    // Copy the image data from the read structures to the write structures.
    png_set_IHDR(write_ptr, write_info_ptr, png_get_image_width(read_ptr, read_info_ptr),
                 png_get_image_height(read_ptr, read_info_ptr), png_get_bit_depth(read_ptr, read_info_ptr),
                 png_get_color_type(read_ptr, read_info_ptr), png_get_interlace_type(read_ptr, read_info_ptr),
                 png_get_compression_type(read_ptr, read_info_ptr), png_get_filter_type(read_ptr, read_info_ptr));

    png_colorp palette;
    int num_palette;

    if (png_get_PLTE(read_ptr, read_info_ptr, &palette, &num_palette)) {
        png_set_PLTE(write_ptr, write_info_ptr, palette, num_palette);
    }

    png_bytep trans_alpha = NULL;
    int num_trans = 0;
    png_color_16p trans_color = NULL;

    if (png_get_tRNS(read_ptr, read_info_ptr, &trans_alpha, &num_trans, &trans_color)) {
        png_set_tRNS(write_ptr, write_info_ptr, trans_alpha, num_trans, trans_color);
    }

    printf("Reading image now. . \n");
    sleep(5);
    output_pixel_values(read_ptr, read_info_ptr, row_pointers);
    printf("Done!!");
    
    // Set up the data in the write structure.
    png_set_rows(write_ptr, write_info_ptr, row_pointers);

    // Finally, write the image to the file.
    png_write_png(write_ptr, write_info_ptr, PNG_TRANSFORM_IDENTITY, NULL);

    // Clean up.
    fclose(input_file);
    fclose(output_file);

    png_destroy_read_struct(&read_ptr, &read_info_ptr, NULL);
    png_destroy_write_struct(&write_ptr, &write_info_ptr);

    // Free the memory associated with row_pointers
    for (png_uint_32 i = 0; i < height; i++) {
        free(row_pointers[i]);
    }
    free(row_pointers);
}

output_pixel_values函数

void output_pixel_values(png_structp png_ptr, png_infop info_ptr, png_bytep *row_pointers) {
    int width = png_get_image_width(png_ptr, info_ptr);
    int height = png_get_image_height(png_ptr, info_ptr);
    int color_type = png_get_color_type(png_ptr, info_ptr);
    int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
    png_colorp palette = NULL;
    int num_palette = 0;
    if (color_type == PNG_COLOR_TYPE_PALETTE) {
        png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
    }
    for (int y = 0; y < height; y++) {
        png_bytep row = row_pointers[y];
        for (int x = 0; x < width; x++) {
            if (color_type == PNG_COLOR_TYPE_RGB) {
                png_bytep px = &(row[x * 3]);
                printf("Pixel at (%d, %d): R=%d, G=%d, B=%d\n", x, y, px[0], px[1], px[2]);
            } else if (color_type == PNG_COLOR_TYPE_RGBA) {
                png_bytep px = &(row[x * 4]);
                printf("Pixel at (%d, %d): R=%d, G=%d, B=%d, A=%d\n", x, y, px[0], px[1], px[2], px[3]);
            } else if (color_type == PNG_COLOR_TYPE_GRAY) {
                int num_bytes = bit_depth == 8 ? 1 : 2;
                png_bytep px = &(row[x * num_bytes]);
                int gray_value = num_bytes == 1 ? px[0] : (px[0] << 8) + px[1];
                printf("Pixel at (%d, %d): Gray=%d\n", x, y, gray_value);
            } else if (color_type == PNG_COLOR_TYPE_PALETTE) {
                int index = row[x];
                if (index < num_palette) {
                    png_color palette_color = palette[index];
                    printf("Pixel at (%d, %d): Palette Index=%d, R=%d, G=%d, B=%d\n", x, y, index, palette_color.red, palette_color.green, palette_color.blue);

                } else {
                    printf("Index = %d\n", index);
                    printf("Pixel at (%d, %d): Palette index out of range\n", x, y);
                    row[x] = 0;
                    

                }
            }

            else {
                printf("Index = %d", row[x]);
                printf("Pixel at (%d, %d): Color type not supported\n", x, y);
            }
        }
    }
}

问题根源

代码在处理PNG_COLOR_TYPE_PALETTE类型图片时,默认认为每个像素占用1个字节(8位深度),但PNG调色板图片支持1/2/4/8位多种位深度:

  • 当位深度小于8时,多个像素的索引会被打包到同一个字节中,直接通过row[x]获取索引会读取到错误的字节值,导致索引越界。
  • 例如4位深度的图片,每个字节存储2个像素索引,row[x]取到的是包含两个索引的完整字节,而非当前x位置的单个索引。

解决方案

有两种可选修复方式:

方式1:让Libpng自动扩展位深度

在读取图片信息后,调用png_set_expand让Libpng自动将低位深度的调色板图片转换为8位,这样每个像素占用1个字节,无需手动处理打包逻辑:

// 在png_read_info之后添加
png_set_expand(read_ptr);
png_read_update_info(read_ptr, read_info_ptr);

添加后,原代码中row[x]获取索引的逻辑即可正常工作。

方式2:手动处理低位深度的像素索引解析

如果需要保留原始位深度,需根据位深度计算每个像素的实际索引:

// 替换output_pixel_values中调色板处理的代码块
else if (color_type == PNG_COLOR_TYPE_PALETTE) {
    int pixels_per_byte = 8 / bit_depth;
    int byte_idx = x / pixels_per_byte;
    int bit_shift = (8 - bit_depth) - (x % pixels_per_byte) * bit_depth;
    int index = (row[byte_idx] >> bit_shift) & ((1 << bit_depth) - 1);
    
    if (index < num_palette) {
        png_color palette_color = palette[index];
        printf("Pixel at (%d, %d): Palette Index=%d, R=%d, G=%d, B=%d\n", x, y, index, palette_color.red, palette_color.green, palette_color.blue);
    } else {
        printf("Index = %d\n", index);
        printf("Pixel at (%d, %d): Palette index out of range\n", x, y);
        // 若需修正索引,需按位深度重新计算字节并修改对应位
        // 此处示例仅输出错误,如需修改可自行实现位操作逻辑
    }
}

内容的提问来源于stack exchange,提问作者Lordmeme0123

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 05:27:05