有单位年会要用照片抽奖,上网搜了几个都不满意,且居然还要收费。自己写一个算了。只是有一点不爽,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 bmpwidth:=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.
时间: 2024-10-29 12:38:33