制作数码相框

准备

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.设置随机风格
7.添加汉字摘要(更新补充,需要HZK16文件)
过程省略,代码如下:


// 请命名为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;            //进程空间中帧缓冲的起始地址
unsigned char *hzk_mem; //进程空间中汉字字符集的起始地址
//需要输出的汉字的GBK编码,仅作示范1234321
unsigned char str1[7][2] = {{0xD2, 0xBC}, {0xB7, 0xA1}, {0xC8, 0xFE}, {0xCB, 0xC1}, {0xC8, 0xFE}, {0xB7, 0xA1}, {0xD2, 0xBC}};

int style_id = 1;
int rand_len = 6;
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", w, h ,k);
  fb_mem = mmap(0, w * h * k / 8, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  return 0;
}

int init_hzk()
{
  struct stat hzk_stat;
  int fd_hzk;
  fd_hzk = open("HZK16", O_RDONLY);
  if (fd_hzk < 0)
  {
    printf("open hzk error\n");
    return -1;
  }
  if (fstat(fd_hzk, &hzk_stat))
  {
    printf("cannot get hzkstat");
    return -1;
  }
  hzk_mem = mmap(0, hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk, 0); //申请汉字的映射空间
  return 0;
}

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

void alpha_point(int x, int y, int color, double a) //实现颜色的层叠,a (0,1) 表示背景的颜色深度 0为无背景
{
  int bcolor;
  int fr, fg, fb;
  int br, bg, bb;
  int ar, ag, ab;
  //get bcolor
  bcolor = fb_mem[y * w + x]; //获取(y,x)该点的背景颜色
  /*获取背景颜色的RGB分量*/
  br = (bcolor >> 16) & 0xff;
  bg = (bcolor >> 8) & 0xff;
  bb = (bcolor)&0xff;
  /*获取需要叠加颜色的RGB分量*/
  fr = (color >> 16) & 0xff;
  fg = (color >> 8) & 0xff;
  fb = (color)&0xff;
  ar = (int)(br * a + fr * (1 - a));
  ag = (int)(bg * a + fg * (1 - a));
  ab = (int)(bb * a + fb * (1 - a));
  fb_point(x, y, ab | (ag << 8) | (ar << 16)); //从新绘制该点
}

//在起始坐标(x,y)绘制粗体汉字(字符串),d为加粗的倍数(默认为2),w为放大的倍数(默认为2), a 为汉字的透明程度
void show_chineseDouble(int x, int y, unsigned char my_str[][2], int color, int d, int w, double a)
{
  int i;
  int X = x;
  int Y = y;
  for (i = 0; i < 7; i++)
  {
    unsigned int area = my_str[i][0] - 0xa1;                  //区号
    unsigned int where = my_str[i][1] - 0xa1;                 //位号
    unsigned char *dots = hzk_mem + (area * 94 + where) * 32; //基地址+偏移量
    unsigned char byte;                                       //一个汉字点阵占32个字节(16×16)的点阵
    int i, j, b, m, n;
    for (i = 0; i < 16; i++)
    { //点阵行
      for (j = 0; j < 2; j++)
      { //点阵列
        byte = dots[i * 2 + j];
        for (b = 7; b >= 0; b--)
        {
          if (byte & (1 << b))
          {
            /* show 加粗显示*/
            for (m = 0; m < d; m++)
            {
              for (n = 0; n < d; n++)
              {
                alpha_point((X + (7 - b + j * 8) * w) + m, (Y + i * w) + n, color, a); //以半透明的方式打印汉字
              }
            }
          }
        }
      }
    }
    X += 16 * w + 20; //按照行来显示,中间空20个pixs
    Y += 0;
  }
}
/**
 * 全屏缩放图片数据流
 */
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;
  unsigned char r, g, b;
  int x, y, color;
  int i, j;
  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
* 功能描述:近邻取样插值方法算法缩放图片
*          ! 注意:分配内存存放缩放图片,用完后要用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进制正整数 )
 */
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();
  init_hzk(); //加载汉字库
  int img_id = 1;
  int i;
  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);
    for (i = 0; i < 4; ++i)
    {
      filename[len1 + i] = str2[i];
    }
    // 设置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);
    //显示汉字摘要
    //show_chineseDouble(100, 100, str1, 0xff0000, 2, 2, 0.5);

    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

效果演示

点赞

发表评论

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