Android的图片压缩类ThumbnailUtils

  1. 1、extractThumbnail (source, width, height):  
  2.  /** 
  3.  * 
  4.  * 创建一个指定大小的缩略图 
  5.  * @param source 源文件(Bitmap类型) 
  6.  * @param width  压缩成的宽度 
  7.  * @param height 压缩成的高度 
  8.  */  
  9. ThumbnailUtils.extractThumbnail(source, width, height);  
  10.   
  11.    
  12.   
  13. 2、extractThumbnail(source, width, height, options):  
  14.  /** 
  15.  * 创建一个指定大小居中的缩略图 
  16.  * 
  17.  * @param source 源文件(Bitmap类型) 
  18.  * @param width  输出缩略图的宽度 
  19.  * @param height 输出缩略图的高度 
  20.  * @param options 如果options定义为OPTIONS_RECYCLE_INPUT,则回收 
  21.  * @param source这个资源文件 
  22.  * (除非缩略图等于@param source) 
  23.  * 
  24.  */  
  25.  ThumbnailUtils.extractThumbnail(source, width, height, options);  
  26.    
  27.    
  28. 3、createVideoThumbnail(filePath, kind):  
  29.   
  30. /** 
  31.  * 创建一张视频的缩略图 
  32.  * 如果视频已损坏或者格式不支持可能返回null 
  33.  * 
  34.  * @param filePath 视频文件路径  如:/sdcard/android.3gp 
  35.  * @param kind kind可以为MINI_KIND或MICRO_KIND 
  36.  * 
  37.  */  
  38. ThumbnailUtils.createVideoThumbnail(filePath, kind);  
  39.   
  40. ================================================源码================================  
  41. /* 
  42.  * Copyright (C) 2009 The Android Open Source Project 
  43.  * 
  44.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  45.  * you may not use this file except in compliance with the License. 
  46.  * You may obtain a copy of the License at 
  47.  * 
  48.  *      http://www.apache.org/licenses/LICENSE-2.0 
  49.  * 
  50.  * Unless required by applicable law or agreed to in writing, software 
  51.  * distributed under the License is distributed on an "AS IS" BASIS, 
  52.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  53.  * See the License for the specific language governing permissions and 
  54.  * limitations under the License. 
  55.  */   
  56.    
  57. package android.media;   
  58.    
  59. import android.content.ContentResolver;   
  60. import android.content.ContentUris;   
  61. import android.content.ContentValues;   
  62. import android.database.Cursor;   
  63. import android.graphics.Bitmap;   
  64. import android.graphics.BitmapFactory;   
  65. import android.graphics.Canvas;   
  66. import android.graphics.Matrix;   
  67. import android.graphics.Rect;   
  68. import android.media.MediaMetadataRetriever;   
  69. import android.media.MediaFile.MediaFileType;   
  70. import android.net.Uri;   
  71. import android.os.ParcelFileDescriptor;   
  72. import android.provider.BaseColumns;   
  73. import android.provider.MediaStore.Images;   
  74. import android.provider.MediaStore.Images.Thumbnails;   
  75. import android.util.Log;   
  76.    
  77. import java.io.FileInputStream;   
  78. import java.io.FileDescriptor;   
  79. import java.io.IOException;   
  80. import java.io.OutputStream;   
  81.    
  82. /** 
  83.  * Thumbnail generation routines for media provider. 
  84.  */   
  85.    
  86. public class ThumbnailUtils {   
  87.     private static final String TAG = "ThumbnailUtils";   
  88.    
  89.     /* Maximum pixels size for created bitmap. */   
  90.     private static final int MAX_NUM_PIXELS_THUMBNAIL = 512 * 384;   
  91.     private static final int MAX_NUM_PIXELS_MICRO_THUMBNAIL = 128 * 128;   
  92.     private static final int UNCONSTRAINED = -1;   
  93.    
  94.     /* Options used internally. */   
  95.     private static final int OPTIONS_NONE = 0x0;   
  96.     private static final int OPTIONS_SCALE_UP = 0x1;   
  97.    
  98.     /** 
  99.      * Constant used to indicate we should recycle the input in 
  100.      * {@link #extractThumbnail(Bitmap, int, int, int)} unless the output is the input. 
  101.      */   
  102.     public static final int OPTIONS_RECYCLE_INPUT = 0x2;   
  103.    
  104.     /** 
  105.      * Constant used to indicate the dimension of mini thumbnail. 
  106.      * @hide Only used by media framework and media provider internally. 
  107.      */   
  108.     public static final int TARGET_SIZE_MINI_THUMBNAIL = 320;   
  109.    
  110.     /** 
  111.      * Constant used to indicate the dimension of micro thumbnail. 
  112.      * @hide Only used by media framework and media provider internally. 
  113.      */   
  114.     public static final int TARGET_SIZE_MICRO_THUMBNAIL = 96;   
  115.    
  116.     /** 
  117.      * This method first examines if the thumbnail embedded in EXIF is bigger than our target 
  118.      * size. If not, then it'll create a thumbnail from original image. Due to efficiency 
  119.      * consideration, we want to let MediaThumbRequest avoid calling this method twice for 
  120.      * both kinds, so it only requests for MICRO_KIND and set saveImage to true. 
  121.      * 
  122.      * This method always returns a "square thumbnail" for MICRO_KIND thumbnail. 
  123.      * 
  124.      * @param filePath the path of image file 
  125.      * @param kind could be MINI_KIND or MICRO_KIND 
  126.      * @return Bitmap 
  127.      * 
  128.      * @hide This method is only used by media framework and media provider internally. 
  129.      */   
  130.     public static Bitmap createImageThumbnail(String filePath, int kind) {   
  131.         boolean wantMini = (kind == Images.Thumbnails.MINI_KIND);   
  132.         int targetSize = wantMini   
  133.                 ? TARGET_SIZE_MINI_THUMBNAIL   
  134.                 : TARGET_SIZE_MICRO_THUMBNAIL;   
  135.         int maxPixels = wantMini   
  136.                 ? MAX_NUM_PIXELS_THUMBNAIL   
  137.                 : MAX_NUM_PIXELS_MICRO_THUMBNAIL;   
  138.         SizedThumbnailBitmap sizedThumbnailBitmap = new SizedThumbnailBitmap();   
  139.         Bitmap bitmap = null;   
  140.         MediaFileType fileType = MediaFile.getFileType(filePath);   
  141.         if (fileType != null && fileType.fileType == MediaFile.FILE_TYPE_JPEG) {   
  142.             createThumbnailFromEXIF(filePath, targetSize, maxPixels, sizedThumbnailBitmap);   
  143.             bitmap = sizedThumbnailBitmap.mBitmap;   
  144.         }   
  145.    
  146.         if (bitmap == null) {   
  147.             try {   
  148.                 FileDescriptor fd = new FileInputStream(filePath).getFD();   
  149.                 BitmapFactory.Options options = new BitmapFactory.Options();   
  150.                 options.inSampleSize = 1;   
  151.                 options.inJustDecodeBounds = true;   
  152.                 BitmapFactory.decodeFileDescriptor(fd, null, options);   
  153.                 if (options.mCancel || options.outWidth == -1   
  154.                         || options.outHeight == -1) {   
  155.                     return null;   
  156.                 }   
  157.                 options.inSampleSize = computeSampleSize(   
  158.                         options, targetSize, maxPixels);   
  159.                 options.inJustDecodeBounds = false;   
  160.    
  161.                 options.inDither = false;   
  162.                 options.inPreferredConfig = Bitmap.Config.ARGB_8888;   
  163.                 bitmap = BitmapFactory.decodeFileDescriptor(fd, null, options);   
  164.             } catch (IOException ex) {   
  165.                 Log.e(TAG, "", ex);   
  166.             }   
  167.         }   
  168.    
  169.         if (kind == Images.Thumbnails.MICRO_KIND) {   
  170.             // now we make it a "square thumbnail" for MICRO_KIND thumbnail    
  171.             bitmap = extractThumbnail(bitmap,   
  172.                     TARGET_SIZE_MICRO_THUMBNAIL,   
  173.                     TARGET_SIZE_MICRO_THUMBNAIL, OPTIONS_RECYCLE_INPUT);   
  174.         }   
  175.         return bitmap;   
  176.     }   
  177.    
  178.     /** 
  179.      * Create a video thumbnail for a video. May return null if the video is 
  180.      * corrupt or the format is not supported. 
  181.      * 
  182.      * @param filePath the path of video file 
  183.      * @param kind could be MINI_KIND or MICRO_KIND 
  184.      */   
  185.     public static Bitmap createVideoThumbnail(String filePath, int kind) {   
  186.         Bitmap bitmap = null;   
  187.         MediaMetadataRetriever retriever = new MediaMetadataRetriever();   
  188.         try {   
  189.             retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY);   
  190.             retriever.setDataSource(filePath);   
  191.             bitmap = retriever.captureFrame();   
  192.         } catch (IllegalArgumentException ex) {   
  193.             // Assume this is a corrupt video file    
  194.         } catch (RuntimeException ex) {   
  195.             // Assume this is a corrupt video file.    
  196.         } finally {   
  197.             try {   
  198.                 retriever.release();   
  199.             } catch (RuntimeException ex) {   
  200.                 // Ignore failures while cleaning up.    
  201.             }   
  202.         }   
  203.         if (kind == Images.Thumbnails.MICRO_KIND && bitmap != null) {   
  204.             bitmap = extractThumbnail(bitmap,   
  205.                     TARGET_SIZE_MICRO_THUMBNAIL,   
  206.                     TARGET_SIZE_MICRO_THUMBNAIL,   
  207.                     OPTIONS_RECYCLE_INPUT);   
  208.         }   
  209.         return bitmap;   
  210.     }   
  211.    
  212.     /** 
  213.      * Creates a centered bitmap of the desired size. 
  214.      * 
  215.      * @param source original bitmap source 
  216.      * @param width targeted width 
  217.      * @param height targeted height 
  218.      */   
  219.     public static Bitmap extractThumbnail(   
  220.             Bitmap source, int width, int height) {   
  221.         return extractThumbnail(source, width, height, OPTIONS_NONE);   
  222.     }   
  223.    
  224.     /** 
  225.      * Creates a centered bitmap of the desired size. 
  226.      * 
  227.      * @param source original bitmap source 
  228.      * @param width targeted width 
  229.      * @param height targeted height 
  230.      * @param options options used during thumbnail extraction 
  231.      */   
  232.     public static Bitmap extractThumbnail(   
  233.             Bitmap source, int width, int height, int options) {   
  234.         if (source == null) {   
  235.             return null;   
  236.         }   
  237.    
  238.         float scale;   
  239.         if (source.getWidth() < source.getHeight()) {   
  240.             scale = width / (float) source.getWidth();   
  241.         } else {   
  242.             scale = height / (float) source.getHeight();   
  243.         }   
  244.         Matrix matrix = new Matrix();   
  245.         matrix.setScale(scale, scale);   
  246.         Bitmap thumbnail = transform(matrix, source, width, height,   
  247.                 OPTIONS_SCALE_UP | options);   
  248.         return thumbnail;   
  249.     }   
  250.    
  251.     /* 
  252.      * Compute the sample size as a function of minSideLength 
  253.      * and maxNumOfPixels. 
  254.      * minSideLength is used to specify that minimal width or height of a 
  255.      * bitmap. 
  256.      * maxNumOfPixels is used to specify the maximal size in pixels that is 
  257.      * tolerable in terms of memory usage. 
  258.      * 
  259.      * The function returns a sample size based on the constraints. 
  260.      * Both size and minSideLength can be passed in as IImage.UNCONSTRAINED, 
  261.      * which indicates no care of the corresponding constraint. 
  262.      * The functions prefers returning a sample size that 
  263.      * generates a smaller bitmap, unless minSideLength = IImage.UNCONSTRAINED. 
  264.      * 
  265.      * Also, the function rounds up the sample size to a power of 2 or multiple 
  266.      * of 8 because BitmapFactory only honors sample size this way. 
  267.      * For example, BitmapFactory downsamples an image by 2 even though the 
  268.      * request is 3. So we round up the sample size to avoid OOM. 
  269.      */   
  270.     private static int computeSampleSize(BitmapFactory.Options options,   
  271.             int minSideLength, int maxNumOfPixels) {   
  272.         int initialSize = computeInitialSampleSize(options, minSideLength,   
  273.                 maxNumOfPixels);   
  274.    
  275.         int roundedSize;   
  276.         if (initialSize <= 8 ) {   
  277.             roundedSize = 1;   
  278.             while (roundedSize < initialSize) {   
  279.                 roundedSize <<= 1;   
  280.             }   
  281.         } else {   
  282.             roundedSize = (initialSize + 7) / 8 * 8;   
  283.         }   
  284.    
  285.         return roundedSize;   
  286.     }   
  287.    
  288.     private static int computeInitialSampleSize(BitmapFactory.Options options,   
  289.             int minSideLength, int maxNumOfPixels) {   
  290.         double w = options.outWidth;   
  291.         double h = options.outHeight;   
  292.    
  293.         int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 :   
  294.                 (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));   
  295.         int upperBound = (minSideLength == UNCONSTRAINED) ? 128 :   
  296.                 (int) Math.min(Math.floor(w / minSideLength),   
  297.                 Math.floor(h / minSideLength));   
  298.    
  299.         if (upperBound < lowerBound) {   
  300.             // return the larger one when there is no overlapping zone.    
  301.             return lowerBound;   
  302.         }   
  303.    
  304.         if ((maxNumOfPixels == UNCONSTRAINED) &&   
  305.                 (minSideLength == UNCONSTRAINED)) {   
  306.             return 1;   
  307.         } else if (minSideLength == UNCONSTRAINED) {   
  308.             return lowerBound;   
  309.         } else {   
  310.             return upperBound;   
  311.         }   
  312.     }   
  313.    
  314.     /** 
  315.      * Make a bitmap from a given Uri, minimal side length, and maximum number of pixels. 
  316.      * The image data will be read from specified pfd if it's not null, otherwise 
  317.      * a new input stream will be created using specified ContentResolver. 
  318.      * 
  319.      * Clients are allowed to pass their own BitmapFactory.Options used for bitmap decoding. A 
  320.      * new BitmapFactory.Options will be created if options is null. 
  321.      */   
  322.     private static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,   
  323.             Uri uri, ContentResolver cr, ParcelFileDescriptor pfd,   
  324.             BitmapFactory.Options options) {   
  325.             Bitmap b = null;   
  326.         try {   
  327.             if (pfd == null) pfd = makeInputStream(uri, cr);   
  328.             if (pfd == nullreturn null;   
  329.             if (options == null) options = new BitmapFactory.Options();   
  330.    
  331.             FileDescriptor fd = pfd.getFileDescriptor();   
  332.             options.inSampleSize = 1;   
  333.             options.inJustDecodeBounds = true;   
  334.             BitmapFactory.decodeFileDescriptor(fd, null, options);   
  335.             if (options.mCancel || options.outWidth == -1   
  336.                     || options.outHeight == -1) {   
  337.                 return null;   
  338.             }   
  339.             options.inSampleSize = computeSampleSize(   
  340.                     options, minSideLength, maxNumOfPixels);   
  341.             options.inJustDecodeBounds = false;   
  342.    
  343.             options.inDither = false;   
  344.             options.inPreferredConfig = Bitmap.Config.ARGB_8888;   
  345.             b = BitmapFactory.decodeFileDescriptor(fd, null, options);   
  346.         } catch (OutOfMemoryError ex) {   
  347.             Log.e(TAG, "Got oom exception ", ex);   
  348.             return null;   
  349.         } finally {   
  350.             closeSilently(pfd);   
  351.         }   
  352.         return b;   
  353.     }   
  354.    
  355.     private static void closeSilently(ParcelFileDescriptor c) {   
  356.       if (c == nullreturn;   
  357.       try {   
  358.           c.close();   
  359.       } catch (Throwable t) {   
  360.           // do nothing    
  361.       }   
  362.     }   
  363.    
  364.     private static ParcelFileDescriptor makeInputStream(   
  365.             Uri uri, ContentResolver cr) {   
  366.         try {   
  367.             return cr.openFileDescriptor(uri, "r");   
  368.         } catch (IOException ex) {   
  369.             return null;   
  370.         }   
  371.     }   
  372.    
  373.     /** 
  374.      * Transform source Bitmap to targeted width and height. 
  375.      */   
  376.     private static Bitmap transform(Matrix scaler,   
  377.             Bitmap source,   
  378.             int targetWidth,   
  379.             int targetHeight,   
  380.             int options) {   
  381.         boolean scaleUp = (options & OPTIONS_SCALE_UP) != 0;   
  382.         boolean recycle = (options & OPTIONS_RECYCLE_INPUT) != 0;   
  383.    
  384.         int deltaX = source.getWidth() - targetWidth;   
  385.         int deltaY = source.getHeight() - targetHeight;   
  386.         if (!scaleUp && (deltaX < 0 || deltaY < 0)) {   
  387.             /* 
  388.             * In this case the bitmap is smaller, at least in one dimension, 
  389.             * than the target.  Transform it by placing as much of the image 
  390.             * as possible into the target and leaving the top/bottom or 
  391.             * left/right (or both) black. 
  392.             */   
  393.             Bitmap b2 = Bitmap.createBitmap(targetWidth, targetHeight,   
  394.             Bitmap.Config.ARGB_8888);   
  395.             Canvas c = new Canvas(b2);   
  396.    
  397.             int deltaXHalf = Math.max(0, deltaX / 2);   
  398.             int deltaYHalf = Math.max(0, deltaY / 2);   
  399.             Rect src = new Rect(   
  400.             deltaXHalf,   
  401.             deltaYHalf,   
  402.             deltaXHalf + Math.min(targetWidth, source.getWidth()),   
  403.             deltaYHalf + Math.min(targetHeight, source.getHeight()));   
  404.             int dstX = (targetWidth  - src.width())  / 2;   
  405.             int dstY = (targetHeight - src.height()) / 2;   
  406.             Rect dst = new Rect(   
  407.                     dstX,   
  408.                     dstY,   
  409.                     targetWidth - dstX,   
  410.                     targetHeight - dstY);   
  411.             c.drawBitmap(source, src, dst, null);   
  412.             if (recycle) {   
  413.                 source.recycle();   
  414.             }   
  415.             return b2;   
  416.         }   
  417.         float bitmapWidthF = source.getWidth();   
  418.         float bitmapHeightF = source.getHeight();   
  419.    
  420.         float bitmapAspect = bitmapWidthF / bitmapHeightF;   
  421.         float viewAspect   = (float) targetWidth / targetHeight;   
  422.    
  423.         if (bitmapAspect > viewAspect) {   
  424.             float scale = targetHeight / bitmapHeightF;   
  425.             if (scale < .9F || scale > 1F) {   
  426.                 scaler.setScale(scale, scale);   
  427.             } else {   
  428.                 scaler = null;   
  429.             }   
  430.         } else {   
  431.             float scale = targetWidth / bitmapWidthF;   
  432.             if (scale < .9F || scale > 1F) {   
  433.                 scaler.setScale(scale, scale);   
  434.             } else {   
  435.                 scaler = null;   
  436.             }   
  437.         }   
  438.    
  439.         Bitmap b1;   
  440.         if (scaler != null) {   
  441.             // this is used for minithumb and crop, so we want to filter here.    
  442.             b1 = Bitmap.createBitmap(source, 00,   
  443.             source.getWidth(), source.getHeight(), scaler, true);   
  444.         } else {   
  445.             b1 = source;   
  446.         }   
  447.    
  448.         if (recycle && b1 != source) {   
  449.             source.recycle();   
  450.         }   
  451.    
  452.         int dx1 = Math.max(0, b1.getWidth() - targetWidth);   
  453.         int dy1 = Math.max(0, b1.getHeight() - targetHeight);   
  454.    
  455.         Bitmap b2 = Bitmap.createBitmap(   
  456.                 b1,   
  457.                 dx1 / 2,   
  458.                 dy1 / 2,   
  459.                 targetWidth,   
  460.                 targetHeight);   
  461.    
  462.         if (b2 != b1) {   
  463.             if (recycle || b1 != source) {   
  464.                 b1.recycle();   
  465.             }   
  466.         }   
  467.    
  468.         return b2;   
  469.     }   
  470.    
  471.     /** 
  472.      * SizedThumbnailBitmap contains the bitmap, which is downsampled either from 
  473.      * the thumbnail in exif or the full image. 
  474.      * mThumbnailData, mThumbnailWidth and mThumbnailHeight are set together only if mThumbnail 
  475.      * is not null. 
  476.      * 
  477.      * The width/height of the sized bitmap may be different from mThumbnailWidth/mThumbnailHeight. 
  478.      */   
  479.     private static class SizedThumbnailBitmap {   
  480.         public byte[] mThumbnailData;   
  481.         public Bitmap mBitmap;   
  482.         public int mThumbnailWidth;   
  483.         public int mThumbnailHeight;   
  484.     }   
  485.    
  486.     /** 
  487.      * Creates a bitmap by either downsampling from the thumbnail in EXIF or the full image. 
  488.      * The functions returns a SizedThumbnailBitmap, 
  489.      * which contains a downsampled bitmap and the thumbnail data in EXIF if exists. 
  490.      */   
  491.     private static void createThumbnailFromEXIF(String filePath, int targetSize,   
  492.             int maxPixels, SizedThumbnailBitmap sizedThumbBitmap) {   
  493.         if (filePath == nullreturn;   
  494.    
  495.         ExifInterface exif = null;   
  496.         byte [] thumbData = null;   
  497.         try {   
  498.             exif = new ExifInterface(filePath);   
  499.             if (exif != null) {   
  500.                 thumbData = exif.getThumbnail();   
  501.             }   
  502.         } catch (IOException ex) {   
  503.             Log.w(TAG, ex);   
  504.         }   
  505.    
  506.         BitmapFactory.Options fullOptions = new BitmapFactory.Options();   
  507.         BitmapFactory.Options exifOptions = new BitmapFactory.Options();   
  508.         int exifThumbWidth = 0;   
  509.         int fullThumbWidth = 0;   
  510.    
  511.         // Compute exifThumbWidth.    
  512.         if (thumbData != null) {   
  513.             exifOptions.inJustDecodeBounds = true;   
  514.             BitmapFactory.decodeByteArray(thumbData, 0, thumbData.length, exifOptions);   
  515.             exifOptions.inSampleSize = computeSampleSize(exifOptions, targetSize, maxPixels);   
  516.             exifThumbWidth = exifOptions.outWidth / exifOptions.inSampleSize;   
  517.         }   
  518.    
  519.         // Compute fullThumbWidth.    
  520.         fullOptions.inJustDecodeBounds = true;   
  521.         BitmapFactory.decodeFile(filePath, fullOptions);   
  522.         fullOptions.inSampleSize = computeSampleSize(fullOptions, targetSize, maxPixels);   
  523.         fullThumbWidth = fullOptions.outWidth / fullOptions.inSampleSize;   
  524.    
  525.         // Choose the larger thumbnail as the returning sizedThumbBitmap.    
  526.         if (thumbData != null && exifThumbWidth >= fullThumbWidth) {   
  527.             int width = exifOptions.outWidth;   
  528.             int height = exifOptions.outHeight;   
  529.             exifOptions.inJustDecodeBounds = false;   
  530.             sizedThumbBitmap.mBitmap = BitmapFactory.decodeByteArray(thumbData, 0,   
  531.                     thumbData.length, exifOptions);   
  532.             if (sizedThumbBitmap.mBitmap != null) {   
  533.                 sizedThumbBitmap.mThumbnailData = thumbData;   
  534.                 sizedThumbBitmap.mThumbnailWidth = width;   
  535.                 sizedThumbBitmap.mThumbnailHeight = height;   
  536.             }   
  537.         } else {   
  538.             fullOptions.inJustDecodeBounds = false;   
  539.             sizedThumbBitmap.mBitmap = BitmapFactory.decodeFile(filePath, fullOptions);   
  540.         }   
  541.     }   
  542. }  
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值