NanoSVG库存在解析文件后修改输入字符串的bug

目录

引言

问题分析

裸机测试

Linux测试

解决方案

结语


引言

我的课程的裸机实验部分有使用NanoSVG进行SVG图片的渲染的作业。这个作业几年来一直没有问题,最近有个学生反映她的程序运行第二次的时候就不能正常显示图片,我的本能反应是她的程序有bug,不过结果分析才发现NanoSVG库存在解析文件后修改输入字符串的bug。

问题分析

裸机测试

为了方便查找原因,我在学生的代码中添加了一些打印输出,以检查程序的问题。

    // 假设串口通道号为 2
    const int SERIAL_CHANNEL = 2;
    serial_printf(SERIAL_CHANNEL, "Entering main_svg_display function with scale %f\r\n", svg_scale);
    
    struct surface_t *screen = s5pv210_screen_surface();
    serial_printf(SERIAL_CHANNEL, "Got screen surface\r\n");
    
    NSVGrasterizer *rast = nsvgCreateRasterizer();
    serial_printf(SERIAL_CHANNEL, "Created SVG rasterizer\r\n");

    NSVGimage *svg = nsvgParse((char *)logo_svg, "px", SVG_DPI);
    serial_printf(SERIAL_CHANNEL, "Parsed SVG image\r\n");
    
    if (!svg || !rast)
    {
        serial_printf(SERIAL_CHANNEL, "Error: Failed to parse SVG or create rasterizer. Exiting function.\r\n");
        return;
    }

    int width = svg->width * svg_scale;
    int height = svg->height * svg_scale;
    serial_printf(SERIAL_CHANNEL, "Calculated scaled image size: svg->width = %f, svg->height = %f width = %d, height = %d\r\n", 
        svg->width, svg->height, width, height);

    if (width < 0 || height < 0)
    {
        serial_printf(SERIAL_CHANNEL, "Error: Invalid scaled image size. Cleaning up and exiting.\r\n");
        nsvgDelete(svg);
        nsvgDeleteRasterizer(rast);
        return;
    }

运行后得到如下的结果:

由此可以看出,程序不能正确执行的原因是第二次解析之后SVG图片的宽度和高度都是0。这个结果令我感到意外,因为这个程序在内存分配和使用方面还是比较规范的,没有看到有内存覆盖的问题。

接下来,我就在解析语句前后加了打印,看看解析前后输入字符串是否发生了变化。

    // 打印 logo_svg 前 20 字节内容
    serial_printf(SERIAL_CHANNEL, "First 20 bytes of logo_svg: ");
    int n;
    for (n = 0; n < 20; n++) {
        serial_printf(SERIAL_CHANNEL, "%c", ((unsigned char *)logo_svg)[n]);
    }
    serial_printf(SERIAL_CHANNEL, "\r\n");
    
    NSVGimage *svg = nsvgParse((char *)logo_svg, "px", SVG_DPI);
    serial_printf(SERIAL_CHANNEL, "Parsed SVG image\r\n");
    
    if (!svg || !rast)
    {
        serial_printf(SERIAL_CHANNEL, "Error: Failed to parse SVG or create rasterizer. Exiting function.\r\n");
        return;
    }

    // 打印 logo_svg 前 20 字节内容
    serial_printf(SERIAL_CHANNEL, "First 20 bytes of logo_svg: ");
    for (n = 0; n < 20; n++) {
        serial_printf(SERIAL_CHANNEL, "%c", ((unsigned char *)logo_svg)[n]);
    }
    serial_printf(SERIAL_CHANNEL, "\r\n");

此次运行结果如下: 

从这个运行结果看确实nsvgParse函数在解析后对输入字符串进行了修改,导致程序第二次运行时没有了正确的SVG文件,从而导致无法解析。

Linux测试

为了进一步验证这个问题,我在Linux下对官方提供的样例Example2进行了修改,发现也会存在这个问题,下面是修改的程序:

//
// Copyright (c) 2013 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty.  In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would be
//    appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//    misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//

#include <stdio.h>
#include <string.h>
#include <float.h>
#include <stdlib.h>  // 为了使用 malloc 和 free
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
#define NANOSVG_IMPLEMENTATION
#include "nanosvg.h"
#define NANOSVGRAST_IMPLEMENTATION
#include "nanosvgrast.h"

// 读取文件内容到字符串
char* readFileToString(const char* filename) {
    FILE* file = fopen(filename, "r");
    if (file == NULL) {
        return NULL;
    }

    // 获取文件大小
    fseek(file, 0, SEEK_END);
    long size = ftell(file);
    fseek(file, 0, SEEK_SET);

    // 分配内存
    char* buffer = (char*)malloc(size + 1);
    if (buffer == NULL) {
        fclose(file);
        return NULL;
    }

    // 读取文件内容
    fread(buffer, 1, size, file);
    buffer[size] = '\0';

    fclose(file);
    return buffer;
}

int main()
{
    NSVGimage *image = NULL;
    NSVGrasterizer *rast = NULL;
    unsigned char* img = NULL;
    int w, h;
    const char* filename = "../example/23.svg";

    // 读取文件内容到字符串
    char* svgContent = readFileToString(filename);
    if (svgContent == NULL) {
        printf("Could not read SVG file.\n");
        return 1;
    }

    // 打印调用 nsvgParse 前的字符串前 50 个字符
    size_t printLen = strlen(svgContent) > 50 ? 50 : strlen(svgContent);
    printf("Before nsvgParse, first 50 characters of SVG content:\n%.50s\n", svgContent);

    printf("parsing %s\n", filename);
    image = nsvgParse(svgContent, "px", 96.0f);
    if (image == NULL) {
        printf("Could not parse SVG image.\n");
        free(svgContent);
        goto error;
    }

    // 打印调用 nsvgParse 后的字符串前 50 个字符
    printLen = strlen(svgContent) > 50 ? 50 : strlen(svgContent);
    printf("After nsvgParse, first 50 characters of SVG content:\n%.50s\n", svgContent);

    w = (int)image->width;
    h = (int)image->height;

    rast = nsvgCreateRasterizer();
    if (rast == NULL) {
        printf("Could not init rasterizer.\n");
        free(svgContent);
        goto error;
    }

    img = malloc(w*h*4);
    if (img == NULL) {
        printf("Could not alloc image buffer.\n");
        free(svgContent);
        goto error;
    }

    printf("rasterizing image %d x %d\n", w, h);
    nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4);

    printf("writing svg.png\n");
    stbi_write_png("svg.png", w, h, 4, img, w*4);

error:
    nsvgDeleteRasterizer(rast);
    nsvgDelete(image);
    free(img);
    free(svgContent);

    return 0;
}

 测试结果如下:

可以发现,确实在解析之后,输入的字符串发生了变化。

解决方案

没有时间去分析为什么NanoSVG存在bug,不过有个简单的办法可以解决这个问题,就是在解析前先备份一下输入字符串。例如:

    
    char* logo_svg_copy = strdup(logo_svg);
    if (logo_svg_copy == NULL) {
        // 处理内存分配失败的情况
        return;
    }
    // NSVGimage *svg = nsvgParse((char *)logo_svg, "px", SVG_DPI);
    NSVGimage *svg = nsvgParse((char *)logo_svg_copy, "px", SVG_DPI);
    serial_printf(SERIAL_CHANNEL, "Parsed SVG image\r\n");
    free(logo_svg_copy);
    

 结语

这个作业做了几年了,为什么以前没有学生遇到这个bug呢?因为一般都是解析文件之后就不使用原始的字符串了,而这次学生的作业中多次使用这个字符串进行解析问题就出现了。NanoSVG的作者平时是对文件进行操作的,这个问题也就不容易发现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神一样的老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值