制作数码相框

准备

1.开发板(支持帧缓冲'fb'的linux系统)
2.开发板需要的显示器
3.libjpeg源代码
4.jpeg.c源代码


我的环境

开发板:这里我使用的是安装了官方镜像的树莓派3b+
显示器:之前和树莓派一起买的HDMI支持触屏的小型显示器
libjpeg:我使用的版本为老师提供的jpegsrc.v9d.tar.gz libjpeg下载地址
jpeg.c:搜索下载或直接点击jpeg.c下载地址蓝奏云不支持c格式,故文件名改为jpeg.c.txt,使用前请自行修改文件名。
文件路径:将'jpegsrc.v9d.tar.gz'和'jpeg.c'统一放在'/home/c_project/img/',路径请保持一致或者自行修改Makefile。
如图所示:

安装libjpeg


# 切换为root
sudo -i
# 下载jpegsrc.v9d.tar.gz到'/home/c_project/img/'
# 解压
tar -xvf jpegsrc.v9d.tar.gz
# 进入解压文件夹
cd jpeg-9d
# 设置为交叉编译器(根据自己的系统环境选择,同时修改Makefile)
# 比如使用交叉编译器 arm-none-linux-gnueabi-gcc
./configure --host=arm-linux-gnueabihf
# 编译
make
# 安装
make install

编写代码

设计思路:
1.屏幕显示像素点
2.屏幕显示图片
3.图片全屏缩放
4.设计图片显示风格
5.设置循环播放
6.设置随机风格
过程省略,代码如下:


// 请命名为fb.c,或自行修改Makefile。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

unsigned char *decode_jpeg(char *filename, short *w, short *h);
int PicZoom(unsigned char *ptOriginPic_aucPixelDatas, unsigned int ptOriginPic_iWidth, unsigned int ptOriginPic_iHeight, unsigned char *ptZoomPic_aucPixelDatas, unsigned int ptZoomPic_iWidth, unsigned int ptZoomPic_iHeight);

int w, h, k;
int *fb_mem;
int style_id = 1;
int rand_len = 6;
// 注意:这里设置了6张图片1-6.jpg
// 同时对应使用6种显示风格(见main函数)
int rand_id[6] = {2, 5, 3, 6, 4, 1};

int init_fb()
{
  struct fb_var_screeninfo fb_var;
  int fd = open("/dev/fb0", O_RDWR);
  if (fd < 0)
  {
    printf("open fb0 error\n");
    return -1;
  }
  ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);
  w = fb_var.xres;
  h = fb_var.yres;
  k = fb_var.bits_per_pixel;
  //printf("framebuffer: %d * %d | %d\n", w, h ,k);
  fb_mem = mmap(0, w * h * k / 8, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  return 0;
}

void fb_point(int x, int y, int color)
{
  fb_mem[y * w + x] = color;
}

/**
 * 全屏缩放图片数据流
 */
unsigned char *full_jpeg_buf(char *filename, short *img_w, short *img_h)
{
  // ! new 1
  unsigned char *buf24 = decode_jpeg(filename, img_w, img_h);
  // ! new 2
  unsigned char *new_buf = malloc(w * h * 3);
  if (new_buf == NULL)
  {
    printf("malloc申请空间失败!\n");
    return NULL;
  }
  // 缩放图片
  if (PicZoom(buf24, *img_w, *img_h, new_buf, w, h))
  {
    printf("图片缩放失败!\n");
    return NULL;
  }
  // ! free 1
  free(buf24);
  buf24 = NULL;
  return new_buf;
}

/**
 * 图片全屏显示风格
 */
void full_show_style(unsigned char *ptr)
{
  unsigned char *tmp = ptr;
  int x, y, color;
  int i;
  if (style_id == 1)
  { // 纵向交叉
    for (y = 0; y < h; y++)
    {
      for (x = 0; x < w; x++)
      {
        if (x < w / 8 ||
            (x >= w * 2 / 8 && x < w * 3 / 8) ||
            (x >= w * 4 / 8 && x < w * 5 / 8) ||
            (x >= w * 6 / 8 && x < w * 7 / 8))
        {
          tmp = ptr + 3 * (y * w + x);
          color = (tmp[0] << 16) | (tmp[1] << 8) | tmp[2];
          fb_point(x, y, color);
        }
        else
        {
          tmp = ptr + 3 * ((h - y - 1) * w + x);
          color = (tmp[0] << 16) | (tmp[1] << 8) | tmp[2];
          fb_point(x, h - y - 1, color);
        }
      }
      usleep(3000);
    }
  }
  else if (style_id == 2)
  { // 横向交叉
    for (x = 0; x < w; x++)
    {
      for (y = 0; y < h; y++)
      {
        if (y < h / 8 ||
            (y >= h * 2 / 8 && y < h * 3 / 8) ||
            (y >= h * 4 / 8 && y < h * 5 / 8) ||
            (y >= h * 6 / 8 && y < h * 7 / 8))
        {
          tmp = ptr + 3 * (y * w + x);
          color = (tmp[0] << 16) | (tmp[1] << 8) | tmp[2];
          fb_point(x, y, color);
        }
        else
        {
          tmp = ptr + 3 * (y * w + w - x - 1);
          color = (tmp[0] << 16) | (tmp[1] << 8) | tmp[2];
          fb_point(w - x - 1, y, color);
        }
      }
      usleep(3000);
    }
  }
  else if (style_id == 3)
  { // 纵向百叶 y < h/8
    for (y = 0; y < h; y++)
    {
      for (x = 0; x < w; x++)
      {
        for (i = 0; i < 8; i++)
        {
          if (y + i * h / 8 < h)
          {
            tmp = ptr + 3 * ((y + i * h / 8) * w + x);
            color = (tmp[0] << 16) | (tmp[1] << 8) | tmp[2];
            fb_point(x, y + i * h / 8, color);
          }
          else
          {
            return;
          }
        }
      }
      usleep(8 * 3000);
    }
  }
  else if (style_id == 4)
  { // 横向百叶 x < w/8
    for (x = 0; x < w; x++)
    {
      for (y = 0; y < h; y++)
      {
        for (i = 0; i < 8; i++)
        {
          if (x + i * w / 8 < w)
          {
            tmp = ptr + 3 * (y * w + x + i * w / 8);
            color = (tmp[0] << 16) | (tmp[1] << 8) | tmp[2];
            fb_point(x + i * w / 8, y, color);
          }
          else
          {
            return;
          }
        }
      }
      usleep(8 * 3000);
    }
  }
  else if (style_id == 5)
  { // 纵向过渡交叉
    for (y = 0; y < h; y++)
    {
      for (x = 0; x < w; x++)
      { //2y
        if (2 * y < h)
        {
          tmp = ptr + 3 * (2 * y * w + x);
          color = (tmp[0] << 16) | (tmp[1] << 8) | tmp[2];
          fb_point(x, 2 * y, color);
        }
        //2y+1
        if (2 * y + 1 < h)
        {
          tmp = ptr + 3 * ((h - 2 * y - 1) * w + x);
          color = (tmp[0] << 16) | (tmp[1] << 8) | tmp[2];
          fb_point(x, h - 2 * y - 1, color);
        }
      }
      usleep(2 * 3000);
    }
  }
  else if (style_id == 6)
  { // 横向过渡交叉
    for (x = 0; x < w; x++)
    {
      for (y = 0; y < h; y++)
      {
        //2x
        if (2 * x < w)
        {
          tmp = ptr + 3 * (y * w + 2 * x);
          color = (tmp[0] << 16) | (tmp[1] << 8) | tmp[2];
          fb_point(2 * x, y, color);
        }
        //2x+1
        if (2 * x + 1 < w)
        {
          tmp = ptr + 3 * (y * w + w - 2 * x - 1);
          color = (tmp[0] << 16) | (tmp[1] << 8) | tmp[2];
          fb_point(w - 2 * x - 1, y, color);
        }
      }
      usleep(2 * 3000);
    }
  }
}

/**
 * 全屏输出图片
 */
void display_jpeg_full(char *filename)
{
  short img_w, img_h;
  unsigned char *buf24 = full_jpeg_buf(filename, &img_w, &img_h);
  full_show_style(buf24);
  // ! free 2
  free(buf24);
  buf24 = NULL;
}

/**
 * 内存拷贝函数
 */
void *my_memcpy(void *v_dst, const void *v_src, unsigned char c)
{
  const char *src = v_src;
  char *dst = v_dst;
  while (c--)
    *dst++ = *src++;
  return v_dst;
}

/**********************************************************************
* 函数名称:PicZoom(参考:https://blog.csdn.net/xiaolong1126626497/article/details/107944138)
* 功能描述:近邻取样插值算法缩放图片
*           !注意:分配内存存放缩放图片,用完后要用free释放
* 输入参数:ptOriginPic - 原始图片数据
*          ptZoomPic   - 缩放后的图片数据
***********************************************************************/
int PicZoom(unsigned char *ptOriginPic_aucPixelDatas, unsigned int ptOriginPic_iWidth, unsigned int ptOriginPic_iHeight, unsigned char *ptZoomPic_aucPixelDatas, unsigned int ptZoomPic_iWidth, unsigned int ptZoomPic_iHeight)
{
  unsigned int ptOriginPic_iLineBytes = ptOriginPic_iWidth * 3; //一行的字节数
  unsigned int ptZoomPic_iLineBytes = ptZoomPic_iWidth * 3;     //一行的字节数
  unsigned long dwDstWidth = ptZoomPic_iWidth;
  unsigned long *pdwSrcXTable;
  unsigned long x;
  unsigned long y;
  unsigned long dwSrcY;
  unsigned char *pucDest;
  unsigned char *pucSrc;
  unsigned long dwPixelBytes = 3; //像素字节
  pdwSrcXTable = malloc(sizeof(unsigned long) * dwDstWidth);
  if (NULL == pdwSrcXTable)
  {
    return -1;
  }
  for (x = 0; x < dwDstWidth; x++) //生成表 pdwSrcXTable
  {
    pdwSrcXTable[x] = (x * ptOriginPic_iWidth / ptZoomPic_iWidth);
  }
  for (y = 0; y < ptZoomPic_iHeight; y++)
  {
    dwSrcY = (y * ptOriginPic_iHeight / ptZoomPic_iHeight);
    pucDest = ptZoomPic_aucPixelDatas + y * ptZoomPic_iLineBytes;
    pucSrc = ptOriginPic_aucPixelDatas + dwSrcY * ptOriginPic_iLineBytes;
    for (x = 0; x < dwDstWidth; x++)
    {
      my_memcpy(pucDest + x * dwPixelBytes, pucSrc + pdwSrcXTable[x] * dwPixelBytes, dwPixelBytes);
    }
  }
  free(pdwSrcXTable);
  return 0;
}

/**
 * Int2String ( 10进制正整数 )
 * 参考:https://blog.csdn.net/nanfeibuyi/article/details/80811498
 */
char *Int2String(int num, char *str)
{
  int i = 0;
  do
  {
    str[i++] = num % 10 + 48;
    num /= 10;
  } while (num);
  str[i] = '\0';
  int j = 0;
  for (; j < i / 2; j++)
  {
    str[j] = str[j] + str[i - j - 1];
    str[i - j - 1] = str[j] - str[i - j - 1];
    str[j] = str[j] - str[i - j - 1];
  }
  return str;
}

/**
 * 交换数组元素模拟随机数
 */
void swap(int *arr, int l_id, int r_id)
{
  int max = rand_len - 1;
  if (l_id == r_id)
  {
    r_id = r_id == max ? 1 : r_id + 1;
  }
  int temp = arr[l_id];
  arr[l_id] = arr[r_id];
  arr[r_id] = temp;

  temp = arr[max - l_id];
  arr[max - l_id] = arr[max - r_id];
  arr[max - r_id] = temp;
}

int main(int argc, char *argv[])
{
  init_fb();
  int img_id = 1;
  while (1)
  {
    srand((unsigned)time(NULL));
    char filename[20] = {0};
    //img_id = rand() % 7 + 1; // 1.jpg - 7.jpg
    Int2String(img_id, filename);
    char *str2 = ".jpg";
    int len1 = strlen(filename);
    int len2;
    for (len2 = 0; len2 < 4; ++len2)
    {
      filename[len1 + len2] = str2[len2];
    }
    // 设置style 1-6
    // style_id = rand() % 6 + 1;
    style_id = rand_id[img_id-1];
    // printf("style_id:%d\n",style_id);
    
    display_jpeg_full(filename);
    
    if (++img_id == 7){
      img_id = 1;
      swap(rand_id, rand() % rand_len, rand() % rand_len);
    }
    sleep(3);
  }
  return 0;
}

编写Makefile编译脚本

注意:缩进必须使用tab,设置CC为系统对应的交叉编译器


CC=arm-linux-gnueabihf-gcc
CFLAGS=-I /home/c_project/img/jpeg-9d
LDFLAGS=-L /home/c_project/img/jpeg-9d/.libs

all:fb

fb:fb.o jpeg.o
	$(CC) -o fb fb.o jpeg.o $(LDFLAGS) -ljpeg -static
clean:
	rm fb *.o -rf

运行fb


# 编译全部生成fb
make fb
# 运行fb(运行前请确认目录含有1-6.jpg)
./fb
# 清除生成文件
make clean

效果演示

点赞

发表评论

电子邮件地址不会被公开。必填项已用 * 标注