zoukankan      html  css  js  c++  java
  • Delphi 实现照片抽奖-原创

    有单位年会要用照片抽奖,上网搜了几个都不满意,且居然还要收费。自己写一个算了。只是有一点不爽,Delphi 7 在 Windows 7 64位下有问题,不能双击 dpr 文件直接打开项目!

    关于性能:

    • 因为总数不大(没超过100个),所以一次性全部载入内存保存,启动速度也不慢,秒开。以流的形式保存,因为可直接使用 TJPEGImage 的 LoadFromStream 方法。如果照片很多,那就要掂量掂量内存占用情况了。实时读取文件的话,同时还要考虑磁盘读写的延时。
    • 图片分辨率对 JPG 的解压、显示的速度影响较大(i3 CPU、B75主板、8G内存):
      4288*2848——耗时 260ms
      1440*956——耗时 109ms
      1156*768——耗时 63ms
      因此,必须限制原始图片的分辨率,宁可放大显示。如果对显示性能要求较高,比如图片切换间隔要求小于100ms(不过短于视觉暂留时间的话就看不见了),必须别想他法。

    废话不说,上代码。

      1 unit main;
      2 
      3 interface
      4 
      5 uses
      6   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      7   Dialogs, StdCtrls, ExtCtrls, ComCtrls, Menus, Jpeg;
      8 
      9 type
     10   TMainForm = class(TForm)
     11     MainTimer: TTimer;
     12     PopMenu: TPopupMenu;
     13     MenuClear: TMenuItem;
     14     MainPaint: TPaintBox;
     15     ExitMenu: TMenuItem;
     16     procedure MainTimerTimer(Sender: TObject);
     17     procedure FormKeyPress(Sender: TObject; var Key: Char);
     18     procedure FormClose(Sender: TObject; var Action: TCloseAction);
     19     procedure FormCreate(Sender: TObject);
     20     procedure MenuClearClick(Sender: TObject);
     21     procedure MainPaintPaint(Sender: TObject);
     22     procedure ExitMenuClick(Sender: TObject);
     23   private
     24     { Private declarations }
     25     procedure ShowPhoto(paint:TPaintBox; src:TGraphic; const name:string);
     26   public
     27     { Public declarations }
     28   end;
     29 
     30 const
     31   BufferSize=64;              //缺省照片缓存大小
     32   CoverFileName='COVER.JPG';  //封面图片
     33   WinnerFileName='中奖.txt';  //抽奖结果文件
     34   
     35   TextColor=clRed;    //显示文字颜色
     36   TextSize=72;        //显示文字大小
     37   TextFont='华文行楷';//显示文字字体
     38 
     39 var
     40   MainForm: TMainForm;
     41   PhotoIndex:integer=0;     //当前显示的图片索引
     42   PhotoCount:integer=0;     //图片总数
     43   Names : array of string;  //图片名称缓存
     44   Photos : array of TMemoryStream; //JPG文件流缓存
     45   Selected : array of integer;  //已中奖图片标志
     46   SelectedCount : integer=0;    //已中奖数量,如果全部中奖则停止抽奖
     47   Log : TStringList;  //中奖记录,存入文本文件
     48 
     49   jpg:TJpegImage;   //解压JPG用的公用变量
     50   Times:Cardinal;   //定时器事件的执行次数
     51 
     52   bmpPaint:TBitmap; //作为PaintBox的显示缓存
     53 
     54 implementation
     55 
     56 {$R *.dfm}
     57 
     58 {
     59 procedure Mosaic(dest:TBitmap; src:TBitmap);
     60 var
     61   i,x,y:Integer;  
     62   from:TRect;
     63   bmpwidth,bmpheight:Integer;
     64 const  
     65   squ=20;
     66 begin  
     67   bmp=src.Width;
     68   bmpheight:=src.Height;
     69 
     70   dest.Width:=bmpwidth;
     71   dest.Height:=bmpHeight; 
     72 
     73   for i:=0 to 400 do
     74   begin
     75     Randomize;
     76     x:=Random(bmpwidth div squ);  
     77     y:=Random(bmpheight div squ);  
     78     from:=Rect(x*squ,y*squ,(x+1)*squ,(y+1)*squ);
     79     dest.Canvas.CopyRect(from,Src.Canvas,from);
     80   end;  
     81 end;
     82 
     83 procedure Alpha(bitmap:TBitmap; jpg:TJPEGImage);
     84 var
     85   BlendFunc: TBlendFunction;
     86   bit:TBitmap;
     87 begin
     88   bit := TBitMap.Create;
     89   try
     90     jpg.DIBNeeded;
     91     bit.Assign(jpg);
     92     BlendFunc.BlendOp := AC_SRC_OVER;
     93     BlendFunc.BlendFlags := 0;
     94     BlendFunc.AlphaFormat := 0;
     95     BlendFunc.SourceConstantAlpha := 127;
     96     windows.AlphaBlend(bitmap.Canvas.Handle, 0, 0, bit.Width, bit.Height,
     97                        bit.Canvas.Handle,  0, 0, bit.Width, bit.Height,
     98                        BlendFunc);
     99   finally
    100     bit.Free;
    101   end;
    102 end;
    103 }
    104 
    105 //源图等比缩放后填充目标图片,width、height指定可用显示区域的大小
    106 procedure ZoomFill(dest:TBitMap; src:TGraphic; width,Height:integer);
    107 var
    108   ZoomX,ZoomY,Zoom:double;
    109 begin
    110   zoomY:= Height / src.Height;
    111   zoomX:= Width / src.Width;
    112   // zoom 为 min(zoomX,zoomY)
    113   if (ZoomX<ZoomY) then
    114     zoom:= zoomX
    115   else
    116     zoom:=zoomY;
    117   dest.Width:= trunc(src.width*zoom);
    118   dest.Height:= trunc(src.Height*zoom);
    119   dest.Canvas.StretchDraw(rect(0, 0, dest.Width, dest.Height), src);
    120 end;
    121 
    122 // 显示图片,name指定了文本(固定居左、上下居中位置)
    123 procedure TMainForm.ShowPhoto(paint:TPaintBox; src:TGraphic; const name:string);
    124 begin
    125   if not src.Empty then
    126   begin
    127     ZoomFill(bmpPaint,src,screen.Width,screen.Height);
    128     if length(name)>0 then
    129     begin
    130       bmpPaint.Canvas.Brush.Style := bsClear;
    131       bmpPaint.Canvas.TextOut(
    132         10,
    133         (bmpPaint.Height-bmpPaint.Canvas.textheight(name)) div 2,
    134         name);
    135     end;
    136     paint.Repaint;
    137   end;
    138 end;
    139 
    140 //关闭 Form 时释放资源
    141 procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
    142 var
    143   i:integer;
    144 begin
    145   if MainTimer.Enabled then
    146     MainTimer.Enabled:=false;
    147 
    148   bmpPaint.Free;
    149   
    150   Log.SaveToFile(WinnerFileName);
    151   Log.Free;
    152   jpg.Free;
    153 
    154   for i:=0 to photocount-1 do
    155     Photos[i].Free;
    156 end;
    157 
    158 //创建 Form 时初始化资源
    159 procedure TMainForm.FormCreate(Sender: TObject);
    160 var   
    161   SearchRec:TSearchRec;
    162   found:integer;
    163   i:integer;
    164 begin
    165   // 开启双缓冲,减少屏幕闪烁
    166   if not Self.doubleBuffered then
    167     Self.doubleBuffered:=true;
    168 
    169   //初始化缓冲区
    170   setlength(Names,BufferSize);
    171   setlength(Photos,BufferSize);
    172   setlength(Selected,BufferSize);
    173 
    174   Log:=TStringList.Create;
    175   jpg:=TJpegImage.Create;
    176   
    177   bmpPaint:=tBitmap.create;
    178   BmpPaint.pixelformat := pf24bit;
    179   bmpPaint.Canvas.Font.Size:=textSize;
    180   bmpPaint.Canvas.Font.Color:=textColor;
    181   bmpPaint.Canvas.Font.Name:=TextFont;
    182 
    183   // 窗口全屏
    184   Self.BorderStyle := bsNone;
    185   Self.Left := 0;
    186   Self.Top := 0;
    187   Self.Width := Screen.Width;
    188   Self.Height := Screen.Height;
    189 
    190   // 载入封面图片
    191   try
    192     jpg.LoadFromFile(coverfilename);
    193     jpg.DIBNeeded;
    194   except
    195   end;
    196   ShowPhoto(MainPaint, jpg, '');
    197 
    198   // 载入 data 目录下的所有JPG文件
    199   found:=FindFirst('data*.jpg',faAnyFile,SearchRec);
    200   try
    201     while found=0 do
    202     begin
    203       if (SearchRec.Name<>'.')  and (SearchRec.Name<>'..')
    204            and (SearchRec.Attr<>faDirectory) then
    205       begin
    206         if (PhotoCount>=length(Names)) then  //内存缓冲长度不足
    207         begin
    208           setlength(Names,length(Names)*2);
    209           setlength(Photos,length(Names));
    210           setlength(Selected,length(Names));
    211         end;
    212         Names[PhotoCount]:= ChangeFileExt(SearchRec.Name,'');
    213         Photos[PhotoCount]:=TMemoryStream.Create;
    214         Photos[PhotoCount].LoadFromFile('data'+ SearchRec.Name);
    215         inc(PhotoCount);
    216       end;
    217       found:=FindNext(SearchRec);
    218     end;
    219   finally
    220     FindClose(SearchRec);
    221   end;
    222 
    223   //载入中奖纪录
    224   if fileexists(WinnerFileName) then
    225     log.LoadFromFile(WinnerFileName);
    226   if (log.Count>0) then //标记已中奖者
    227   begin
    228     for i:=0 to photoCount-1 do
    229       if log.IndexOf(names[i])>=0 then
    230       begin
    231         Selected[i]:=1;
    232         inc(selectedCount);
    233       end;
    234   end;
    235 
    236 end;
    237 
    238 //计时器事件
    239 procedure TMainForm.MainTimerTimer(Sender: TObject);
    240 var
    241   s:TMemoryStream;
    242 begin
    243   repeat
    244     Randomize;
    245     PhotoIndex:=random(photocount);
    246   until (Selected[photoIndex]<=0); //跳过已中奖的图片
    247   s:= Photos[PhotoIndex];
    248   jpg.LoadFromStream(s);
    249   s.Position:=0;  //这句必不可少。否则再读时不会报错,jpg.Empty不为空,但长度宽度均为0。
    250   showPhoto(MainPaint,jpg,Names[PhotoIndex]);
    251   inc(times);
    252   //逐渐加快图片滚动速度
    253   if (times>16) then
    254   begin
    255     if MainTimer.Interval>125 then
    256       MainTimer.Interval:=125;
    257   end
    258   else if times>8 then
    259     maintimer.Interval:=250
    260   else if times>3 then
    261     Maintimer.Interval:=500
    262   else
    263     MainTimer.Interval:=800;
    264 end;
    265 
    266 //按键处理
    267 procedure TMainForm.FormKeyPress(Sender: TObject; var Key: Char);
    268 begin
    269   if (Key=#27) then //Esc
    270   begin
    271     MainTimer.Enabled:=false;
    272     showmessage(Log.Text);
    273     close;
    274   end
    275   else  if (Key=' ') or (Key=#13) then
    276   begin
    277     if MainTimer.Enabled then //要停止滚动
    278     begin
    279       MainTimer.Enabled:=false;
    280       inc(SelectedCount);
    281       Selected[PhotoIndex]:=1;  //设置中奖标记
    282       Log.Append(Names[PhotoIndex]);
    283       Log.SaveToFile(WinnerFileName);
    284     end
    285     else
    286     begin //要开始滚动
    287       if SelectedCount<PhotoCount then  //还有未中奖
    288       begin
    289         times:=0;
    290         MainTimer.Enabled:=true;
    291       end
    292       else
    293         showmessage('全部人员均已抽中!');  
    294     end;
    295   end;
    296 end;
    297 
    298 //清除中奖纪录
    299 procedure TMainForm.MenuClearClick(Sender: TObject);
    300 var
    301   i:integer;
    302 begin
    303   if MessageDlg('真的要清除中奖记录么?',
    304     mtConfirmation, [mbYes, mbNo], 0) = mrYes then
    305   begin
    306     Log.Clear;
    307     SelectedCount:=0;
    308     for i:=0 to PhotoCount-1 do
    309       selected[i]:=0;
    310     if fileexists(WinnerFileName) then
    311       deletefile(WinnerFileName);
    312   end;
    313 end;
    314 
    315 //重绘 TPaintBox 事件
    316 procedure TMainForm.MainPaintPaint(Sender: TObject);
    317 begin
    318   with MainPaint.Canvas do
    319   begin
    320     pen.mode := pmcopy;
    321     brush.style := bssolid;
    322     copymode := srccopy;
    323     draw(
    324       (MainPaint.Width-bmpPaint.Width) div 2,   //左右居中
    325       (MainPaint.Height-bmpPaint.Height) div 2, //上下居中
    326       bmpPaint);
    327   end;
    328 end;
    329 
    330 procedure TMainForm.ExitMenuClick(Sender: TObject);
    331 begin
    332   close;
    333 end;
    334 
    335 end.

    可执行程序下载

  • 相关阅读:
    物联网解决方案
    热门研究方向
    LC滤波器简单设计法
    LC滤波电路分析,LC滤波电路原理及其时间常数的计算
    ams1117资料汇总
    杂项
    关于天线长度及LC值的计算
    稳压二极管、肖特基二极管、静电保护二极管、TVS管
    SPI、I2C、I2S、UART、GPIO、SDIO、CAN、JTAG的区别及使用方法。
    单片机串口通信电平不匹配的解决电路,5V 3.3V串口通讯
  • 原文地址:https://www.cnblogs.com/popapa/p/DrawLots.html
Copyright © 2011-2022 走看看