Fork me on GitHub

【图像处理】使用SDL预览webp图片

写在前面的话

WebP是Google开发的一种图像格式,支持图像数据的有损和无损压缩。保留动画和alpha透明通道数据。

可以创建和JPEG、PNG和GIF图像格式在质量相同或质量更高,但是数据量更小的一种图像格式。

如下简单的分析一下webp图像格式,并使用sdl显示图片。

webp项目地址:https://github.com/webmproject/libwebp

sdl项目地址:https://libsdl.org/

webp格式图片说明

webp格式图像数据由两部分组成,RIFF头和图像负载信息。

 

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      'R'      |      'I'      |      'F'      |      'F'      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           File Size                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      'W'      |      'E'      |      'B'      |      'P'      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

 

RIFF头信息由21个字节组成。

0-3 四个字节是 RIFF 四个字符,表示 资源交换格式Resource Interchange File Format的简写。

4-7 四个字节是 WEBP文件的全部长度,这个长度包含RIFF

8-11 四个字节是 资源交换格式的名称,填WEBP这四个字符

12-15 四个字节是数据块Chunk的负载信息的编码格式,取值有VP8表示无损vp8压缩,VP8L表示有损vp8压缩

16-19 四个字节是有损压缩时的VP8数据负载信息的长度

20-以后数vp8格式的图像数据帧。

 

VP8格式的定义如下

struct VP8Io {
  // set by VP8GetHeaders()
  int width, height;         // picture dimensions, in pixels (invariable).
                             // These are the original, uncropped dimensions.
                             // The actual area passed to put() is stored
                             // in mb_w / mb_h fields.

  // set before calling put()
  int mb_y;                  // position of the current rows (in pixels)
  int mb_w;                  // number of columns in the sample
  int mb_h;                  // number of rows in the sample
  const uint8_t* y, *u, *v;  // rows to copy (in yuv420 format)
  int y_stride;              // row stride for luma
  int uv_stride;             // row stride for chroma
  const uint8_t* a;
};

VP8使用14位表示图像的宽和高,因此webp格式图像宽高最大是2^14 = 16384像素。

VP8的每一个像素的值是通过 左上top-left 上top 右上top-right 和左left这4个像素做预测得到。

预测的方法如下

  // L = left pixel, T = top pixel, TL = top left pixel.

  // ARGB component estimates for prediction.
  int pAlpha = ALPHA(L) + ALPHA(T) - ALPHA(TL);
  int pRed = RED(L) + RED(T) - RED(TL);
  int pGreen = GREEN(L) + GREEN(T) - GREEN(TL);
  int pBlue = BLUE(L) + BLUE(T) - BLUE(TL);

  // Manhattan distances to estimates for left and top pixels.
  int pL = abs(pAlpha - ALPHA(L)) + abs(pRed - RED(L)) +
           abs(pGreen - GREEN(L)) + abs(pBlue - BLUE(L));
  int pT = abs(pAlpha - ALPHA(T)) + abs(pRed - RED(T)) +
           abs(pGreen - GREEN(T)) + abs(pBlue - BLUE(T));

VP8的详细分析,后面增加一篇对应的文章分析。

 

环境准备

centos环境可以先安装png tiff jpeg和sdl

yum install libjpeg-devel
yum install libpng-devel
yum install libtiff-devel

osx系统可以使用brew命令按照对应的包

使用cmake编译webp项目后会生成如下小工具

cwebp 工具可以将其他格式图片转成webp图片

./cwebp -h
Usage:

   cwebp [options] -q quality input.png -o output.webp

where quality is between 0 (poor) to 100 (very good).
Typical value is around 80.

 

dwebp 工具可以将webp图片转成png jpg tiff ppm等格式

./dwebp -h
Usage: dwebp in_file [options] [-o out_file]

  -pam ......... save the raw RGBA samples as a color PAM
  -ppm ......... save the raw RGB samples as a color PPM
  -bmp ......... save as uncompressed BMP format
  -tiff ........ save as uncompressed TIFF format
  -pgm ......... save the raw YUV samples as a grayscale PGM
                 file with IMC4 layout
  -yuv ......... save the raw YUV samples in flat layout

vwebp_sdl 工具可以通过sdl显示webp图片

 

做SDL显示的一个例子

首先找一张png的图片,将这个图片转成webp格式的图像。

./cwebp leopard2.png -o  leopard2.webp

Saving file 'leopard2.webp'
File:      leopard2.png
Dimension: 842 x 1134
Output:    59610 bytes Y-U-V-All-PSNR 40.28 44.99 46.35   41.45 dB
           (0.50 bpp)
block count:  intra4:       2898  (77.01%)
              intra16:       865  (22.99%)
              skipped:         1  (0.03%)
bytes used:  header:            203  (0.3%)
             mode-partition:  13872  (23.3%)
 Residuals bytes  |segment 1|segment 2|segment 3|segment 4|  total
    macroblocks:  |      12%|      37%|      10%|      42%|    3763
      quantizer:  |      36 |      32 |      27 |      16 |
   filter level:  |      11 |       8 |      12 |      15 |

 

如下是png格式图片是webp格式图片大小的3.4倍,png格式的图像大小是1.4M,webp格式图像的大小是58k

1.4M  9  8 16:29 leopard2.png
58K  9  8 16:30 leopard2.webp

显示webp图片

./vwebp_sdl leopard2.webp

 

 

 

SDL图像显示原理

SDL库做图像显示时,渲染表面接收RGB或者YUV格式的图像数据。

因此,若显示webp格式的图像,需要将webp格式的图像转成RGB或者YUV格式,再将图像数据传递给SDL的显示表面实现显示效果。

注:其他格式的图片也是同样的做法,其他格式转成RGB或者YUV格式数据交给SDL显示表面。

 

显示逻辑如下:

 

 

 结合SDL显示图像的原理如下:

 

 

 

SDL显示webp的主要代码

int WebpToSDL(const char* data, unsigned int data_size) {
  int ok = 0;
  // 第一步 声明webp的配置和属性
  VP8StatusCode status;
  WebPDecoderConfig config;
  WebPBitstreamFeatures* const input = &config.input;
  WebPDecBuffer* const output = &config.output;

  SDL_Surface* screen = NULL;
  SDL_Surface* surface = NULL;
  // 第二步 初始化webp解码配置信息
  if (!WebPInitDecoderConfig(&config)) {
    fprintf(stderr, "Library version mismatch!\n");
    return 0;
  }

  if (!init_ok) {
    SDL_Init(SDL_INIT_VIDEO);
    init_ok = 1;
  }
  // 第三步 获取webp图像数据
  status = WebPGetFeatures((uint8_t*)data, (size_t)data_size, &config.input);
  if (status != VP8_STATUS_OK) goto Error;

  screen = SDL_SetVideoMode(input->width, input->height, 32, SDL_SWSURFACE);
  if (screen == NULL) {
    fprintf(stderr, "Unable to set video mode (32bpp %dx%d)!\n",
            input->width, input->height);
    goto Error;
  }

  surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
                                 input->width, input->height, 32,
                                 0x000000ffu,   // R mask
                                 0x0000ff00u,   // G mask
                                 0x00ff0000u,   // B mask
                                 0xff000000u);  // A mask

  if (surface == NULL) {
    fprintf(stderr, "Unable to create %dx%d RGBA surface!\n",
            input->width, input->height);
    goto Error;
  }
  if (SDL_MUSTLOCK(surface)) SDL_LockSurface(surface);

#if SDL_BYTEORDER == SDL_BIG_ENDIAN
  output->colorspace = MODE_BGRA;
#else
  output->colorspace = MODE_RGBA;
#endif
  output->width  = surface->w;
  output->height = surface->h;
  output->u.RGBA.rgba   = surface->pixels;
  output->u.RGBA.stride = surface->pitch;
  output->u.RGBA.size   = surface->pitch * surface->h;
  output->is_external_memory = 1;
  // 第四步 解码webp格式成rgb格式的图像数据
  status = WebPDecode((const uint8_t*)data, (size_t)data_size, &config);
  if (status != VP8_STATUS_OK) {
    fprintf(stderr, "Error decoding image (%d)\n", status);
    goto Error;
  }

  if (SDL_MUSTLOCK(surface)) SDL_UnlockSurface(surface);
  // 第五步 将rgb的图像数据显示到SDL的目标表面,实现显示
  if (SDL_BlitSurface(surface, NULL, screen, NULL) ||
      SDL_Flip(screen)) {
    goto Error;
  }

  ok = 1;
  
 Error:
  SDL_FreeSurface(surface);
  SDL_FreeSurface(screen);
  WebPFreeDecBuffer(output);
  return ok;
}

 

参考材料:

https://developers.google.cn/speed/webp/docs/riff_container

 

done.

祝玩的开心~

posted @ 2021-09-08 18:12  Mr.YF  阅读(631)  评论(0编辑  收藏  举报