音视频入门-20-BMP、PNG、JPG、GIF静态图生成GIF动态图
静态图 -> 动态图
前面 【18-手动生成一张GIF图片】 和 【19-使用giflib处理GIF图片】 生成的 GIF 每一帧都是一个颜色,平时用到的 GIF 每一帧都是图片,下面就做一张每一帧都是图片的 GIF。
准备了 4 张静态图 .bmp
、 .png
、 .jpg
、 .gif
(静态的GIF):
BMP | PNG | JPG | GIF |
---|---|---|---|
Android.bmp | Huawei.png | Fuchsia.jpg | iOS.gif |
每张图片显示 1 秒,生成 GIF:
image to RGB
GIF 中使用 RGB 颜色索引来表示图像,每一帧图像最多 256 个颜色。所以第一步,要将静态图片转成 RGB。
BMP to RGB
根据 【05-RGB-TO-BMP使用开源库】 ,使用 libbmp 库来完成 .bmp
to RGB。
int decodeBMP(char *filename, unsigned char **bmpRGB) {
bmp_img img;
bmp_img_read(&img, filename);
int width = img.img_header.biWidth;
int height = img.img_header.biHeight;
printf("Size: [%d, %d]\n", width, height);
printf("BitCount: %d\n", img.img_header.biBitCount);
printf("Compression: %d\n", img.img_header.biCompression);
printf("SizeImage: %d\n", img.img_header.biSizeImage);
*bmpRGB = malloc(width * height * 3);
int x, y;
unsigned char *BufferP;
for (x = 0 ; x < height ; x++) {
bmp_pixel *row = img.img_pixels[x];
for (y = 0, BufferP = *bmpRGB+width*3*x; y < width; y++) {
/* Get pixel's RGB values */
bmp_pixel pixel = row[y];
*BufferP++ = pixel.red;
*BufferP++ = pixel.green;
*BufferP++ = pixel.blue;
}
}
return 0;
}
PNG to RGB
根据 【13-使用开源库生成PNG图片】 ,使用 libpng 库来完成 .png
to RGB。
int decodePNG(char *filename, unsigned char **pngRGB) {
FILE *fp = fopen(filename, "rb");
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if(!png) {
fclose(fp);
return -1;
}
png_infop info = png_create_info_struct(png);
if(!info) {
fclose(fp);
return -1;
}
if(setjmp(png_jmpbuf(png))) {
fclose(fp);
return -1;
}
png_init_io(png, fp);
png_read_info(png, info);
int width, height;
png_byte color_type;
png_byte bit_depth;
png_bytep *row_pointers = NULL;
width = png_get_image_width(png, info);
height = png_get_image_height(png, info);
color_type = png_get_color_type(png, info);
bit_depth = png_get_bit_depth(png, info);
printf("PNG 图片尺寸:【%d, %d】\n", width, height);
printf("颜色类型:%d, 位深:%d\n", color_type, bit_depth);
// Read any color_type into 8bit depth, RGBA format.
// See http://www.libpng.org/pub/png/libpng-manual.txt
if(bit_depth == 16)
png_set_strip_16(png);
if(color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png);
// PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth.
if(color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_expand_gray_1_2_4_to_8(png);
if(png_get_valid(png, info, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(png);
// These color_type don't have an alpha channel then fill it with 0xff.
if(color_type == PNG_COLOR_TYPE_RGB ||
color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_PALETTE)
png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
if(color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png);
png_read_update_info(png, info);
int rowByteCount = png_get_rowbytes(png,info);
printf("rowByteCount: %d\n", rowByteCount);
row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);
for(int y = 0; y < height; y++) {
row_pointers[y] = (png_byte*)malloc(rowByteCount);
}
png_read_image(png, row_pointers);
*pngRGB = malloc(width*height*3);
int counter = 0;
for(int i = 0; i < height; i++) {
if(color_type == 6) { // 带有透明 RGBA
for(int j = 0; j < rowByteCount; j+=4) {
memcpy(*pngRGB+counter, row_pointers[i]+j, 3);
counter+=3;
}
} else {
memcpy(*pngRGB+rowByteCount, row_pointers[i], rowByteCount);
}
}
fclose(fp);
png_destroy_read_struct(&png, &info, NULL);
return 0;
}
JPG to RGB
根据 【16-使用libjpeg-trubo处理JPEG图片】 ,使用 libjpeg-turbo 库来完成 .jpg
to RGB。
int decodeJPG(char *filename, unsigned char **jpgRGB) {
FILE *fp = fopen(filename, "rb");
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, fp);
jpeg_read_header(&cinfo, TRUE);
printf("image_width = %d\n", cinfo.image_width);
printf("image_height = %d\n", cinfo.image_height);
printf("num_components = %d\n", cinfo.num_components);
printf("enter scale M/N:\n");
// cinfo.out_color_space = JCS_YCbCr;
printf("scale to : %d/%d\n", cinfo.scale_num, cinfo.scale_denom);
jpeg_start_decompress(&cinfo);
//输出的图象的信息
printf("output_width = %d\n", cinfo.output_width);
printf("output_height = %d\n", cinfo.output_height);
printf("output_components = %d\n", cinfo.output_components);
int row_stride = cinfo.output_width * cinfo.output_components;
/* Make a one-row-high sample array that will go away when done with image */
JSAMPARRAY buffer = (JSAMPARRAY)malloc(sizeof(JSAMPROW));
buffer[0] = (JSAMPROW)malloc(sizeof(JSAMPLE) * row_stride);
*jpgRGB = malloc(row_stride*cinfo.image_height);
long counter = 0;
while (cinfo.output_scanline < cinfo.output_height) {
jpeg_read_scanlines(&cinfo, buffer, 1);
memcpy(*jpgRGB + counter, buffer[0], row_stride);
counter += row_stride;
}
printf("total size: %ld\n", counter);
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
fclose(fp);
return 0;
}
GIF to RGB
根据 【19-使用giflib处理GIF图片】 ,使用 giflib 库来完成 .gif
to RGB。
int decodeGIF(char *filename, unsigned char **gifRGB) {
int i, j, Size, Row, Col, Width, Height, ExtCode, Count;
GifRecordType RecordType;
GifByteType *Extension;
GifRowType *ScreenBuffer;
GifFileType *GifFile;
int InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */
int ImageNum = 0;
ColorMapObject *ColorMap;
int Error;
if ((GifFile = DGifOpenFileName(filename, &Error)) == NULL) {
printf("Open File Error.\n");
return -1;
}
if (GifFile->SHeight == 0 || GifFile->SWidth == 0) {
printf("Image of width or height 0\n");
return -1;
}
/*
* Allocate the screen as vector of column of rows. Note this
* screen is device independent - it's the screen defined by the
* GIF file parameters.
*/
if ((ScreenBuffer = (GifRowType *)
malloc(GifFile->SHeight * sizeof(GifRowType))) == NULL) {
printf("Failed to allocate memory required, aborted.\n");
return -1;
}
Size = GifFile->SWidth * sizeof(GifPixelType);/* Size in bytes one row.*/
if ((ScreenBuffer[0] = (GifRowType) malloc(Size)) == NULL) { /* First row. */
printf("Failed to allocate memory required, aborted.\n");
return -1;
}
for (i = 0; i < GifFile->SWidth; i++) /* Set its color to BackGround. */
ScreenBuffer[0][i] = GifFile->SBackGroundColor;
for (i = 1; i < GifFile->SHeight; i++) {
/* Allocate the other rows, and set their color to background too: */
if ((ScreenBuffer[i] = (GifRowType) malloc(Size)) == NULL) {
printf("Failed to allocate memory required, aborted.\n");
return -1;
}
memcpy(ScreenBuffer[i], ScreenBuffer[0], Size);
}
int screenIndex = 0;
/* Scan the content of the GIF file and load the image(s) in: */
do {
if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
printf("DGifGetRecordType Error.\n");
return -1;
}
switch (RecordType) {
case IMAGE_DESC_RECORD_TYPE:
if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
printf("DGifGetImageDesc Error.\n");
return -1;
}
Row = GifFile->Image.Top; /* Image Position relative to Screen. */
Col = GifFile->Image.Left;
Width = GifFile->Image.Width;
Height = GifFile->Image.Height;
if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
printf("Image %d is not confined to screen dimension, aborted.\n",ImageNum);
return -1;
}
if (GifFile->Image.Interlace) {
/* Need to perform 4 passes on the images: */
for (Count = i = 0; i < 4; i++)
for (j = Row + InterlacedOffset[i]; j < Row + Height;
j += InterlacedJumps[i]) {
if (DGifGetLine(GifFile, &ScreenBuffer[j][Col],
Width) == GIF_ERROR) {
printf("DGifGetLine Error.\n");
return -1;
}
}
}
else {
for (i = 0; i < Height; i++) {
if (DGifGetLine(GifFile, &ScreenBuffer[Row++][Col],
Width) == GIF_ERROR) {
printf("DGifGetLine Error.\n");
return -1;
}
}
}
/* Lets dump it - set the global variables required and do it: */
ColorMap = (GifFile->Image.ColorMap
? GifFile->Image.ColorMap
: GifFile->SColorMap);
if (ColorMap == NULL) {
fprintf(stderr, "Gif Image does not have a colormap\n");
exit(EXIT_FAILURE);
}
/* check that the background color isn't garbage (SF bug #87) */
if (GifFile->SBackGroundColor < 0 || GifFile->SBackGroundColor >= ColorMap->ColorCount) {
printf("Background color out of range for colormap\n");
return -1;
}
GifRowType GifRow;
GifColorType *ColorMapEntry;
unsigned char *BufferP;
*gifRGB = malloc(GifFile->SWidth*GifFile->SHeight*3);
for (i = 0; i < GifFile->SHeight; i++) {
GifRow = ScreenBuffer[i];
for (j = 0, BufferP = *gifRGB+GifFile->SWidth*3*i; j < GifFile->SWidth; j++) {
ColorMapEntry = &ColorMap->Colors[GifRow[j]];
*BufferP++ = ColorMapEntry->Red;
*BufferP++ = ColorMapEntry->Green;
*BufferP++ = ColorMapEntry->Blue;
}
}
break;
case EXTENSION_RECORD_TYPE:
/* Skip any extension blocks in file: */
if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
printf("DGifGetExtension Error.\n");
return -1;
}
while (Extension != NULL) {
if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
printf("DGifGetExtensionNext Error.\n");
return -1;
}
}
break;
case TERMINATE_RECORD_TYPE:
break;
default: /* Should be trapped by DGifGetRecordType. */
break;
}
} while (RecordType != TERMINATE_RECORD_TYPE);
(void)free(ScreenBuffer);
if (DGifCloseFile(GifFile, &Error) == GIF_ERROR) {
printf("DGifCloseFile Error.\n");
return -1;
}
return 0;
}
RGB 查看
ffplay -f rawvideo -pixel_format rgb24 -video_size 400x400 texture.rgb
RGB to GIF
将静态图转成 RGB 以后,就可以根据 【19-使用giflib处理GIF图片】 使用 giflib 将 RGB 编码成 GIF 动态图。
完整步骤
#include <stdio.h>
......
int decodeBMP(char *, unsigned char **);
int decodePNG(char *, unsigned char **);
int decodeJPG(char *, unsigned char **);
int decodeGIF(char *, unsigned char **);
int encodeGIF(unsigned char **RGBBuffers, int NumOfRGBBuffer, char *GIFFileName,
int ExpNumOfColors, int Width, int Height);
int main() {
char *bmp = "/Users/staff/Desktop/Android.bmp";
char *png = "/Users/staff/Desktop/Huawei.png";
char *jpg = "/Users/staff/Desktop/Fuchsia.jpg";
char *gif = "/Users/staff/Desktop/iOS.gif";
unsigned char *bmpRGB = NULL;
unsigned char *pngRGB = NULL;
unsigned char *jpgRGB = NULL;
unsigned char *gifRGB = NULL;
printf("开始解码 BMP 文件!\n");
decodeBMP(bmp, &bmpRGB);
char *androidRGB = "/Users/staff/Desktop/Android.rgb";
FILE *androidRGBFile = fopen(androidRGB, "wb");
fwrite(bmpRGB, 400*400*3, 1, androidRGBFile);
fclose(androidRGBFile);
printf("\n\n");
printf("开始解码 PNG 文件!\n");
decodePNG(png, &pngRGB);
char *huaweiRGB = "/Users/staff/Desktop/Huawei.rgb";
FILE *huaweiRGBFile = fopen(huaweiRGB, "wb");
fwrite(pngRGB, 400*400*3, 1, huaweiRGBFile);
fclose(huaweiRGBFile);
printf("\n\n");
printf("开始解码 JPG 文件!\n");
decodeJPG(jpg, &jpgRGB);
char *fuchsiaRGB = "/Users/staff/Desktop/Fuchsia.rgb";
FILE *fuchsiaRGBFile = fopen(fuchsiaRGB, "wb");
fwrite(jpgRGB, 400*400*3, 1, fuchsiaRGBFile);
fclose(fuchsiaRGBFile);
printf("\n\n");
printf("开始解码 GIF 文件!\n");
decodeGIF(gif, &gifRGB);
char *iOSRGB = "/Users/staff/Desktop/iOS.rgb";
FILE *iOSRGBFile = fopen(iOSRGB, "wb");
fwrite(gifRGB, 400*400*3, 1, iOSRGBFile);
fclose(iOSRGBFile);
printf("\n\n");
unsigned char **rgbBuffers = malloc(4*sizeof(unsigned char *));
rgbBuffers[0] = bmpRGB;
rgbBuffers[1] = pngRGB;
rgbBuffers[2] = jpgRGB;
rgbBuffers[3] = gifRGB;
encodeGIF(rgbBuffers, 4, "/Users/staff/Desktop/image-to-gif.gif",
7, 400, 400);
free(bmpRGB);
free(pngRGB);
free(jpgRGB);
free(gifRGB);
return 0;
}
int decodeBMP(char *filename, unsigned char **bmpRGB) {
......
}
int decodePNG(char *filename, unsigned char **pngRGB) {
......
}
int decodeJPG(char *filename, unsigned char **jpgRGB) {
......
}
int decodeGIF(char *filename, unsigned char **gifRGB) {
......
}
int encodeGIF(unsigned char **RGBBuffers, int NumOfRGBBuffer, char *GIFFileName,
......
}
将 RGB 编码成 GIF 的方法 encodeGIF:
int encodeGIF(unsigned char **RGBBuffers, int NumOfRGBBuffer, char *GIFFileName,
int ExpNumOfColors, int Width, int Height) {
int ColorMapSize;
GifByteType *RedBuffer = NULL, *GreenBuffer = NULL, *BlueBuffer = NULL, *OutputBuffer = NULL;
ColorMapObject *OutputColorMap = NULL;
// 打开输出的 GIF 文件
int Error;
GifFileType *GifFile;
if ((GifFile = EGifOpenFileName(GIFFileName, false, &Error)) == NULL) {
PrintGifError(Error);
printf("EGifOpenFileName Error.\n");
return -1;
}
GifFile->SWidth = Width;
GifFile->SHeight = Height;
GifFile->SColorResolution = 1;
GifFile->SBackGroundColor = 0;
GifFile->SColorMap = NULL;
unsigned long Size;
GifByteType *RedP, *GreenP, *BlueP;
GifByteType *Buffer, *BufferP;
Size = ((long) Width) * Height * sizeof(GifByteType);
if ((RedBuffer = (GifByteType *) malloc((unsigned int) Size)) == NULL ||
(GreenBuffer = (GifByteType *) malloc((unsigned int) Size)) == NULL ||
(BlueBuffer = (GifByteType *) malloc((unsigned int) Size)) == NULL) {
return -1;
}
if ((Buffer = (GifByteType *) malloc(Width * 3)) == NULL) {
return -1;
}
for(int i = 0; i < NumOfRGBBuffer; i++) {
ColorMapSize = 1 << ExpNumOfColors;
RedP = RedBuffer;
GreenP = GreenBuffer;
BlueP = BlueBuffer;
int pointer = 0;
for (int j = 0; j < Height; j++) {
int k;
memcpy(Buffer, RGBBuffers[i]+pointer, Width * 3);
pointer+=Width*3;
for (k = 0, BufferP = Buffer; k < Width; k++) {
*RedP++ = *BufferP++;
*GreenP++ = *BufferP++;
*BlueP++ = *BufferP++;
}
}
if ((OutputColorMap = GifMakeMapObject(ColorMapSize, NULL)) == NULL ||
(OutputBuffer = (GifByteType *) malloc(Width * Height *
sizeof(GifByteType))) == NULL) {
printf("Failed to allocate memory required, aborted.\n");
return -1;
}
if (GifQuantizeBuffer(Width, Height, &ColorMapSize,
RedBuffer, GreenBuffer, BlueBuffer,
OutputBuffer, OutputColorMap->Colors) == GIF_ERROR) {
printf("GifQuantizeBuffer Error.\n");
return -1;
}
printf("MakeSavedImage:%d\n", i);
SavedImage *image = GifMakeSavedImage(GifFile, NULL);
GifImageDesc *imageDesc = (GifImageDesc *) malloc(sizeof(GifImageDesc));
imageDesc->Left = 0;
imageDesc->Top = 0;
imageDesc->Width = Width;
imageDesc->Height = Height;
imageDesc->Interlace = false;
imageDesc->ColorMap = OutputColorMap;
image->ImageDesc = *imageDesc;
image->RasterBits = OutputBuffer;
GraphicsControlBlock *GCB = (GraphicsControlBlock *) malloc(sizeof(GraphicsControlBlock));
GCB->DisposalMode = DISPOSAL_UNSPECIFIED;
GCB->DelayTime = 100;
GCB->UserInputFlag = false;
GCB->TransparentColor = NO_TRANSPARENT_COLOR;
printf("GCBToSaved:%d\n", i);
EGifGCBToSavedExtension(GCB, GifFile, i);
}
free((char *) RedBuffer);
free((char *) GreenBuffer);
free((char *) BlueBuffer);
printf("输出 GIF 文件。\n");
// 输出文件
EGifSpew(GifFile);
return 0;
}
查看 GIF
参考资料:
【音视频入门-16-使用libjpeg-trubo处理JPEG图片】
音视频入门-20-BMP、PNG、JPG、GIF静态图生成GIF动态图的更多相关文章
- 音视频入门-03-RGB转成BMP图片
* 音视频入门文章目录 * BMP 文件格式解析 BMP 文件由文件头.位图信息头.颜色信息和图形数据四部分组成. 位图文件头(14个字节) 位图信息头(40个字节) 颜色信息 图形数据 文件头与信息 ...
- 音视频入门-11-PNG文件格式详解
* 音视频入门文章目录 * PNG 文件格式解析 PNG 图像格式文件由一个 8 字节的 PNG 文件署名域和 3 个以上的后续数据块(IHDR.IDAT.IEND)组成. PNG 文件包括 8 字节 ...
- 音视频入门-10-使用libyuv对YUV数据进行缩放、旋转、镜像、裁剪、混合
* 音视频入门文章目录 * libyuv libyuv 是 Google 开源的实现各种 YUV 与 RGB 之间相互转换.旋转.缩放等的库.它是跨平台的,可在 Windows.Linux.Mac.A ...
- 音视频入门-09-RGB&YUV互转-使用开源库
* 音视频入门文章目录 * 介绍开源库 使用第三方开源库来简化开发,屏蔽一些底层的复杂度,节省大量编写代码的时间. libyuv: Google 开源的实现各种 YUV 与 RGB 之间相互转换.旋转 ...
- 音视频入门-08-RGB&YUV
* 音视频入门文章目录 * YUV & RGB 相互转换公式 YCbCr 的 Y 与 YUV 中的 Y 含义一致,Cb 和 Cr 与 UV 同样都指色彩,Cb 指蓝色色度,Cr 指红色色度,在 ...
- 音视频入门-05-RGB-TO-BMP使用开源库
* 音视频入门文章目录 * RGB-TO-BMP 回顾 将 RGB 数据转成 BMP 图片: 了解 BMP 文件格式 准备 BMP 文件头信息 准备 BMP 信息头 BMP 存储 RGB 的顺序是 B ...
- 音视频入门-04-BMP图像四字节对齐的问题
* 音视频入门文章目录 * BMP 图像四字节对齐 表示 BMP 位图中像素的位元是以行为单位对齐存储的,每一行的大小都向上取整为4字节(32 位 DWORD)的倍数.如果图像的高度大于 1,多个经过 ...
- 音视频入门-02-RGB拼图
* 音视频入门文章目录 * 图片 & 像素点 & RGB 平时浏览的图片看不出像素点: 图片放大时,可以看出图片是一个个像素点组成的: 每个像素点的颜色可以用 RGB 表示: RGB ...
- 堪称教科书级别的Android音视频入门进阶学习手册,开源分享!
概述 随着整个互联网的崛起,数据传递的形式也在不断升级变化,总的流行趋势如下: 纯文本的短信,QQ -> 空间,微博,朋友圈的图片文字结合 -> 微信语音 -> 各大直播软件 -&g ...
随机推荐
- apache重写URL时,排除静态资源
THINKPHP项目部署的apache 上面时,如果为了隐藏入口文件配置了重写URL,会导致将静态资源的URL也解析成Controller/Method,导致触发模块不存在 所以在URL重写配置中,需 ...
- 女朋友突然问我DNS是个啥....
女朋友突然问我DNS是个啥.... 今天晚上我正在床上躺着刷手机,然后我女朋友突然说她的电脑坏了.说连着WIFi上不了网,让我给她看一下.(这就是有个程序员男朋友的好处) 然后我拿到电脑看了一下发现访 ...
- 六、Jmeter测试元件-测试计划
启动Jmeter时,会默认建好一个测试计划,启动一个Jmeter只有个测试计划 测试用到的源码下载地址 https://www.cnblogs.com/fanfancs/p/13557062.html ...
- 【Azure Service Bus】 Service Bus如何确保消息发送成功,发送端是否有Ack机制
问题描述 Service Bus如何确保消息发送成功,发送端是否有Ack机制(是否有回调API告诉发送端,服务端已经收到消息)?根据对.NET发送Service Bus消息代码的分析,发送方法queu ...
- k8s之深入解剖Pod(二)
目录: Pod配置管理:ConfigMap 容器内获取Pod信息:Downward API Pod生命周期和重启策略 Pod健康检查 一.ConfigMap 将应用所需的配置信息与程序进行分离,可以使 ...
- 【进程/作业管理】篇章一:Linux进程及管理(专用内存监控类工具)------【vmstat、pmap】
主要讲解专用内存监控工具的使用:vmstat.pmap命令的使用. 命令概览: vmstat 显示虚拟内存状态 pmap 报告进程与内存映射关系 vmstat命令是最常见的Linux/Unix监控工具 ...
- 如何在iOS设备中配置S/MIME邮件签名证书
本篇将介绍如何在iOS设备(如iPhone或iPad)上导入.配置并使用S/MIME邮件证书. 前置条件: iOS设备上已完成邮箱账号配置: 您的S/MIME邮件证书PFX/P12文件已导出备用. 步 ...
- iOS崩溃治理--开篇
去年我开始负责iOS崩溃治理的工作,从原来的万分之五崩溃率,一直到现在的万分之一左右的崩溃率,期间踩了很多坑,因此想和大家分享一下,希望能对大家有所帮助,也欢迎大家私信交流. 如果你打算开始治理崩溃的 ...
- swap是干嘛的?
本文截取自:http://hbasefly.com/2017/05/24/hbase-linux/ swap是干嘛的? 在Linux下,SWAP的作用类似Windows系统下的"虚拟内存&q ...
- JS中var与let的区别
区别: var声明的变量,其作用域在该语句所在的函数之内,存在着变量提升的现象. let声明的变量,其作用域为该句所在的代码块内,不存在变量提升的问题. let相比于var,其不允许在相同作用域内,重 ...