* 音视频入门文章目录 *

静态图 -> 动态图

前面 【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.txtif(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) { // 带有透明 RGBAfor(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


代码:
audio-video-blog-demos

参考资料:

【音视频入门-05-RGB-TO-BMP使用开源库】

【音视频入门-13-使用开源库生成PNG图片】

【音视频入门-16-使用libjpeg-trubo处理JPEG图片】

【音视频入门-19-使用giflib处理GIF图片】


音视频入门-20-BMP、PNG、JPG、GIF静态图生成GIF动态图相关推荐

  1. 音视频入门系列-服务器篇(nginx-rtmp 的部署)

    本篇文章,我们详细介绍下使用nginx-rtmp来部署一套流媒体服务器,并用FFmpeg来推流,VLC来拉流. RTMP是Real Time Messaging Protocol(实时消息传输协议)的 ...

  2. 堪称教科书级别的Android音视频入门进阶学习手册,开源分享

    概述 随着整个互联网的崛起,数据传递的形式也在不断升级变化,总的流行趋势如下: 纯文本的短信,QQ -> 空间,微博,朋友圈的图片文字结合 -> 微信语音 -> 各大直播软件 -&g ...

  3. 音视频入门-02-RGB拼图

    音视频入门文章目录 图片 & 像素点 & RGB 平时浏览的图片看不出像素点: 图片放大时,可以看出图片是一个个像素点组成的: 每个像素点的颜色可以用 RGB 表示: RGB 拼图 既 ...

  4. 音视频入门之如何绘制一张图片

    Android 的音视频入门学习,首先了解一下绘制图片.在 Android 平台绘制一张图片,使用至少 3 种不同的 API,ImageView,SurfaceView,自定义 View作绘制图片.下 ...

  5. 音视频入门-10-使用libyuv对YUV数据进行缩放、旋转、镜像、裁剪、混合

    音视频入门文章目录 libyuv libyuv 是 Google 开源的实现各种 YUV 与 RGB 之间相互转换.旋转.缩放等的库.它是跨平台的,可在 Windows.Linux.Mac.Andro ...

  6. Android 音视频入门之音频采集、编码、播放

    今天我们学习音频的采集.编码.生成文件.转码等操作,我们生成三种格式的文件格式,pcm.wav.aac 三种格式,并且我们用 AudioStack 来播放音频,最后我们播放这个音频. 本篇文章你将学到 ...

  7. 音视频入门-11-PNG文件格式详解

    音视频入门文章目录 PNG 文件格式解析 PNG 图像格式文件由一个 8 字节的 PNG 文件署名域和 3 个以上的后续数据块(IHDR.IDAT.IEND)组成. PNG 文件包括 8 字节文件署名 ...

  8. 音视频入门系列-音视频基础知识篇(录播、点播、直播)

    在学习音视频技术前,笔者还是希望可以分享给小伙伴们一些音视频方便的基本概念,掌握这些概念,有助于大家对于音视频有一个更直观和清晰的了解. 话不多说,今天笔者给大家分享下:录播.点播和直播. 录播:录播 ...

  9. 音视频入门 (iOS上fdk-aac的交叉编译)

    前篇音视频入门记录了我对音视频的一些基础理解.现在将我在交叉编译ffmpeg之前的一些准备工作记录一下. 本文记录fdk-aac的交叉编译,首先需要说明几个问题: 基本概念 交叉编译:有过移动开发经验 ...

最新文章

  1. Mysql的日志那些事
  2. 35岁前十件事该做好
  3. 我也能做CTO之程序员职业规划
  4. poj Going from u to v or from v to u? 强联通缩点+拓扑排序(或搜索)
  5. 搭建Cent OS7服务器时需要注意的一些问题
  6. confusion中文_confusion
  7. 如何编辑PDF文件,教你几招轻松搞定
  8. Clean Code 《代码整洁之道》前四章读书笔记
  9. linux 提取重复数据处理,Linux提取命令cut
  10. python语言中函数在调用前必须先定义吗_应该在python中使用函数之前进行定义?...
  11. python封装成jar包_将Python代码打包为jar软件的简单方法
  12. 关于html引用css无法加载——新手踩的坑
  13. 准确生成电信、联通、移动IP地址段
  14. 南阳oj 28 大数阶乘
  15. 机器学习实践:非监督学习-8
  16. win7无线网显示小太阳
  17. Scala的类型擦除 和 TypeTags、Manifests的用法
  18. html 字体模糊,详解css3使用transform出现字体模糊的解决办法
  19. linux 学习之路
  20. Error creating document instance

热门文章

  1. NET 进阶--WebClient和WebRequest
  2. 机器学习--房屋销售的探索性数据分析
  3. 蓝库云|实体店搭建一套巡店管理系统,能让大型连锁店立竿见影
  4. stripe支付 旧版 charge付款方式 demo代码
  5. win10 java 配置环境变量
  6. 消防安全专项整治三年行动(2020--2022)
  7. 什么是 SAML 断言?
  8. FGD · 它是 vue-next 操作文件的“御用”库
  9. Pycharm配置Qt Designer教程
  10. 2020php就业前景如何,2020什么专业有前途 全面解析2020年最有前景的8大专业