1. 程式人生 > >音視訊入門-20-BMP、PNG、JPG、GIF靜態圖生成GIF動態圖

音視訊入門-20-BMP、PNG、JPG、GIF靜態圖生成GIF動態圖

[* 音視訊入門文章目錄 *](https://www.ihubin.com/blog/audio-video-basic-catagory/) # 靜態圖 -> 動態圖 前面 [【18-手動生成一張GIF圖片】](https://www.ihubin.com/blog/audio-video-basic-18-generate-gif-by-hand/) 和 [【19-使用giflib處理GIF圖片】](https://www.ihubin.com/blog/audio-video-basic-19-giflib-handle-gif/) 生成的 GIF 每一幀都是一個顏色,平時用到的 GIF 每一幀都是圖片,下面就做一張每一幀都是圖片的 GIF。 準備了 4 張靜態圖 `.bmp` 、 `.png` 、 `.jpg` 、 `.gif`(靜態的GIF): | BMP | PNG | JPG | GIF | | :--: | :--: | :--: | :--: | | ![Android.bmp](https://cloudstorage.ihubin.com/blog/audio-video/blog-20/Android.bmp) | ![Huawei.png](https://cloudstorage.ihubin.com/blog/audio-video/blog-20/Huawei.png) | ![Fuchsia.jpg](https://cloudstorage.ihubin.com/blog/audio-video/blog-20/Fuchsia.jpg) | ![iOS.gif](https://cloudstorage.ihubin.com/blog/audio-video/blog-20/iOS.gif) | | [Android.bmp](https://cloudstorage.ihubin.com/blog/audio-video/blog-20/Android.bmp) | [Huawei.png](https://cloudstorage.ihubin.com/blog/audio-video/blog-20/Huawei.png) | [Fuchsia.jpg](https://cloudstorage.ihubin.com/blog/audio-video/blog-20/Fuchsia.jpg) | [iOS.gif](https://cloudstorage.ihubin.com/blog/audio-video/blog-20/iOS.gif) | 每張圖片顯示 1 秒,生成 GIF: ![image-to-gif-loop.gif](https://cloudstorage.ihubin.com/blog/audio-video/blog-20/image-to-gif-loop.gif) # image to RGB GIF 中使用 RGB 顏色索引來表示影象,每一幀影象最多 256 個顏色。所以第一步,要將靜態圖片轉成 RGB。 ## BMP to RGB 根據 [【05-RGB-TO-BMP使用開源庫】](https://www.ihubin.com/blog/audio-video-basic-05-rgb-to-bmp-library/) ,使用 libbmp 庫來完成 `.bmp` to RGB。 ```c 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圖片】](https://www.ihubin.com/blog/audio-video-basic-13-generate-png-with-library/) ,使用 libpng 庫來完成 `.png` to RGB。 ```c 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圖片】](https://www.ihubin.com/blog/audio-video-basic-16-libjpeg-turbo-handle-jpeg) ,使用 libjpeg-turbo 庫來完成 `.jpg` to RGB。 ```c 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圖片】](https://www.ihubin.com/blog/audio-video-basic-19-giflib-handle-gif/) ,使用 giflib 庫來完成 `.gif` to RGB。 ```c 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 ``` ![image-to-rgb.jpg](https://cloudstorage.ihubin.com/blog/audio-video/blog-20/image-to-rgb.jpg) # RGB to GIF 將靜態圖轉成 RGB 以後,就可以根據 [【19-使用giflib處理GIF圖片】](https://www.ihubin.com/blog/audio-video-basic-19-giflib-handle-gif/) 使用 giflib 將 RGB 編碼成 GIF 動態圖。 ## 完整步驟 ```c #