TIFF图像文件格式详解

TIFF是一种标记语言,广泛应用于数字影响、遥感、医学等领域。文件结构包括图像文件头(IFH)、图像文件目录(IFD)和目录项(DE)。TIFF文件可能包含多个IFD,每个IFD对应一个子文件。本文详细介绍了TIFF的结构和数据类型,并提及了坐标系转换的相关处理。
摘要由CSDN通过智能技术生成

1 什么是TIFF?
TIFF是Tagged Image File Format的缩写。在现在的标准中,只有TIFF存在, 其他的提法已经舍弃不用了。做为一种标记语言,TIFF与其他文件格式最大的不同在于除了图像数据,它还可以记录很多图像的其他信息。它记录图像数据的方式也比较灵活, 理论上来说, 任何其他的图像格式都能为TIFF所用, 嵌入到TIFF里面。比如JPEG, Lossless JPEG, JPEG2000和任意数据宽度的原始无压缩数据都可以方便的嵌入到TIFF中去。由于它的可扩展性, TIFF在数字影响、遥感、医学等领域中得到了广泛的应用。TIFF文件的后缀是.tif或者.tiff

2 TIFF文件结构 
TIFF文件中的三个关键词是:图像文件头Image File Header(IFH), 图像文件目录Image File Directory(IFD)和目录项Directory Entry(DE)。每一幅图像是以8字节的IFH开始的, 这个IFH指向了第一个IFD。IFD包含了图像的各种信息, 同时也包含了一个指向实际图像数据的指针。
IFH的构成:
Byte 0-1: 字节顺序标志位, 值为II或者MM。II表示小字节在前, 又称为little-endian。MM表示大字节在前,又成为big-endian。
Byte 2-3: TIFF的标志位,一般都是42
Byte 4-7: 第一个IFD的偏移量。可以在任意位置, 但必须是在一个字的边界,也就是说必须是2的整数倍。
IFD的构成(0代表此IFD的起始位置):
Byte 0-1: 表示此IFD包含了多少个DE,假设数目为n
Byte 2-(n*12+1): n个DE
Byte (n*12+2)-(n*12+5): 下一个IFD的偏移量,如果没有则置为0
DE的构成:
Byte 0-1: 此TAG的唯一标识
Byte 2-3: 数据类型。
Byte 4-7: 数量。通过类型和数量可以确定存储此TAG的数据需要占据的字节数
Byte 8-11: 如果占用的字节数少于4, 则数据直接存于此。 如果超过4个,则这里存放的是指向实际数据的指针

可以用以下的图来表示(图来自http://www.cppblog.com/windcsn/archive/2009/03/12/1158.html)

在TIFF6.0中,定义了12种数据类型,分别是:

1 = BYTE 8-bit unsigned integer.
2 = ASCII 8-bit byte that contains a 7-bit ASCII code; the last byte
must be NUL (binary zero).
3 = SHORT 16-bit (2-byte) unsigned integer.
4 = LONG 32-bit (4-byte) unsigned integer.
5 = RATIONAL Two LONGs: the first represents the numerator
6 = SBYTE An 8-bit signed (twos-complement) integer.
7 = UNDEFINED An 8-bit byte that may contain anything, depending on
the definition of the field.
8 = SSHORT A 16-bit (2-byte) signed (twos-complement) integer.
9 = SLONG A 32-bit (4-byte) signed (twos-complement) integer.
10 = SRATIONAL Two SLONG’s: the first represents the numerator of a
fraction, the second the denominator.
11 = FLOAT Single precision (4-byte) IEEE format.
12 = DOUBLE Double precision (8-byte) IEEE format.

-个TIFF文件可能包含多个IFD,每一个IFD都是一个子文件。Baseline解码器只要求解第一个IFD所对应的图像数据。扩展的TIFF图像经常包含多个IFD,每一个IFD都包含了不同的信息。

TIF图一般由三个部分组成:文件头(简称IFH)、文件目录(简称IFD)、图像数据。
一、图像文件头(Image File Header)
  IFH数据结构包含3个成员共计8个字节(见表一):
表一 IFH结构描述
------------------------------------------------------------
名称        字节数 数据类型 说明
------------------------------------------------------------
Byteorder       2  Integer  TIF标记,其值为4D4D或4949
Version        2  Integer  版本号,其值恒为2A00
Offset to first IFD  4  Long    第一个IFD的偏移量
------------------------------------------------------------
表一说明
  1.Byteorder:可能是H4D4D或H4949,H4D4D表示该图是摩托罗拉整数格式,H4949表示该图是Intel整数格式。
  2.Version:总是H2A00,它可能是tif文件的版本,也可能用于进一步校验该文件是否为TIF格式。
  3.Offset to first IFD:第一个IFD相对文件开始处的偏移量(因为可能会有多个顺序排列的IFD)。
  IFD数据结构并不一定紧跟在IFH后面,相反,它常常位于第三部分图像数据的后面,即TIF图像文件的一般组织形式是:IFH——图像数据——IFD。
  二、图像文件目录(Image File Directory)
  IFD是TIF图像文件中重要的数据结构,它包含了三个成员。由于一个TIF文件中可以有多个图像,而一个IFD只标识一个图像的所有属性(有的文章把“属性”称之为“标签”),所以,一个TIF文件中有几个图像,就会有几个IFD。IFD的结构见表二:
表二 IFD结构描述
-----------------------------------------------------------------
名称         字节数 数据类型 说明
-----------------------------------------------------------------
Directory Entry Count  2  Integer  本IFD中DE的数量
Directory Entry(1)   12       简称DE,中文译义“目录项”
Directory Entry(2)   12
……
Directory Entry(N)   12
Offset to next IFD   4  Long    下一个IFD的偏移量
-----------------------------------------------------------------
表二说明
  1.Directory Entry Count:指出在该IFD中DE的个数;
  2.Directory Entry:共12个字节,结构见表三。需要指出的是,DE的个数是不定的,因为每个DE只标识了图像的一个属性,那么这幅图像有N个属性就会有N个DE,用户甚至可添加自定义的标记属性,这就是为什么称TIF格式文件为“可扩充标记的文件”的原因。
  3.Offset to next IFD Or NULL:下一个IFD相对于文件开始处的位置,这是一个链式构成。如果该数字为0,表示已经是最后一个IFD。当然,如果该TIF文件只包含了一幅图像,那么就只有一个IFD,显然这个偏移量也会等于0。
表三 DE结构描述
--------------------------------------------------
名称     字节数  数据类型 说明
--------------------------------------------------
tag       2   Integer  本属性的标签编号
type       2   Integer  本属性值的数据类型
length      4   Long    该类型数据的数量
valueOffset   4   Long    属性值的存放偏移量
--------------------------------------------------
表三说明
  由DE标识的图像属性有:图像的大小、分辨率、是否压缩、像素的行列数、颜色深度(单色、16色、256色、真彩色)等等。其中:
  ①tag:是该属性的标签编号(TagID),在图像文件目录中,它是按照升序排列的(但不一定是连续的)。这些编号在TIF格式官方白皮书中可以查到相应的含义,但遗憾的是,我们到哪儿可以找到官方白皮书呢?所以,笔者只能把网上能找得到资料(再结合自己的实验结果)罗列出来,见表四。
  ②type:表示该属性数据的类型,一般认为TIF官方指定的有5种数据类型(但也有说12种数据类型的)。见表五。
  ③length:该种类型的数据的个数,而不是某个数据的长度。
  ④valueOffset:是tagID代表的变量值相对文件开始处的偏移量,但如果变量值占用的空间不多于4个字节(例如只有1个Integer类型的值),那么该值就直接存放在valueOffset中,没必要再另外指向一个地方了。
表四 DE中标签编号的含义
-------------------------------------------------------------------------
TagID 属性名称 type 说明
-------------------------------------------------------------------------
0100 图像宽       0003
0101 图像高       0003
0102 颜色深度      0003  值=1为单色,=4为16色,=8为256色。
                 如果该类型数据个数>2个,说明是真彩图像
0103 图像数据是否压缩  0003  值=05表示压缩
0106 图像是否采用反色显示0003  值=01表示反色,否则表示不反色
0111 图像扫描线偏移量  0004  图像数据起始字节相对于文件开始处的位置
0116 图像扫描线的数量  0004  表示图像有几行扫描线,实际上等于图像高度
0117 图像数据字节总数  0003  如果不是偶数,那么实际存放时会在后面加0
011A 水平分辩率偏移量  0005  常用计量单位是:像素/英寸
011B 垂直分辩率 偏移量  0005  常用计量单位是:像素/英寸
0131 生成该图像的软件名 0002  文本类型
0132 生成该图像的时间  0002  文本类型
0140 调色板偏移量    0003  256色和16色图像才有此属性,而且有连续2个
                 调色板,但属性的length值只表示出1个调色板
-------------------------------------------------------------------------
表四说明
  ①“水平(垂直)分辩率”是分数型的属性,其值要占用8个字节,所以在valueOffset中存放的肯定是它的具体数值的偏移量,而不是数值本身。
  ②“生成图像的软件名称”和“生成图像的时间”这两个字符型属性,它们的值所占用的空间也会大于4字节,所以在valueOffset中存放的也是它们的值的偏移量,而不是值本身。
  ③“图像数据字节总数”一般是个偶数,如果是奇数,那么实际存放时会在后面加一个0,但这个0不会计算在字节总数之内。
表五 DE中的数据类型
--------------------------------------------------------------------
type值 数据类型  说明
--------------------------------------------------------------------
0001  Byte
0002  Ascii   文本类型,7位Ascii码加1位二进制0
0003  Integer
0004  Long
0005  RATIONAL  分数类型,由两个Long组成,第1个是分子,第2个是分母
--------------------------------------------------------------------
  三、图像数据。这些数据可能是压缩的,也可能是未压缩的。如果经过压缩,那么压缩算法又有许多种,所以,图像数据是TIF文件中最为复杂的部分,暂还没有哪个软件能译出所有的压缩算法。

 

TIFF图片可以存储坐标信息,但是如何对这些坐标信息进行转换,然后生成新的图片呢?首先需要知道现在TIFF图片中的坐标系,然后要知道被转换的坐标系。现在我们只支持WGS84、西安80、北京54等中国常用的坐标系。
坐标系转换的头文件:

头文件

#pragma once
#include "proj_api.h"

#define PROJ4_COUNT 1024
#define coord_wgs84 "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"
#define coord_google "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"
/*
北京54 +proj=tmerc +a=6378245.000000 +b=6356863.018800 +x_0=39500000.000000 +y_0=0.000000 +lat_0=0.000000 +lon_0=117.000000 +units=m +k=1.000000  +towgs84=0,-0,0,0,0,0,0
西安80 +proj=tmerc +a=6378140.000000 +b=6356755.288158 +x_0=39500000.000000 +y_0=0.000000 +lat_0=0.000000 +lon_0=117.000000 +units=m +k=1.000000  +towgs84=0,-0,0,0,0,0,0
国家2000 +proj=tmerc +a=6378137.000000 +b=6356752.314140 +x_0=39500000.000000 +y_0=0.000000 +lat_0=0.000000 +lon_0=117.000000 +units=m +k=1.000000  +towgs84=0,-0,0,0,0,0,0
*/


class CoordTrans
{
public:
    CoordTrans();
public:

    void define_coord_sys( const int coord_sys );
    void define_ellps( const int ellps );
    void define_proj_zone( const int maj_zone );
    void define_7_trans( double dx , double dy , double dz , double rx , double ry , double rz , double k );
    void trans_2_des( double&left , double&top , double&right , double&bottom );
    void trans_2_des( double&left , double&top );

    void trans_back_src( double&left , double&top );
public:

    bool init_proj( const char* src_ ,const char* des_ = coord_google );

    void xy_2_latlong( double&x , double&y );
    void xy_2_xy( double&x , double&y );
    void longlat_2_xy( double&x , double&y );

private:
    void deinit();
private:
    void* _source;
    void* _des;

    char* _sz_source;
    char* _sz_des;

private:
    int _coord_sys;
    int _ellps;
    int _maj_zone;

    double _dx;
    double _dy;
    double _dz;
    double _rx;
    double _ry;
    double _rz;
    double _k;
};

坐标转换cpp文件

#include "coord_trans.h"
#include <string.h>
#include <stdio.h>
#include "windows.h"
#include <string>
using namespace std;

struct _ellps_type_data_
{
    TCHAR* name;
    double a;
    double b;
};

const _ellps_type_data_ ellps_list[]=
{
    {L"WGS84"   , 6378137.0 , 6356752.3142 },
    {L"西安80"    , 6378140.0 , 6356755.288157528},
    {L"北京54"    , 6378245.0 , 6356863.0188 },
    {L"国家2000", 6378137.0 , 6356752.31414},
    {L"web墨卡托",6378137.0 , 6378137.0},
    {NULL           , 0         , 0 }
};

typedef struct _proj_type_data_
{
    TCHAR* name;
    int zome_number;
    double pl;
    double pb;
    double pfe;
    double pfn;
    double pk;
} ProjData;

ProjData theProj[]=
{
    {L"(6度带)13-中央经线 75"   ,13,75,0,13500000,0,1},
    {L"(6度带)14-中央经线 81"   ,14,81,0,14500000,0,1},
    {L"(6度带)15-中央经线 87"   ,15,87,0,15500000,0,1},
    {L"(6度带)16-中央经线 93"   ,16,93,0,16500000,0,1},
    {L"(6度带)17-中央经线 99" ,17,99,0,17500000,0,1},
    {L"(6度带)18-中央经线 105"    ,18,105,0,18500000,0,1},
    {L"(6度带)19-中央经线 111"    ,19,111,0,19500000,0,1},
    {L"(6度带)20-中央经线 117"    ,20,117,0,20500000,0,1},
    {L"(6度带)21-中央经线 123"    ,21,123,0,21500000,0,1},
    {L"(6度带)22-中央经线 129"    ,22,129,0,22500000,0,1},
    {L"(6度带)23-中央经线 135"    ,23,135,0,23500000,0,1},

    {L"(3度带)25-中央经线 75" ,25,75,0,25500000,0,1},
    {L"(3度带)26-中央经线 78" ,26,78,0,26500000,0,1},
    {L"(3度带)27-中央经线 81" ,27,81,0,27500000,0,1},
    {L"(3度带)28-中央经线 84" ,28,84,0,28500000,0,1},
    {L"(3度带)29-中央经线 87" ,29,87,0,29500000,0,1},
    {L"(3度带)30-中央经线 90" ,30,90,0,30500000,0,1},
    {L"(3度带)31-中央经线 93" ,31,93,0,31500000,0,1},
    {L"(3度带)32-中央经线 96" ,32,96,0,32500000,0,1},
    {L"(3度带)33-中央经线 99" ,33,99,0,33500000,0,1},
    {L"(3度带)34-中央经线 102"    ,34,102,0,34500000,0,1},
    {L"(3度带)35-中央经线 105"    ,35,105,0,35500000,0,1},
    {L"(3度带)36-中央经线 108"    ,36,108,0,36500000,0,1},
    {L"(3度带)37-中央经线 111"    ,37,111,0,37500000,0,1},
    {L"(3度带)38-中央经线 114"    ,38,114,0,38500000,0,1},
    {L"(3度带)39-中央经线 117"    ,39,117,0,39500000,0,1},
    {L"(3度带)40-中央经线 120"    ,40,120,0,40500000,0,1},
    {L"(3度带)41-中央经线 123"    ,41,123,0,41500000,0,1},
    {L"(3度带)42-中央经线 126"    ,42,126,0,42500000,0,1},
    {L"(3度带)43-中央经线 129"    ,43,129,0,43500000,0,1},
    {L"(3度带)44-中央经线 132"    ,44,132,0,44500000,0,1},
    {L"(3度带)45-中央经线 135"    ,45,135,0,45500000,0,1},
    /*{L"自定义",39,117,0,500000,0,1},*/
    {NULL   ,0,0,0,0,1}
};

CoordTrans::CoordTrans()
{
    _source = NULL;
    _des = NULL;

    _sz_source = new char[PROJ4_COUNT];
    memset( _sz_source , 0 , PROJ4_COUNT );

    _sz_des = new char[PROJ4_COUNT];
    memset( _sz_des , 0 , PROJ4_COUNT );
}

void CoordTrans::define_coord_sys( const int coord_sys )
{
    _coord_sys = coord_sys;

}

void CoordTrans::define_ellps( const int ellps )
{
    _ellps = ellps;
}

void CoordTrans::define_proj_zone( const int maj_zone )
{
    _maj_zone = maj_zone;
}
void CoordTrans::define_7_trans( double dx , double dy , double dz , double rx , double ry , double rz , double k )
{
    _dx = dx;
    _dy = -dy;
    _dz = dz;
    _rx = rx;
    _ry = ry;
    _rz = rz;
    _k = k;
}

void CoordTrans::trans_2_des( double&left , double&top , double&right , double&bottom )
{
    double left2 = left;
    double right2 = right;
    double top2 = top;
    double bottom2 = bottom;

    string proj ;
    if ( _coord_sys == 0 )
    {
        proj = "+proj=longlat ";
    }
    else if ( _coord_sys == 1 )
    {
        proj = "+proj=tmerc ";
    }

    //椭球体
    char ch_ellps[128];
    memset( ch_ellps , 0 , 128 );
    sprintf( ch_ellps , "+a=%f +b=%f " , ellps_list[_ellps].a , ellps_list[_ellps].b );
    string ellps( ch_ellps );

    //投影带
    char ch_zone[512];
    memset( ch_zone , 0 , 512 );
    sprintf ( ch_zone ,"+x_0=%f +y_0=0.000000 +lat_0=0.000000 +lon_0=%f +units=m +k=1.000000 "
        , theProj[_maj_zone].pfe , theProj[_maj_zone].pl );
    string zone( ch_zone );

    //towgs84
    char ch_wgs84[128];
    memset( ch_wgs84 , 0 , 128 );
    sprintf( ch_wgs84 , "+towgs84=%f,%f,%f,%f,%f,%f,%f" , _dx , _dy , _dz , _rx , _ry , _rz , _k );
    string towgs84( ch_wgs84 );

    sprintf ( _sz_source ,"%s%s%s%s" , proj.c_str() , ellps.c_str() , zone.c_str() , towgs84.c_str() );


    init_proj( _sz_source );

    if ( _coord_sys == 0 )
    {
        longlat_2_xy( left , top );
        longlat_2_xy( right , bottom );
    }
    else
    {
        xy_2_xy( left , top );
        xy_2_xy( right , bottom );
    }


    /*init_proj( _sz_source , coord_wgs84 );
    xy_2_latlong( left2 , top2 );
    xy_2_latlong( right2 , bottom2 );
    init_proj( coord_wgs84 , coord_google );
    longlat_2_xy( left2 , top2 );
    longlat_2_xy( right2 , bottom2  );*/
}

void CoordTrans::trans_2_des( double&left , double&top )
{
    double left2 = left;
    double top2 = top;

    string proj ;
    if ( _coord_sys == 0 )
    {
        proj = "+proj=longlat ";
    }
    else if ( _coord_sys == 1 )
    {
        proj = "+proj=tmerc ";
    }

    //椭球体
    char ch_ellps[128];
    memset( ch_ellps , 0 , 128 );
    sprintf( ch_ellps , "+a=%f +b=%f " , ellps_list[_ellps].a , ellps_list[_ellps].b );
    string ellps( ch_ellps );

    //投影带
    char ch_zone[512];
    memset( ch_zone , 0 , 512 );
    sprintf ( ch_zone ,"+x_0=%f +y_0=0.000000 +lat_0=0.000000 +lon_0=%f +units=m +k=1.000000 "
        , theProj[_maj_zone].pfe , theProj[_maj_zone].pl );
    string zone( ch_zone );

    //towgs84
    char ch_wgs84[128];
    memset( ch_wgs84 , 0 , 128 );
    sprintf( ch_wgs84 , "+towgs84=%f,%f,%f,%f,%f,%f,%f" , _dx , _dy , _dz , _rx , _ry , _rz , _k );
    string towgs84( ch_wgs84 );

    sprintf ( _sz_source ,"%s%s%s%s" , proj.c_str() , ellps.c_str() , zone.c_str() , towgs84.c_str() );


    init_proj( _sz_source );

    if ( _coord_sys == 0 )
    {
        longlat_2_xy( left , top );
    }
    else
    {
        xy_2_xy( left , top );
    }
}

void CoordTrans::trans_back_src( double&left , double&top )
{
    double left2 = left;
    double top2 = top;

    string proj ;
    if ( _coord_sys == 0 )
    {
        proj = "+proj=longlat ";
    }
    else if ( _coord_sys == 1 )
    {
        proj = "+proj=tmerc ";
    }

    //椭球体
    char ch_ellps[128];
    memset( ch_ellps , 0 , 128 );
    sprintf( ch_ellps , "+a=%f +b=%f " , ellps_list[_ellps].a , ellps_list[_ellps].b );
    string ellps( ch_ellps );

    //投影带
    char ch_zone[512];
    memset( ch_zone , 0 , 512 );
    sprintf ( ch_zone ,"+x_0=%f +y_0=0.000000 +lat_0=0.000000 +lon_0=%f +units=m +k=1.000000 "
        , theProj[_maj_zone].pfe , theProj[_maj_zone].pl );
    string zone( ch_zone );

    //towgs84
    char ch_wgs84[128];
    memset( ch_wgs84 , 0 , 128 );
    sprintf( ch_wgs84 , "+towgs84=%f,%f,%f,%f,%f,%f,%f" , _dx , _dy , _dz , _rx , _ry , _rz , _k );
    string towgs84( ch_wgs84 );

    sprintf ( _sz_des ,"%s%s%s%s" , proj.c_str() , ellps.c_str() , zone.c_str() , towgs84.c_str() );


    init_proj( coord_google , _sz_des );

    if ( _coord_sys == 0 )
    {
        longlat_2_xy( left , top );
    }
    else
    {
        xy_2_xy( left , top );
    }
}

bool CoordTrans::init_proj( const char* src_ ,const char* des_ )
{
    deinit();

    _source = pj_init_plus( src_ );
    if ( !_source )
    {
        return false;
    }
    _des = pj_init_plus( des_ );
    if ( !_des )
    {
        return false;
    }

    return true;
}

void CoordTrans::xy_2_latlong( double&x , double&y )
{
    pj_transform(  _source,_des, 1, 0, &x , &y , 0 );

    x *= RAD_TO_DEG;
    y *= RAD_TO_DEG;
}

void CoordTrans::xy_2_xy( double&x , double&y )
{
    pj_transform(  _source,_des, 1, 0, &x , &y , 0 );
}
void CoordTrans::longlat_2_xy( double&x , double&y )
{
    x *= DEG_TO_RAD;
    y *= DEG_TO_RAD;
    pj_transform(  _source ,_des ,  1, 0, &x , &y , 0 );

}

void CoordTrans::deinit()
{
    if ( _source )
    {
        pj_free( _source );
        _source = NULL;
    }

    if ( _des )
    {
        pj_free( _des );

        _des= NULL;
    }
}

tiff处理的头文件

#pragma once
#include "tiflib.h"
#include "coord_trans.h"
#include <math.h>
#include <string>
#include <vector>
using namespace std;

#include<Windows.h>

#define TIFF_HEADER_SIZE 8    //文件头字节数
#define DE_START 10           //tiff TAG开始的位置
#define ONE_DE_SIZE 12        //每个TAG的大小
#define IDF_END_FLAG_SIZE 4   //IFD最后结尾的4个空字节
typedef struct
{
    int i_tag;
    const char* text;
}TagText;

typedef struct
{
    TIFF_UINT16_T type;
    char* type_name;
    TIFF_UINT16_T type_size;
}DataType;
typedef struct
{
    TIFF_UINT16_T tag;
    TIFF_UINT16_T type;
    TIFF_UINT32_T count;
    TIFF_UINT32_T offset;
}DirectoryEntry;

typedef struct
{
    DirectoryEntry de;
    int data_source; //0 - offset本身值 1 - offset对应的源文件偏移量 2 - 来自内存
    TIFF_UINT8_T* mem_data; //当 data_soure = 2 时 ,指向内存
}deInfo;

typedef struct
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值