服务报价 | 域名主机 | 网络营销 | 软件工具| [加入收藏]
 热线电话: #
当前位置: 主页 > php教程 > php教程 >

php转换图片为.bin文件

时间:2016-01-15 17:45来源:未知 作者:最模板 点击:
公司的打印机最近新添加一个打印图片的功能,具体是用户在后台设置他的logo,服务器接收到图片文件,转换单色的bmp文件(热敏打印机),再由php将单色bmp图片转换成bin文件,里面包

公司的打印机最近新添加一个打印图片的功能,具体是用户在后台设置他的logo,服务器接收到图片文件,转换单色的bmp文件(热敏打印机),再由php将单色bmp图片转换成bin文件,里面包含了图片的头信息和数据。做这么个小功能还花了我不少时间,走了很多弯路,各位看官请容我一一讲述吧。

思路1:使用PHP扩展用c语言来完成这个操作
最开始的时候,认为这个操作php语言无法胜任,于是选用了php extension。可是作为菜鸟的我,何曾玩过这么高大上的技能?没办法了,只好现学现卖了,最开始我以为是只要用c语言将转换的功能实现了,然后封装下就可以给php调用,做到后面才发现很多c语言的函数都不能直接使用,要么是被阉割掉了,要么就是被zend重写了,只有遵循zend_api才能够做出一个有用的php扩展。网上搜索了很多关于php extension的资料,发现中文资料还是相对较少。虽然有那么几个稍微系统点的资料:

PHP扩展开发及内核应用
这本书的是翻译的一本国外一个大牛的扩展开发的书籍,翻译的还是比较有水准的,这个翻译项目的发起人也是大牛,其中最为知名的应该是鸟哥,如果连鸟哥都不知道的话,你这php程序员估计也是白当了。
还有极客学院也有一份这本书,与上面介绍的差不多。还有这本深入理解php内核:Thinking In PHP Int ernals也不错。还有一些国外牛人整理的一些资料,先不再这里罗嗦,待会放在结尾当福利发送哈。
我的php extension第一步是先搭建环境,我也把搭建环境的步骤也讲下吧,这样也方便其他需要使用到扩展的童鞋。

1.下载php的完整版源码,我的是php-5.4.41版本的,因为公司的服务器就是使用的5.4.41的,不然版本不对,扩展就算开发好了,后面也是无法添加到php里的(可以兼容老版本的,但是开发版本不能比服务器版本高)。
2.将下载好的php源码解压放在某个地方,我的是放在主文件夹里(就是ubuntu的个人文件夹里)。

然后用cd命令跳转到php-5.4.41/ext目录下:

  cd /home/pchangl/php-5.4.41/ext

然后在主文件夹下创建一个extension_def的目录(这个目录不是必须的,只是我为了方便管理.def的文件才建立的),在这个文件夹下建立一个叫做pchangl.def的文件(def的文件是扩展方法的签名)。

这个时候建立好了def的文件,我们就开始使用php/ext目录下的ext_skel小工具为我们的扩展生成框架。

  root@pchangl:/home/pchangl/php-5.4.41/ext #ext_skel --extname=pchangl --proto=/home/pchangl/php-extension/pchangl.def

这个时候就给我们生成了扩展的框架:


这个时候我们的框架已经生成了,我们先把config.m4文件修改下,删除掉第16,17,18行前的dnl注释


这个时候我们就可以修改pchangl.c的代码了,这个文件里的代码就是我们自己的程序代码。

  #ifdef HAVE_CONFIG_H
  #include "config.h"
  #endif

  #include "php.h"
  #include "php_ini.h"
  #include "ext/standard/info.h"
  #include "php_pchangl.h"

  static int le_pchangl;
  const zend_function_entry pchangl_functions[] = {
      PHP_FE(confirm_pchangl_compiled,    NULL)       /* For testing, remove later. */
      PHP_FE_END  /* Must be the last line in pchangl_functions[] */
  };
  zend_module_entry pchangl_module_entry = {
    #if ZEND_MODULE_API_NO >= 20010901
      STANDARD_MODULE_HEADER,
    #endif
    "pchangl",
    pchangl_functions,
    PHP_MINIT(pchangl),
    PHP_MSHUTDOWN(pchangl),
    PHP_RINIT(pchangl),     /* Replace with NULL if there's nothing to do at request start */
    PHP_RSHUTDOWN(pchangl), /* Replace with NULL if there's nothing to do at request end */
    PHP_MINFO(pchangl),
    #if ZEND_MODULE_API_NO >= 20010901
      PHP_PCHANGL_VERSION,
    #endif
    STANDARD_MODULE_PROPERTIES
  };
  #ifdef COMPILE_DL_PCHANGL
    ZEND_GET_MODULE(pchangl)
  #endif
  PHP_MINIT_FUNCTION(pchangl)
  {
      return SUCCESS;
  }
  PHP_MSHUTDOWN_FUNCTION(pchangl)
  {
      return SUCCESS;
  }
  PHP_RINIT_FUNCTION(pchangl)
  {
      return SUCCESS;
  }
  PHP_RSHUTDOWN_FUNCTION(pchangl)
  {
      return SUCCESS;
  }
  PHP_MINFO_FUNCTION(pchangl)
  {
      php_info_print_table_start();
      php_info_print_table_header(2, "pchangl support", "enabled");
      php_info_print_table_end();
  }
  PHP_FUNCTION(confirm_pchangl_compiled)
  {
      char *arg = NULL;
      int arg_len, len;
      char *strg;
      if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) {
          return;
      }
      len = spprintf(&strg, 0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "pchangl", arg);
      RETURN_STRINGL(strg, len, 0);
  }

上面的这个代码是ext_skel工具生成的框架,你写的扩展也要按照他的标准来写,由于代码长度的原因,我删除掉了一部分生成的注释,如果想看的话,你可以自己编写个框架看看注视,下面我就讲解下几个比较重要的部分吧。

  PHP_MINIT_FUNCTION(pchangl)
  {
      return SUCCESS;
  }

这个方法随着php在apache或者nginx中启动而诞生内存的时候,就会执行,可以把这个方法当成初始化的方法吧。

  PHP_RINIT_FUNCTION(pchangl)
  {
      return SUCCESS;
  }

这个方法会在每一个页面请求到来的时候执行。

  PHP_RSHUTDOWN_FUNCTION(pchangl)
  {
      return SUCCESS;
  }

RINIT_FUNCTION方法执行玩后,请求也处理的差不多了,这个时候就会进入这个方法,启动回收程序来收拾烂摊子。

  PHP_MSHUTDOWN_FUNCTION(pchangl)
  {
      return SUCCESS;
  }

前面该启动的也启动了,该结束的也结束了,现在该apache老人家休息的时候,当apache通知php自己要stop的时候,就会进入这个方法。给扩展下最后通碟,如果有未了的心愿,就放在这个方法里,这可是最后的机会哦

  PHP_FUNCTION(function name)
  {
      //实现
  }

这个方法就是我们扩展方法的主体了。

pchangl.c这个文件就介绍到这里了,其他的内容就到我推荐的几个资料中查询吧,都有的。我也就不一一讲解了。

介绍完了环境的搭建,我就开始写自己的扩展,我们的bmp转bin的c代码是这样的:

  #include <stdio.h>
  #include <stdlib.h>
  #include <memory.h>

  //调试开关
  #define PRINTF_DEBUG 0

  //四个字节对齐  进1制处理
  #define WIDTHBYTES(bits)    (((bits)+31)/32*4)  //每一行的像素宽度必须是4的倍数,否则补0补齐。

  /*定义BYTE为无符号一个字节的类型*/
  typedef unsigned char   BYTE;
  /*定义WORD为无符号两个字节的类型*/
  typedef unsigned short  WORD;
  /*定义DWORD为无符号四个字节的类型*/
  typedef unsigned long   DWORD;

  #pragma pack(1)

  /*位图文件头*/
  typedef struct BMP_FILE_HEADER
  {
      WORD    bType;              /* 文件标识符 */
      DWORD   bSize;              /* 文件的大小 */
      WORD    bReserved1;         /* 保留值,必须设置为0 */
      WORD    bReserved2;         /* 保留值,必须设置为0 */
      DWORD   bOffset;            /* 文件头的最后到图像数据位开始的偏移量 */
  } BMPFILEHEADER;

  /*位图信息头*/
  typedef struct BMP_INFO
  {
      DWORD   bInfoSize;          /* 信息头的大小 */
      DWORD   bWidth;             /* 图像的宽度 */
      DWORD   bHeight;            /* 图像的高度 */
      WORD    bPlanes;            /* 图像的位面数 */
      WORD    bBitCount;          /* 每个像素的位数 */
      DWORD   bCompression;       /* 压缩类型 */
      DWORD   bmpImageSize;       /* 图像的大小,以字节为单位 */
      DWORD   bXPelsPerMeter;     /* 水平分辨率 */
      DWORD   bYPelsPerMeter;     /* 垂直分辨率 */
      DWORD   bClrUsed;           /* 使用的色彩数 */
      DWORD   bClrImportant;      /* 重要的颜色数 */
  } BMPINF;

  typedef struct tagRGBQUAD
  {
      BYTE    rgbBlue;            //蓝色的亮度(值范围为0-255)
      BYTE    rgbGreen;           //绿色的亮度(值范围为0-255)
      BYTE    rgbRed;             //红色的亮度(值范围为0-255)
      BYTE    rgbReserved;        //保留,必须为0
  } RGBQUAD;

  #pragma pack()

  int main()
  {
      FILE *fpin = NULL;
      FILE *fpout = NULL;
      BMPFILEHEADER fileHeader = { 0, 0 };
      BMPINF infoHeader = { 0, 0 };
      RGBQUAD imgHeader[256 * 4] = { 0, 0 };
      DWORD offset = 0, width = 0, height = 0, bitCount = 0;
      DWORD l_width = 0, bmp_width = 0, bmp_height = 0;
      DWORD i = 0, j = 0, m = 0;
      BYTE *bmpDataTmp = NULL;
      BYTE *bmpPixelTmp = NULL;
      BYTE bmp_start[8] = "ICONTOP";
      BYTE bmp_end[8] = "ICONEND";

      //打开文件
      if((fpin = fopen("logo.bmp", "rb")) == NULL)
      {
          printf("\r\nCann't open the file!\r\n");
  #if PRINTF_DEBUG
          while(1);
  #endif
          return 1; //错误,返回1
      }

      //把fpin指针移动到文件开头
      fseek(fpin, 0, 0);
      //获取文件头数据
      fread(&fileHeader, sizeof(fileHeader), 1, fpin);
      //获取信息头数据
      fread(&infoHeader, sizeof(infoHeader), 1, fpin);
      //计算并输出位图数据的偏移量,图像的宽度和高度,每个像素的位数
      offset = fileHeader.bOffset;
      width = infoHeader.bWidth;
      height = infoHeader.bHeight;
      bitCount = infoHeader.bBitCount;
  #if PRINTF_DEBUG
      printf("\r\noffset=%d, width=%d, height=%d, bitCount=%d\r\n", offset, width, height, bitCount);
  #endif
      l_width = WIDTHBYTES(width * bitCount); //每一行的像素宽度必须是4的倍数,否则补0补齐。

      //图象限制:单色位图,宽48字节内,尺寸1024 * 20字节内。
      if((bitCount == 1) && (((l_width + bitCount - 1) / bitCount) * bitCount / bitCount <= 48) && (((l_width + bitCount - 1) / bitCount) * bitCount / bitCount * height <= 1024 * 20))
      {
          if((bmpPixelTmp = (unsigned char *)malloc(sizeof(char) * (((l_width + bitCount - 1) / bitCount) * bitCount * height))) == NULL)
          {
              printf("\r\nData allocation failed!\r\n");
  #if PRINTF_DEBUG
              while(1);
  #endif
              return 1; //错误,返回1
          }

          memset(bmpPixelTmp, 0, sizeof(char) * (((l_width + bitCount - 1) / bitCount) * bitCount * height));
          //获取其他数据
          fread(imgHeader, sizeof(char), (offset - 54), fpin);
          //获取图像数据
          fread(bmpPixelTmp, sizeof(char), (l_width * height), fpin);
          //关闭文件
          fclose(fpin);

          if((bmpDataTmp = (unsigned char *)malloc(sizeof(char) * (((l_width + bitCount - 1) / bitCount) * bitCount * height / bitCount))) == NULL)
          {
              printf("\r\nData allocation failed!\r\n");
  #if PRINTF_DEBUG
              while(1);
  #endif
              return 1; //错误,返回1
          }

          memset(bmpDataTmp, 0, sizeof(char) * (((l_width + bitCount - 1) / bitCount) * bitCount * height / bitCount));

          /*********************************************图像数据纠正*********************************************/
          for(m = 0, i = 0; i < height; i++)
          {
              for(j = 0; j < l_width; j = j + 1)
              {
                  bmpDataTmp[m] =  ~bmpPixelTmp[(height - i - 1) * l_width + j + 0]; //图像纠正
                  m = m + 1;
              }
          }

          /**********************************************结  束**********************************************/
          /*********************************************图像数据填充*********************************************/
          for(m = 0, i = 0; i < height; i++)
          {
  #if PRINTF_DEBUG
              printf("\r\n");
  #endif

              for(j = 0; j < (((l_width + bitCount - 1) / bitCount)*bitCount / bitCount); j = j + 1)
              {
                  if(j == width / 8)
                  {
                      bmpDataTmp[m] = (bmpDataTmp[m]) & (~(0xff >> (width - width / 8 * 8))); //多余位填白
                  }
                  else if(j > width / 8)
                  {
                      bmpDataTmp[m] = 0x00; //多余字节填白
                  }
                  else
                  {
                      bmpDataTmp[m] =  bmpDataTmp[m]; //不处理
                  }

  #if PRINTF_DEBUG
                  printf("%d%d%d%d%d%d%d%d", (bmpDataTmp[m] & 0x80) >> 7, (bmpDataTmp[m] & 0x40) >> 6, (bmpDataTmp[m] & 0x20) >> 5, (bmpDataTmp[m] & 0x10) >> 4, (bmpDataTmp[m] & 0x08) >> 3, (bmpDataTmp[m] & 0x04) >> 2, (bmpDataTmp[m] & 0x02) >> 1, (bmpDataTmp[m] & 0x01) >> 0);
  #endif
                  m = m + 1;
              }
          }

          /**********************************************结  束**********************************************/
          /*****************************************数据写入BIN文件******************************************/
          //第01~07字节:命令头"ICONTOP"
          //第08~09字节:图片高bmp_height
          //第10~11字节:图片宽bmp_width
          //   其他字节:图像数据
          //最后7个字节:命令尾"ICONEND"
          /**************************************************************************************************/
          //打开文件
          if((fpout = fopen("logo.bin", "wb+")) == NULL)
                {
              printf("\r\nCann't open the file!\r\n");
  #if PRINTF_DEBUG
              while(1);
  #endif
              return 1; //错误,返回1
          }

          //把fpout指针移动到文件开头
          fseek(fpout, 0, 0);
          bmp_height = height;
          bmp_width = ((l_width + bitCount - 1) / bitCount) * bitCount / bitCount;
          //写入命令头"ICONTOP"
          fwrite(&bmp_start, 7, 1, fpout);
          //写入图片高
          fwrite(&bmp_height, 2, 1, fpout);
          //写入图片宽
          fwrite(&bmp_width, 2, 1, fpout);
          //写入图像数据
          fwrite(bmpDataTmp, (bmp_height * bmp_width), 1, fpout);
          //写入命令尾"ICONEND"
          fwrite(&bmp_end, 7, 1, fpout);
          //关闭文件
          fclose(fpout);
          /**********************************************结  束**********************************************/
          free(bmpDataTmp);
          bmpDataTmp = NULL;
          free(bmpPixelTmp);
          bmpPixelTmp = NULL;
      }
      else
      {
          //关闭文件
          fclose(fpin);
          printf("\r\nIt has high resolution for this picture!\r\n");
  #if PRINTF_DEBUG
          while(1);
  #endif
          return 1; //错误,返回1
      }

  #if PRINTF_DEBUG
      while(1);
  #endif
      return 0; //正确,返回0
  }
(责任编辑:最模板)
顶一下
(1)
100%
踩一下
(0)
0%
------分隔线----------------------------
栏目列表
热点内容