BMP图片详解

一、介绍

1、简要介绍

BMP(Bitmap)格式,也称为位图,是一种无损的图像文件格式,也是一种与设备无关的图像文件格式,最初由微软公司为其Windows操作系统开发。

BMP格式的起源可以追溯到1980年代中期,当时微软正在开发Windows 2.x操作系统。BMP格式的设计目的是为了提供一种简单、直接的方式来存储和显示图像,同时确保图像在不同的显示设备上保持一致性。广泛用于Windows操作系统中。

它支持从1位(单色)到24位(真彩色)的多种色彩深度,主要包括以下几种:

  1. 1位色(单色)

    每个像素使用1位表示,可以表示两种颜色(通常是黑色和白色)。
  2. 4位色(16色)

    每个像素使用4位表示,可以表示16种不同的颜色。
  3. 8位色(256色)

    每个像素使用8位表示,可以表示256种不同的颜色。这种格式通常需要一个颜色表(调色板)来定义这256种颜色。
  4. 16位色(高彩色)

    每个像素使用16位表示,可以有两种常见的表示方式:
    1、5-5-5格式:5位用于红色,5位用于绿色,5位用于蓝色,剩下1位通常不使用。
    2、5-6-5格式:5位用于红色,6位用于绿色,5位用于蓝色。这是最常见的16位色格式。
  5. 24位色(真彩色)

    每个像素使用24位表示,其中8位用于红色,8位用于绿色,8位用于蓝色,就是我们常说的RGB。这种格式可以表示16,777,216种颜色,足以显示大多数自然场景和人眼能分辨的颜色,因此被称为“真彩色”。
  6. 32位色(增强型真彩色
    实际上一般情况下BMP文件是不支持32位色(我们常说的RGBA)的,然而,有一种扩展的BMP格式,称为32位BMP,它通过在每个像素的24位颜色信息之后添加额外的8位来表示透明度(alpha通道)。这种格式不是BMP标准的一部分,但在某些应用程序中得到了支持。
    在32位BMP格式中,每个像素使用32位来表示:
    1、前24位(8位红色,8位绿色,8位蓝色)用于表示颜色。
    2、最后的8位(alpha通道)用于表示透明度。
    Alpha通道的值通常表示为0到255之间的整数,其中:
    1、0表示完全透明。
    2、255表示完全不透明。

不同的色彩深度会影响文件的大小和颜色表现能力。色彩深度越高,文件越大,颜色表现越丰富。对于需要高质量图像的应用,通常会选择更高的色彩深度。然而,对于存储空间有限或需要快速加载的场合,可能会选择较低的色彩深度以减小文件大小。

还可以存储压缩或非压缩的图像数据。BMP格式简单、易于处理,但不具备高效的压缩特性(一般不压缩),因此文件通常较大。

2、BMP文件的基本结构

一个标准的BMP文件由以下几个部分组成:

  1. 位图文件头(Bitmap File Header)

    大小:14字节
    包含文件类型、文件大小、保留字段和位图数据偏移量等信息。
  2. 位图信息头(Bitmap Info Header)

    大小:通常为40字节(在较新的BMP版本中可能更大)
    包含图像的宽度、高度、色彩平面数、位深度、压缩方法、图像大小、水平和垂直分辨率等信息。
  3. 调色板(Color Palette)

    仅在位深度小于24位时存在
    包含图像使用的颜色表,每个颜色由RGB三原色组成。
  4. 像素数据(Pixel Data)

    包含实际的像素数据
    数据可以是压缩的(如RLE压缩)或非压缩的
    数据按从左下角到右上角的顺序存储,即先存储最下面一行的像素,然后是上面一行,以此类推。

下面我们详细介绍这几部分:

3、位图文件头(Bitmap File Header)

下面是位图文件头的结构体:

// BMP文件头结构体
typedef struct {
    uint16_t bfType;      // BMP文件的类型,必须是 "BM" 才表示是BMP文件
    uint32_t bfSize;      // 文件大小(包括像素数据和所有头部信息)
    uint16_t bfReserved1; // 保留字,必须设置为0
    uint16_t bfReserved2; // 保留字,必须设置为0
    uint32_t bfOffBits;   // 从文件开始到像素数据开始的字节偏移量
} BITMAPFILEHEADER;
  1. bfType (uint16_t):

    这个成员用于标识文件的类型。对于BMP文件,这个值必须是 0x4D42,即字符 "BM" 的ASCII码。用于确保文件确实是BMP格式。
  2. bfSize (uint32_t):

    这个成员指定了整个BMP文件的大小,包括所有的头部信息和像素数据,以字节为单位。这个值是文件的实际大小,可以用于计算文件的校验和或者用于内存分配。我们也可以通过二进制编辑器来查看这个位置的数据,可以获取BMP文件的大小,虽然比直接看文件的大小麻烦。
  3. bfReserved1 (uint16_t):

    这是一个保留字段,必须设置为0。在标准的BMP文件格式中,这个字段没有实际用途,但必须存在且值为0。这个字段的设置就是为了以后可能的对于位图格式的功能拓展。
  4. bfReserved2 (uint16_t):

    类似于 bfReserved1,这也是一个保留字段,必须设置为0。这个字段同样没有实际用途,但在文件格式中是必需的。这个字段的设置就是为了以后可能的对于位图格式的功能拓展。
  5. bfOffBits (uint32_t):

    这个成员指定了从文件开始到实际像素数据开始的字节偏移量。这个值告诉解析器在处理完文件头和位图信息头之后,应该跳过多少字节才能到达像素数据。这对于快速访问图像数据非常有用,因为它允许直接跳过头部信息。

这里使用一个10像素×10像素的4位位图做例子:

可以看到确实是有这些结构的,第一个结构就是bfType,它占2字节 0x4D42,即字符 "BM" 的ASCII码。用于确保文件确实是BMP格式。然后就是bfSize,这个是文件大小,以字节为单位,这里是小端字节序,所以实际数据应当是0x00 00 00 C6,十进制为198,也就是198字节,我们可以查看一下:

可以看到这里确实是198字节。然后就是两个保留字段,这里都是默认设置为0的。然后就是bfOffBits,这个就是从文件开始到实际像素数据开始的字节偏移量,由于这里是小端字节序,这里的具体数据就是0x00 00 00 76,十进制为118,我们也可以发现,实际上这里件开始到实际像素数据开始的字节偏移量就是118字节。

4、位图信息头(Bitmap Info Header)

下面是位图文件头的结构体:

// BMP信息头结构体
typedef struct {
    uint32_t biSize;          // 本结构体的大小
    int32_t  biWidth;         // 图像的宽度(像素)
    int32_t  biHeight;        // 图像的高度(像素)
    uint16_t biPlanes;        // 颜色平面数,必须设置为1
    uint16_t biBitCount;      // 每个像素的位数(1, 4, 8, 16, 24, 32)
    uint32_t biCompression;   // 使用的压缩类型
    uint32_t biSizeImage;     // 图像的大小(字节)
    int32_t  biXPelsPerMeter; // 水平分辨率(像素/米)
    int32_t  biYPelsPerMeter; // 垂直分辨率(像素/米)
    uint32_t biClrUsed;       // 在位图中使用的颜色索引数
    uint32_t biClrImportant;  // 对图像显示有重要影响的颜色索引数
} BITMAPINFOHEADER;
  1. biSize (uint32_t):

    这个成员指定了 BITMAPINFOHEADER 结构体本身的大小,以字节为单位。这个值通常是40字节,但根据BMP文件的版本和包含的信息,这个大小可能会有所不同。
  2. biWidth (int32_t):

    这个成员指定了图像的宽度,以像素为单位。这个值是图像水平方向上的像素数。
  3. biHeight (int32_t):

    这个成员指定了图像的高度,以像素为单位。这个值是图像垂直方向上的像素数。如果这个值是正数,图像将从下到上存储,即图像的底部在文件的前面。如果这个值是负数,图像将从上到下存储,即图像的顶部在文件的前面。
  4. biPlanes (uint16_t):

    这个成员指定了颜色平面数。在标准的BMP文件中,这个值必须设置为1,表示只有一个颜色平面。
  5. biBitCount (uint16_t):

    这个成员指定了每个像素的位数,决定了图像的颜色深度。可能的值包括1(单色)、4(16色)、8(256色)、16(高彩色)、24(真彩色)和32(真彩色加alpha通道)。
  6. biCompression (uint32_t):

    这个成员指定了图像数据使用的压缩类型。可能的值包括0(无压缩)、1(BI_RLE8,8位游程编码)和2(BI_RLE4,4位游程编码)。大多数真彩色图像不使用压缩。
  7. biSizeImage (uint32_t):

    这个成员指定了图像数据的大小,以字节为单位。如果图像没有被压缩,这个值可以由 biWidth 和 biBitCount 计算得出。如果使用了压缩,这个值表示解压缩后图像数据的大小。
  8. biXPelsPerMeter (int32_t):

    这个成员指定了图像的水平分辨率,以像素每米为单位。这个值可以用于显示设备,以确保图像以正确的分辨率显示。
  9. biYPelsPerMeter (int32_t):

    这个成员指定了图像的垂直分辨率,以像素每米为单位。这个值与 biXPelsPerMeter 一起使用,以确保图像以正确的分辨率显示。这两个值在打印时用于确定每个像素在纸张上的物理尺寸。具体来说,它们表示的是打印机或显示设备在水平和垂直方向上每米长度内应该打印或显示的像素数量。
  10. biClrUsed (uint32_t):

    这个成员指定了图像实际使用的颜色表中的颜色索引数。如果这个值为0,则表示使用了最大数量的颜色,即2的 biBitCount 次方。或者是16位以上的颜色深度,都设为0,表示这个字段是不被使用的。因为16位颜色深度的位图都是不使用索引的,是直接存储像素的RGB数据的。
  11. biClrImportant (uint32_t):

    这个成员指定了对图像显示有重要影响的颜色索引数。如果这个值为0,则表示所有颜色都很重要。这个值可以用于优化图像显示,特别是当颜色数有限时。

这里使用一个10像素×10像素的4位位图做例子:

首先是信息头的第一个字段,biSize,指定的是信息头的大小,这里是0x00 00 00 28,十进制为40,我们也可以发现,信息头的大小确实是40字节。然后是biWidth,图像的宽度,以像素为单位,这里依旧是上面的例子,是10像素×10像素,所以下面的biHeight和这里的biWidth都是10,这里是正确的。然后是biPlanes,颜色平面数,一般都是1,这里也是1。然后是biBitCount,颜色深度,由于这里是4位位图,所以这里是4,这里是对的。然后是biCompression,压缩类型,一般都是0,这里也是0。然后是biSizeImage,像素数据大小,这里是0x00 00 00 50,十进制80,由上面的介绍文件头的图片可知,这里的像素数据确实是80字节,然后就是biXPelsPerMeterbiYPelsPerMeter,这两个一般是不使用的,一般用于打印机,它们表示的是打印机或显示设备在水平和垂直方向上每米长度内应该打印或显示的像素数量。然后是biClrUsed就是颜色索引使用数,如果是0的话,就是所有索引都使用了。最后就是biClrImportant,表示重要的索引数,这里为0,表示所有索引都很重要。

5、调色板(Color Palette)

调色板的作用

当BMP图像的位深度小于16位时(如1位、4位、8位等),图像中的每个像素并不直接存储RGB值,而是存储一个指向调色板中颜色的索引。这样做的目的是为了减少图像文件的大小,因为直接存储索引比存储完整的RGB值要节省空间。对于16位和24位和32位图像,通常不使用颜色表,因为每个像素数据中直接存储RGB值或者对应的颜色数据。

调色板的结构

调色板通常紧跟在BITMAPINFOHEADER(或BITMAPCOREHEADER)之后。每个调色板条目通常由三个字节组成,分别代表红色、绿色和蓝色的强度。每个颜色分量的强度范围通常是0到255。

  • 1位图像:最多有2种颜色(通常是黑白)。
  • 4位图像:最多有16种颜色。
  • 8位图像:最多有256种颜色。

调色板的存储

调色板中的每个条目通常由以下结构表示:

typedef struct {
    uint8_t rgbBlue;     // 蓝色的强度(0-255)
    uint8_t rgbGreen;    // 绿色的强度(0-255)
    uint8_t rgbRed;      // 红色的强度(0-255)
    uint8_t rgbReserved; // 保留字段,通常设置为0
} RGBQUAD;

调色板的使用

在解析BMP图像时,对于位深度小于16位的图像,每个像素的值实际上是对应调色板中颜色的索引。例如,一个8位图像中的像素值为5,那么这个像素的颜色就是调色板中索引为5的颜色。

示例

假设有一个8位的BMP图像,其调色板可能如下所示:

索引0: (255, 0, 0)  // 红色
索引1: (0, 255, 0)  // 绿色
索引2: (0, 0, 255)  // 蓝色
...
索引255: (255, 255, 255)  // 白色

在这个例子中,如果图像中的某个像素值为1,那么这个像素的颜色就是绿色。

调色板的大小

对于调色板的大小,与颜色深度有关,由于每个调色板的条目都是一个四字节的结构体,所以实际的调色板大小就是颜色深度可表示的最大颜色乘上四字节:

例如8位深度,则可表示的最大颜色数为2^8,为256种颜色,所以调色板要有256个条目,则调色板大小为1024字节。实际上调色板还可以由下面的公式计算:

调色板大小 = bfOffBits - 文件头大小 - 文件信息头大小

由于 bfOffBits 表示的是文件最开始到文件真正像素信息之间的大小,所以用 bfOffBits 减去文件头大小,在减去文件信息头大小,就可已得到调色板大小。

我们在使用Windows中的画图工具时,保存图片时,可以看到这些选项:

我们可以保存后来使用十六进制查看器查看一下调色板信息:

单色位图:

因为我们知道在文件信息头的后面就是调色板,由于文件头的大小一般是14字节,文件信息头一般是40字节,然后我们直接跳过54字节来看调色板信息。

上图淡蓝色背景的8个字节的内容就是调色板,因为是单色位图,所以调色板就只有两个条目,我们知道调色板的每个条目都是存储RGB的每个分量,然后有一个保留字节,一般设为零。

调色板的第一个条目是这个,最后一个值为保留字节,为0,前面的三个值分别为RGB三色的分量,这里的三个分量都是零,众所周知,#000000对应的RGB颜色是黑色。

第二个条目是这个,众所周知,#FFFFFF对应的RGB颜色是白色。

经过分析,我们可以知道单色位图的调色板就两个条目,条目一为黑色,条目二为白色。

由于我们知道具体的文件头的结构体,我们可以找到文件头中的 bfOffBits 这一字段,就是下图中淡蓝色背景的数据,这一字段是uint32_t,是四个字节,这里是小端字节序:

所以文件最开始的位置到文件像素信息开始的位置需要的偏移量为0x00 00 00 3E,即为十进制62,然后我们可以使用上面的公式来计算调色板的大小:

调色板的大小 = 62 - 14 - 50 = 8字节,这与我们上面分析的是一致的,上面我们说调色板有两个条目,每一个条目都是4字节,所以总共为8字节。

16色位图(即为4位颜色深度,2^4为16,可以表示16中颜色):

实际上,这里的位图与上面的位图像素大小是一样的,为什么这里的看起来更大,就是因为这里的调色板数据更多。实际上后面的像素数据也有些差异,我们在下面会讲解。

下面是对上面的调色板的总结:

对于16色图,它的调色板有16个条目,每个条目是4字节,总计64字节。上面的只能作为一个例子,实际上的调色板的内容取决于图像的实际颜色需求。

在这里我们也通过 bfOffBits 来计算一下调色板的大小:

这里的淡蓝色背景的数据就是 bfOffBits ,由于是小端字节序,实际数据为0x00 00 00 76,十进制为118,然后使用公式:

调色板的大小 = 118 - 14 - 50 = 64字节,这与我们分析的相同。

256色位图(即为8位深度位图):

对于8位深度位图,它的调色板有256个条目,每个条目都是4字节,所以调色板大小就是1024字节。这里不便截图,你们可以自己在画图工具中保存为8位位图(256色位图),然后使用十六进制查看器来查看。

在这里我们也通过 bfOffBits 来计算一下调色板的大小:

由于256色位图数据较多,我只截出了文件头和信息头,以及调色板的一部分,这里的淡蓝色背景的数据就是 bfOffBits ,由于是小端字节序,实际数据为0x00 00 04 36,十进制为1078,然后使用公式:

调色板的大小 = 1078 - 14 - 50 = 1024字节,这与我们分析的相同。

6、像素数据(Pixel Data)

一个位图的主要数据就在这里,前面的部分结构就是为了可以正确地读取这一部分数据。这里我们依旧使用上面的4位位图作为例子:

像素数据的结构

像素数据的结构取决于位图的深度(即每个像素使用的位数)。对于4位深度的位图,每个像素使用4位来表示,这意味着每个像素可以表示16种颜色之一(因为2^4 = 16)。这些颜色由调色板定义,每个像素的值实际上是对应调色板中颜色的索引。

存储方式

像素数据通常以字节为单位进行存储,这意味着对于4位深度的位图,每个字节存储两个像素的信息。具体来说,一个字节的高4位表示第一个像素的颜色索引,低4位表示第二个像素的颜色索引。

字节对齐

为了提高访问效率,位图数据通常会进行字节对齐。这意味着即使实际的像素数据可能不是字节对齐的,位图文件也会填充额外的位或字节,以确保数据按字节边界对齐。例如,如果图像的宽度不是字节对齐的,文件可能会在每行的末尾添加额外的填充字节。就例如,我们这里的例子是10像素×10像素,由于是4位颜色深度,所以每一个像素都是4bit的数据,这里的宽度是10像素,所以这里一行像素也就是10个像素,所以一行的数据就是10*4 = 40bit,也就是5字节。由于对齐字节为4字节,所以每行都应当是4字节的倍数,所以每行要填充3字节,填充的内容一般是0。由于高度为10像素,所以也就是10行,所以一共填充30字节。所以本来的没有填充的单纯像素数据的大小是400bit,也就是50字节,然后填充了30字节,总共像素数据位80字节。对于上面的例子,我们可以看下图:

可以看到这里的biSizeImage中存储的数据位0x00 00 00 50,十进制为80,也就是说这里的像素数据大小为80字节,与我们分析的是相同的。

具体对齐技术

  1. 计算每行所需的字节数:首先,根据位图的宽度(像素数)和每个像素的位深度计算每行所需的字节数。例如,对于4位深度的位图,每个字节可以存储2个像素(因为4位 = 0.5字节)。

  2. 确定对齐要求:如果计算出的每行字节数不是4的倍数,就需要进行填充。填充的目的是使得每行的字节数达到4的倍数。

  3. 填充字节:填充通常在每行的末尾进行。填充的字节内容通常是0,但理论上可以是任何值。在BMP文件中,填充字节通常不会影响图像的视觉内容,因为它们位于有效像素数据的后面。

示例

假设有一个4位深度的位图,宽度为5像素。每个字节存储2个像素,因此每行需要3个字节(5像素 / 2像素/字节 = 2.5字节,向上取整到3字节)。由于3字节不是4的倍数,需要填充1字节以满足4字节对齐的要求。

例子

我们接着使用上面的10像素×10像素的4位位图做例子:

由于我举的这个例子其实所有的像素都是纯白色的,所以我们可以发现实际上这里的像素数据内容每一行都是一样的,我们上面分析的就是一行8字节,总共10行,可以发现这里是没问题的。然后我们对内容进行具体的分析,由于每一行的像素数据都是一样的,所以我们只用分析一行的可以看到一行数据是这样的:

我们在上面分析的是一个像素使用4bit,一个像素的数据是4bit,由于一行是10像素,一行像素数据使用10*4 = bit,也就是5字节,在行最后填充3字节,达到4字节的倍数。所以我们可以发现这里的前5字节是有效的数据,后3字节是填充,因为填充的是0。

这样就很清晰了,所以说这里的每一个像素的数据都是0xF,由于4位位图像素数据中存储的是调色板索引,所以这里的0xF是索引,即索引为15,对应上面例子的图,索引15对应的是白色:

这更加证实了我们的分析。所以说这个图就是一行10个白色像素,总共有10行。

二、实例

1、介绍

我们可以直接使用Windows电脑自带的画图导出位图实例。

可以看到这里是有BMP图片导出选项的。

我们可以将图片的尺寸调小一些,以便我们分析:

然后将图片放大些,以便我们作画:

然后调整画笔粗细:

然后我们可以调整颜色,这里可以直接调整RGB颜色:

然后就可以在刚刚我们创建的画布上面作画了。

2、示例

下面是我创作的一个BMP图像,这里稍微放大了些,实际上的尺寸是20像素×20像素:(下面两个图像中,左为直接使用导出的BMP图片上传至此,然后在浏览器放大。右为在创作过程中使用画图工具放大到最大倍数,然后截图粘贴在此,并未经过在浏览器中放大)

它由25个色块构成,每个色块都是4像素×4像素,颜色以十六进制RGB代码来表示从左向右从上至下依次为:

我们发现上面的两个图片有所不同,左图是直接使用BMP格式上传至浏览器,然后在浏览器放大。右图是在创作过程中使用Windows画图工具放大到最大倍数,然后截图粘贴在此。但是我们发现左图的色块边缘是模糊的,而右图的色块边缘是清晰的,这是为什么呢?

左图(BMP格式上传至浏览器并放大):

将BMP格式的图片上传至浏览器并放大时,浏览器会使用自己的图像渲染引擎来处理图像。

浏览器在放大图像时通常会使用插值算法(如双线性插值或双立方插值)来估计新像素的颜色值。

这些插值算法旨在平滑颜色过渡,以减少放大后的像素化效果,但同时也会导致边缘变得模糊。

右图(在Windows画图工具中放大到最大倍数后截图):

在Windows画图工具中,放大图像时,画图工具使用的是最近邻插值方法,这种方法简单地重复原始像素,而不是混合颜色。

因此,每个色块的边缘保持清晰,因为每个像素只是被重复了多次,而没有进行颜色值的插值。

在画图工具中放大图像到最大倍数后截图,截图捕捉的是画图工具显示的图像,而不是经过浏览器渲染的图像。

  • 23
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在nginx进行动静分离配置的详解如下: 首先,在html/index.html文件,可以通过添加一个图片来标注backend的IP地址。例如,可以使用以下代码添加一个图片: ``` <img src="/my.gif" height="100" width="100"/> <p>Thank you for using nginx 172.16.225.110.</p> ``` 这样,当访问该页面时,会显示这个图片和相应的文字信息。\[1\] 接下来,在nginx的配置,可以创建一个专门用来配置静态资源路径的主机。这样做的好处是,当需要更改静态资源的目录时,只需修改主机路径即可,而不需要在多个地方进行修改。可以使用以下配置将图片的路径修改为静态资源路径: ``` location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|ico|svg)$ { proxy_pass http://localhost:81; } ``` 这样,当访问以.gif、.jpg、.jpeg、.png、.bmp、.swf、.ico或.svg结尾的URL时,nginx会将请求转发到本地的81端口,实现静态资源的访问。\[2\] 总结来说,nginx作为一种轻量级、高性能、多进程的Web服务器,非常适合作为静态资源的服务器使用。通过动静分离的配置,可以将静态资源的访问交给nginx处理,而动态的访问操作可以使用其他稳定的服务器来实现,例如Apache、Tomcat或IIS。\[3\] #### 引用[.reference_title] - *1* [Nginx 动静分离相关配置解析](https://blog.csdn.net/qq_34285557/article/details/128259079)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [nginx实现动静分离--附nginx配置文件详解](https://blog.csdn.net/weixin_30337157/article/details/97202469)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Nginx 动静分离配置详解](https://blog.csdn.net/qq_45859670/article/details/123166567)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值