shadow.cs 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108
  1. using System;
  2. using System.IO;
  3. using System.Text;
  4. using System.Collections;
  5. using System.Windows.Forms;
  6. using System.Drawing;
  7. using System.Drawing.Imaging;
  8. using System.Drawing.Drawing2D;
  9. using System.Globalization;
  10. //////////////////////////////////////////////////////////////////////////////
  11. public class FastBitmap : IDisposable, ICloneable
  12. {
  13. internal Bitmap _bitmap;
  14. private BitmapData _bitmapData;
  15. public FastBitmap(Int32 width, Int32 height, PixelFormat fmt)
  16. {
  17. _bitmap = new Bitmap(width, height, fmt);
  18. }
  19. public FastBitmap(String filename)
  20. {
  21. _bitmap = new Bitmap(filename);
  22. }
  23. ~FastBitmap()
  24. {
  25. Dispose(false);
  26. }
  27. public void Dispose()
  28. {
  29. GC.SuppressFinalize(this);
  30. Dispose(true);
  31. }
  32. protected virtual void Dispose(Boolean disposing)
  33. {
  34. Unlock();
  35. if (disposing)
  36. {
  37. _bitmap.Dispose();
  38. }
  39. }
  40. private FastBitmap()
  41. {
  42. }
  43. public Object Clone()
  44. {
  45. FastBitmap clone = new FastBitmap();
  46. clone._bitmap = (Bitmap)_bitmap.Clone();
  47. return clone;
  48. }
  49. public Int32 Width
  50. {
  51. get { return _bitmap.Width; }
  52. }
  53. public Int32 Height
  54. {
  55. get { return _bitmap.Height; }
  56. }
  57. public void Lock()
  58. {
  59. _bitmapData = _bitmap.LockBits(
  60. new Rectangle(0, 0, _bitmap.Width, _bitmap.Height),
  61. ImageLockMode.ReadWrite,
  62. _bitmap.PixelFormat
  63. );
  64. }
  65. unsafe public Color GetPixel(Int32 x, Int32 y)
  66. {
  67. if (_bitmapData.PixelFormat == PixelFormat.Format32bppArgb)
  68. {
  69. Byte* b = (Byte*)_bitmapData.Scan0 + (y * _bitmapData.Stride) + (x * 4);
  70. return Color.FromArgb(*(b + 3), *(b + 2), *(b + 1), *b);
  71. }
  72. if (_bitmapData.PixelFormat == PixelFormat.Format24bppRgb)
  73. {
  74. Byte* b = (Byte*)_bitmapData.Scan0 + (y * _bitmapData.Stride) + (x * 3);
  75. return Color.FromArgb(*(b + 2), *(b + 1), *b);
  76. }
  77. return Color.Empty;
  78. }
  79. unsafe public void SetPixel(Int32 x, Int32 y, Color c)
  80. {
  81. if (_bitmapData.PixelFormat == PixelFormat.Format32bppArgb)
  82. {
  83. Byte* b = (Byte*)_bitmapData.Scan0 + (y * _bitmapData.Stride) + (x * 4);
  84. *b = c.B;
  85. *(b + 1) = c.G;
  86. *(b + 2) = c.R;
  87. *(b + 3) = c.A;
  88. }
  89. if (_bitmapData.PixelFormat == PixelFormat.Format24bppRgb)
  90. {
  91. Byte* b = (Byte*)_bitmapData.Scan0 + (y * _bitmapData.Stride) + (x * 3);
  92. *b = c.B;
  93. *(b + 1) = c.G;
  94. *(b + 2) = c.R;
  95. }
  96. }
  97. public void CopyTo(FastBitmap bitmap, Int32 destx, Int32 desty, Int32 srcx, Int32 srcy,
  98. Int32 width, Int32 height)
  99. {
  100. try
  101. {
  102. Lock();
  103. bitmap.Lock();
  104. for (Int32 y = 0; y < height; y++)
  105. {
  106. for (Int32 x = 0; x < width; x++)
  107. {
  108. Color c = GetPixel(srcx + x, srcy + y);
  109. bitmap.SetPixel(destx + x, desty + y, c);
  110. }
  111. }
  112. }
  113. finally
  114. {
  115. Unlock();
  116. bitmap.Unlock();
  117. }
  118. }
  119. public Byte GetIntensity(Int32 x, Int32 y)
  120. {
  121. Color c = GetPixel(x, y);
  122. return (Byte)((c.R * 0.30 + c.G * 0.59 + c.B * 0.11) + 0.5);
  123. }
  124. public void Unlock()
  125. {
  126. if (_bitmapData != null)
  127. {
  128. _bitmap.UnlockBits(_bitmapData);
  129. _bitmapData = null;
  130. }
  131. }
  132. public void Save(String filename, ImageFormat format)
  133. {
  134. _bitmap.Save(filename, format);
  135. }
  136. public void Save(String filename)
  137. {
  138. _bitmap.Save(filename);
  139. }
  140. }
  141. //////////////////////////////////////////////////////////////////////////////
  142. class Layer : ICloneable
  143. {
  144. internal FastBitmap _bitmap;
  145. private FastBitmap _mask;
  146. public Double _opacity;
  147. private Int32 _offsetx, _offsety;
  148. public Layer(Int32 width, Int32 height)
  149. {
  150. _bitmap = new FastBitmap(width, height, PixelFormat.Format32bppArgb);
  151. Clear(Color.Transparent);
  152. _opacity = 1.0;
  153. }
  154. public Layer(FastBitmap bitmap)
  155. {
  156. _bitmap = bitmap;
  157. _opacity = 1.0;
  158. }
  159. public Double Opacity
  160. {
  161. get { return _opacity; }
  162. set { _opacity = value; }
  163. }
  164. public FastBitmap Bitmap
  165. {
  166. get { return _bitmap; }
  167. }
  168. public FastBitmap Mask
  169. {
  170. get { return _mask; }
  171. set { _mask = value; }
  172. }
  173. public Int32 OffsetX
  174. {
  175. get { return _offsetx; }
  176. set { _offsetx = value; }
  177. }
  178. public Int32 OffsetY
  179. {
  180. get { return _offsety; }
  181. set { _offsety = value; }
  182. }
  183. public Object Clone()
  184. {
  185. Layer clone = new Layer(_bitmap.Width, _bitmap.Height);
  186. clone._bitmap = (FastBitmap)_bitmap.Clone();
  187. return clone;
  188. }
  189. public void Clear(Color c)
  190. {
  191. _bitmap.Lock();
  192. for (Int32 y = 0; y < _bitmap.Height; y++)
  193. for (Int32 x = 0; x < _bitmap.Width; x++)
  194. _bitmap.SetPixel(x, y, c);
  195. _bitmap.Unlock();
  196. }
  197. public void DrawText(Int32 x, Int32 y, String text, Font font,
  198. Brush brush)
  199. {
  200. Graphics g = Graphics.FromImage(_bitmap._bitmap);
  201. g.DrawString(text, font, brush, x, y, StringFormat.GenericTypographic);
  202. g.Dispose();
  203. }
  204. public void FillRectangle(Int32 x, Int32 y, Int32 width,
  205. Int32 height, Brush brush)
  206. {
  207. Graphics g = Graphics.FromImage(_bitmap._bitmap);
  208. g.FillRectangle(brush, x, y, width, height);
  209. g.Dispose();
  210. }
  211. public Color GetPixel(Int32 x, Int32 y)
  212. {
  213. return _bitmap.GetPixel(x, y);
  214. }
  215. public void Invert()
  216. {
  217. _bitmap.Lock();
  218. for (Int32 y = 0; y < _bitmap.Height; y++)
  219. {
  220. for (Int32 x = 0; x < _bitmap.Width; x++)
  221. {
  222. Color c = _bitmap.GetPixel(x, y);
  223. _bitmap.SetPixel(x, y, Color.FromArgb(c.A,
  224. 255 - c.R, 255 - c.G, 255 - c.B));
  225. }
  226. }
  227. _bitmap.Unlock();
  228. }
  229. private Single Gauss(Single x, Single middle, Single width)
  230. {
  231. if (width == 0)
  232. return 1F;
  233. Double t = -(1.0 / width) * ((middle - x) * (middle - x));
  234. return (Single)Math.Pow(/*Math.E*/1.5, t);
  235. }
  236. public void Blur(Int32 horz, Int32 vert)
  237. {
  238. Single weightsum;
  239. Single[] weights;
  240. FastBitmap t = (FastBitmap)_bitmap.Clone();
  241. _bitmap.Lock();
  242. t.Lock();
  243. // horizontal blur
  244. weights = new Single[horz * 2 + 1];
  245. for (Int32 i = 0; i < horz * 2 + 1; i++)
  246. {
  247. Single y = Gauss(-horz + i, 0, horz);
  248. weights[i] = y;
  249. }
  250. for (Int32 row = 0; row < _bitmap.Height; row++)
  251. {
  252. for (Int32 col = 0; col < _bitmap.Width; col++)
  253. {
  254. Double r = 0;
  255. Double g = 0;
  256. Double b = 0;
  257. Double a = 0;
  258. weightsum = 0;
  259. for (Int32 i = 0; i < horz * 2 + 1; i++)
  260. {
  261. Int32 x = col - horz + i;
  262. if (x < 0)
  263. {
  264. i += -x;
  265. x = 0;
  266. }
  267. if (x > _bitmap.Width - 1)
  268. break;
  269. Color c = _bitmap.GetPixel(x, row);
  270. r += c.R * weights[i] / 255.0 * c.A;
  271. g += c.G * weights[i] / 255.0 * c.A;
  272. b += c.B * weights[i] / 255.0 * c.A;
  273. a += c.A * weights[i];
  274. weightsum += weights[i];
  275. }
  276. r /= weightsum;
  277. g /= weightsum;
  278. b /= weightsum;
  279. a /= weightsum;
  280. Byte br = (Byte)Math.Round(r);
  281. Byte bg = (Byte)Math.Round(g);
  282. Byte bb = (Byte)Math.Round(b);
  283. Byte ba = (Byte)Math.Round(a);
  284. if (br > 255) br = 255;
  285. if (bg > 255) bg = 255;
  286. if (bb > 255) bb = 255;
  287. if (ba > 255) ba = 255;
  288. t.SetPixel(col, row, Color.FromArgb(ba, br, bg, bb));
  289. }
  290. }
  291. // vertical blur
  292. weights = new Single[vert * 2 + 1];
  293. for (Int32 i = 0; i < vert * 2 + 1; i++)
  294. {
  295. Single y = Gauss(-vert + i, 0, vert);
  296. weights[i] = y;
  297. }
  298. for (Int32 col = 0; col < _bitmap.Width; col++)
  299. {
  300. for (Int32 row = 0; row < _bitmap.Height; row++)
  301. {
  302. Double r = 0;
  303. Double g = 0;
  304. Double b = 0;
  305. Double a = 0;
  306. weightsum = 0;
  307. for (Int32 i = 0; i < vert * 2 + 1; i++)
  308. {
  309. Int32 y = row - vert + i;
  310. if (y < 0)
  311. {
  312. i += -y;
  313. y = 0;
  314. }
  315. if (y > _bitmap.Height - 1)
  316. break;
  317. Color c = t.GetPixel(col, y);
  318. r += c.R * weights[i] / 255.0 * c.A;
  319. g += c.G * weights[i] / 255.0 * c.A;
  320. b += c.B * weights[i] / 255.0 * c.A;
  321. a += c.A * weights[i];
  322. weightsum += weights[i];
  323. }
  324. r /= weightsum;
  325. g /= weightsum;
  326. b /= weightsum;
  327. a /= weightsum;
  328. Byte br = (Byte)Math.Round(r);
  329. Byte bg = (Byte)Math.Round(g);
  330. Byte bb = (Byte)Math.Round(b);
  331. Byte ba = (Byte)Math.Round(a);
  332. if (br > 255) br = 255;
  333. if (bg > 255) bg = 255;
  334. if (bb > 255) bb = 255;
  335. if (ba > 255) ba = 255;
  336. _bitmap.SetPixel(col, row, Color.FromArgb(ba, br, bg, bb));
  337. }
  338. }
  339. t.Dispose(); // will unlock
  340. _bitmap.Unlock();
  341. }
  342. private Byte GetBumpMapPixel(FastBitmap bmp, Int32 x, Int32 y)
  343. {
  344. // create a single number from R, G and B values at point (x, y)
  345. // if point (x, y) lays outside the bitmap, GetBumpMapPixel()
  346. // returns the closest pixel within the bitmap
  347. if (x < 0)
  348. x = 0;
  349. if (x > _bitmap.Width - 1)
  350. x = _bitmap.Width - 1;
  351. if (y < 0)
  352. y = 0;
  353. if (y > _bitmap.Height - 1)
  354. y = _bitmap.Height - 1;
  355. return bmp.GetIntensity(x, y);
  356. }
  357. public void BumpMap(Layer bumpmap, Int32 azimuth, Int32 elevation,
  358. Int32 bevelwidth, Boolean lightzalways1)
  359. {
  360. bumpmap._bitmap.Lock();
  361. _bitmap.Lock();
  362. for (Int32 row = 0; row < _bitmap.Height; row++)
  363. {
  364. for (Int32 col = 0; col < _bitmap.Width; col++)
  365. {
  366. // calculate normal for point (col, row)
  367. // this is an approximation of the derivative
  368. // I personally haven't figured out why it's
  369. // the way it is
  370. // normal_z is constant, I think this means
  371. // the longer the vector is, the more it lays
  372. // in the xy plane
  373. Byte[] x = new Byte[6];
  374. x[0] = GetBumpMapPixel(bumpmap._bitmap, col - 1, row - 1);
  375. x[1] = GetBumpMapPixel(bumpmap._bitmap, col - 1, row - 0);
  376. x[2] = GetBumpMapPixel(bumpmap._bitmap, col - 1, row + 1);
  377. x[3] = GetBumpMapPixel(bumpmap._bitmap, col + 1, row - 1);
  378. x[4] = GetBumpMapPixel(bumpmap._bitmap, col + 1, row - 0);
  379. x[5] = GetBumpMapPixel(bumpmap._bitmap, col + 1, row + 1);
  380. Single normal_x = x[0] + x[1] + x[2] - x[3] - x[4] - x[5];
  381. x[0] = GetBumpMapPixel(bumpmap._bitmap, col - 1, row + 1);
  382. x[1] = GetBumpMapPixel(bumpmap._bitmap, col - 0, row + 1);
  383. x[2] = GetBumpMapPixel(bumpmap._bitmap, col + 1, row + 1);
  384. x[3] = GetBumpMapPixel(bumpmap._bitmap, col - 1, row - 1);
  385. x[4] = GetBumpMapPixel(bumpmap._bitmap, col - 0, row - 1);
  386. x[5] = GetBumpMapPixel(bumpmap._bitmap, col + 1, row - 1);
  387. Single normal_y = x[0] + x[1] + x[2] - x[3] - x[4] - x[5];
  388. Single normal_z = (6F * 255F) / bevelwidth;
  389. Single length = (Single)Math.Sqrt(
  390. normal_x * normal_x +
  391. normal_y * normal_y +
  392. normal_z * normal_z);
  393. if (length != 0)
  394. {
  395. normal_x /= length;
  396. normal_y /= length;
  397. normal_z /= length;
  398. }
  399. // convert to radians
  400. Double azimuth_rad = azimuth / 180.0 * Math.PI;
  401. Double elevation_rad = elevation / 180.0 * Math.PI;
  402. // light vector -- this is the location of the light
  403. // source but it also serves as the direction with
  404. // origin <0, 0, 0>
  405. // the formulas to calculate x, y and z are those to
  406. // rotate a point in 3D space
  407. // if lightzalways1 is true, light_z is set to 1
  408. // because we want full color intensity for that pixel;
  409. // if we set light_z to (Single)Math.Sin(elevation_rad),
  410. // which is the correct way to calculate light_z, the
  411. // color is more dark, but when we ignore light_z, the
  412. // light source is straight above the pixel and
  413. // therefore sin(elevation_rad) is always 1
  414. Single light_x = (Single)(Math.Cos(azimuth_rad) *
  415. Math.Cos(elevation_rad));
  416. Single light_y = (Single)(Math.Sin(azimuth_rad) *
  417. Math.Cos(elevation_rad));
  418. Single light_z = 1F;
  419. if (!lightzalways1)
  420. light_z = (Single)Math.Sin(elevation_rad);
  421. // the normal and light vector are unit vectors, so
  422. // taking the dot product of these two yields the
  423. // cosine of the angle between them
  424. Single cos_light_normal = 0;
  425. cos_light_normal += normal_x * light_x;
  426. cos_light_normal += normal_y * light_y;
  427. cos_light_normal += normal_z * light_z;
  428. // get pixel (col, row) of this layer, calculate color
  429. // and set pixel back with new color
  430. Color c = _bitmap.GetPixel(col, row);
  431. Single r = c.R;
  432. Single g = c.G;
  433. Single b = c.B;
  434. r *= cos_light_normal;
  435. g *= cos_light_normal;
  436. b *= cos_light_normal;
  437. Byte red = (Byte)Math.Min(Math.Round(r), 255);
  438. Byte green = (Byte)Math.Min(Math.Round(g), 255);
  439. Byte blue = (Byte)Math.Min(Math.Round(b), 255);
  440. _bitmap.SetPixel(col, row, Color.FromArgb(red, green, blue));
  441. }
  442. }
  443. _bitmap.Unlock();
  444. bumpmap._bitmap.Unlock();
  445. }
  446. }
  447. //////////////////////////////////////////////////////////////////////////////
  448. class Layers
  449. {
  450. LayeredImage _image;
  451. ArrayList _layers = new ArrayList();
  452. public Layers(LayeredImage image)
  453. {
  454. _image = image;
  455. }
  456. public Int32 Count
  457. {
  458. get { return _layers.Count; }
  459. }
  460. public Layer Add()
  461. {
  462. Layer layer = new Layer(_image.Width, _image.Height);
  463. _layers.Add(layer);
  464. return layer;
  465. }
  466. public Layer Add(FastBitmap bitmap)
  467. {
  468. Layer layer = new Layer(bitmap);
  469. _layers.Add(layer);
  470. return layer;
  471. }
  472. public Layer Add(Int32 width, Int32 height)
  473. {
  474. Layer layer = new Layer(width, height);
  475. _layers.Add(layer);
  476. return layer;
  477. }
  478. public Layer Copy(Layer layer)
  479. {
  480. Layer copy = (Layer)layer.Clone();
  481. _layers.Add(copy);
  482. return copy;
  483. }
  484. public Layer this[Int32 i]
  485. {
  486. get { return (Layer)_layers[i]; }
  487. }
  488. }
  489. //////////////////////////////////////////////////////////////////////////////
  490. class LayeredImage
  491. {
  492. Int32 _width, _height;
  493. Bitmap _checkerboard;
  494. Layers _layers;
  495. public LayeredImage(Int32 width, Int32 height)
  496. {
  497. _width = width;
  498. _height = height;
  499. _layers = new Layers(this);
  500. // checker board brush
  501. _checkerboard = new Bitmap(32, 32, PixelFormat.Format24bppRgb);
  502. Color darkgray = Color.FromArgb(102, 102, 102);
  503. Color lightgray = Color.FromArgb(153, 153, 153);
  504. for (Int32 i = 0; i < 32; i++)
  505. {
  506. for (Int32 j = 0; j < 32; j++)
  507. {
  508. if ((i < 16 && j < 16) || (i >= 16 && j >= 16))
  509. _checkerboard.SetPixel(j, i, lightgray);
  510. else
  511. _checkerboard.SetPixel(j, i, darkgray);
  512. }
  513. }
  514. // background layer
  515. Layer bglayer = _layers.Add();
  516. Graphics g = Graphics.FromImage(bglayer._bitmap._bitmap);
  517. TextureBrush brush = new TextureBrush(_checkerboard);
  518. g.FillRectangle(brush, 0, 0, _width, _height);
  519. brush.Dispose();
  520. g.Dispose();
  521. }
  522. public Int32 Width
  523. {
  524. get { return _width; }
  525. }
  526. public Int32 Height
  527. {
  528. get { return _height; }
  529. }
  530. public Layers Layers
  531. {
  532. get { return _layers; }
  533. }
  534. internal FastBitmap Flatten()
  535. {
  536. // create a bitmap for result image
  537. FastBitmap final = new FastBitmap(_width, _height,
  538. PixelFormat.Format24bppRgb);
  539. // lock all bitmaps
  540. final.Lock();
  541. for (Int32 i = 0; i < _layers.Count; i++)
  542. {
  543. Layer l = _layers[i];
  544. l._bitmap.Lock();
  545. if (l.Mask != null)
  546. l.Mask.Lock();
  547. }
  548. // calculate colors of flattened image
  549. // 1. take offsetx, offsety into consideration
  550. // 2. calculate alpha of color (alpha, opacity, mask)
  551. // 3. mix colors of current layer and layer below
  552. for (Int32 y = 0; y < _height; y++)
  553. {
  554. for (Int32 x = 0; x < _width; x++)
  555. {
  556. Color c0 = _layers[0]._bitmap.GetPixel(x, y);
  557. for (Int32 i = 1; i < _layers.Count; i++)
  558. {
  559. Layer layer = _layers[i];
  560. Color c1 = Color.Transparent;
  561. if (x >= layer.OffsetX &&
  562. x <= layer.OffsetX + layer._bitmap.Width - 1 &&
  563. y >= layer.OffsetY &&
  564. y <= layer.OffsetY + layer._bitmap.Height - 1)
  565. {
  566. c1 = layer._bitmap.GetPixel(x - layer.OffsetX,
  567. y - layer.OffsetY);
  568. }
  569. if (c1.A == 255 && layer.Opacity == 1.0 &&
  570. layer.Mask == null)
  571. {
  572. c0 = c1;
  573. }
  574. else
  575. {
  576. Double tr, tg, tb, a;
  577. a = c1.A / 255.0 * layer.Opacity;
  578. if (layer.Mask != null)
  579. {
  580. a *= layer.Mask.GetIntensity(x, y) / 255.0;
  581. }
  582. tr = c1.R * a + c0.R * (1.0 - a);
  583. tg = c1.G * a + c0.G * (1.0 - a);
  584. tb = c1.B * a + c0.B * (1.0 - a);
  585. tr = Math.Round(tr);
  586. tg = Math.Round(tg);
  587. tb = Math.Round(tb);
  588. tr = Math.Min(tr, 255);
  589. tg = Math.Min(tg, 255);
  590. tb = Math.Min(tb, 255);
  591. c0 = Color.FromArgb((Byte)tr, (Byte)tg, (Byte)tb);
  592. }
  593. }
  594. final.SetPixel(x, y, c0);
  595. }
  596. }
  597. // unlock all bitmaps
  598. for (Int32 i = 0; i < _layers.Count; i++)
  599. {
  600. Layer l = _layers[i];
  601. l._bitmap.Unlock();
  602. if (l.Mask != null)
  603. l.Mask.Unlock();
  604. }
  605. final.Unlock();
  606. return final;
  607. }
  608. }
  609. //////////////////////////////////////////////////////////////////////////////
  610. class Shadow
  611. {
  612. static void PrintUsage()
  613. {
  614. Console.WriteLine("Shadow 1.0 for .NET http://home.tiscali.be/zoetrope/");
  615. Console.WriteLine("Syntax is: SHADOW [ options ] inpfile [ outfile ]");
  616. Console.WriteLine(" -s:xx Shadow width (default = 8)");
  617. Console.WriteLine(" -b:xx Border width (default = 0)");
  618. Console.WriteLine(" -m:xx Margin (default = 8)");
  619. Console.WriteLine(" -r:x Shadow direction (default = 0)");
  620. Console.WriteLine(" -t:N Shadow transparency (default = 0.0)");
  621. Console.WriteLine(" -a:xxxxxx Background color (default = FFFFFF)");
  622. Console.WriteLine(" -c:xxxxxx Shadow color (default = 000000)");
  623. Console.WriteLine(" -d:xxxxxx Border color (default = 000000)");
  624. Console.WriteLine(" -n No soft shadow");
  625. }
  626. private static Color HexColorToColor(String s)
  627. {
  628. if (s.Length != 6)
  629. return Color.Empty;
  630. UInt32 r, g, b;
  631. r = g = b = 0;
  632. for (Int32 i = 0; i < 6; i++)
  633. {
  634. Int32 n = "0123456789ABCDEF".IndexOf(Char.ToUpper(s[i]));
  635. if (n == -1)
  636. return Color.Empty;
  637. UInt32 x = (UInt32)n;
  638. switch (i)
  639. {
  640. case 0:
  641. r |= x << 4;
  642. break;
  643. case 1:
  644. r |= x;
  645. break;
  646. case 2:
  647. g |= x << 4;
  648. break;
  649. case 3:
  650. g |= x;
  651. break;
  652. case 4:
  653. b |= x << 4;
  654. break;
  655. case 5:
  656. b |= x;
  657. break;
  658. }
  659. }
  660. return Color.FromArgb((Byte)r, (Byte)g, (Byte)b);
  661. }
  662. private static Boolean GetArgParam(String arg, out Int32 num)
  663. {
  664. num = 0;
  665. if (arg.Length < 4)
  666. return false;
  667. String[] str = arg.Split(new Char[] { ':' }, 2);
  668. if (str.Length != 2)
  669. return false;
  670. try
  671. {
  672. num = Convert.ToInt32(str[1]);
  673. return true;
  674. }
  675. catch
  676. {
  677. // catch all exceptions Convert.ToInt32() can throw;
  678. // no other exceptions can be thrown
  679. return false;
  680. }
  681. }
  682. private static Boolean GetArgParam(String arg, out Double num)
  683. {
  684. num = 0.0;
  685. if (arg.Length < 4)
  686. return false;
  687. String[] str = arg.Split(new Char[] { ':' }, 2);
  688. if (str.Length != 2)
  689. return false;
  690. try
  691. {
  692. num = Convert.ToDouble(str[1], CultureInfo.InvariantCulture);
  693. return true;
  694. }
  695. catch
  696. {
  697. // catch all exceptions Convert.ToInt32() can throw;
  698. // no other exceptions can be thrown
  699. return false;
  700. }
  701. }
  702. private static Boolean GetArgParam(String arg, out Color col)
  703. {
  704. col = Color.Empty;
  705. if (arg.Length != 9)
  706. return false;
  707. String[] str = arg.Split(new Char[] { ':' }, 2);
  708. if (str.Length != 2)
  709. return false;
  710. col = HexColorToColor(str[1]);
  711. return (col != Color.Empty);
  712. }
  713. static void Main(String[] args)
  714. {
  715. if (args.Length == 0)
  716. {
  717. PrintUsage();
  718. return;
  719. }
  720. Int32 shadowwidth = 8;
  721. Int32 borderwidth = 0;
  722. Int32 margin = shadowwidth;
  723. Int32 shadowdir = 0;
  724. Double shadowtrans = 0.0;
  725. Color bkcolor = Color.FromArgb(255, 255, 255);
  726. Color shadowcolor = Color.FromArgb(0, 0, 0);
  727. Color bordercolor = Color.FromArgb(0, 0, 0);
  728. Boolean softshadow = true;
  729. String inputfile = "";
  730. String outputfile = "";
  731. for (Int32 i = 0; i < args.Length; i++)
  732. {
  733. if (args[i].Length == 0)
  734. {
  735. PrintUsage();
  736. return;
  737. }
  738. if (args[i][0] == '-')
  739. {
  740. Boolean b;
  741. switch (args[i][1])
  742. {
  743. case 's':
  744. b = GetArgParam(args[i], out shadowwidth);
  745. if (!b || (shadowwidth < 0 || shadowwidth > 99))
  746. {
  747. Console.WriteLine("Error: Incorrect command line option: -s");
  748. return;
  749. }
  750. break;
  751. case 'b':
  752. b = GetArgParam(args[i], out borderwidth);
  753. if (!b || (borderwidth < 0 || borderwidth > 99))
  754. {
  755. Console.WriteLine("Error: Incorrect command line option: -b");
  756. return;
  757. }
  758. break;
  759. case 'm':
  760. b = GetArgParam(args[i], out margin);
  761. if (!b || (margin < 0 || margin > 99))
  762. {
  763. Console.WriteLine("Error: Incorrect command line option: -b");
  764. return;
  765. }
  766. break;
  767. case 'r':
  768. b = GetArgParam(args[i], out shadowdir);
  769. if (!b || (shadowdir < 0 || shadowdir > 3))
  770. {
  771. Console.WriteLine("Error: Incorrect command line option: -r");
  772. return;
  773. }
  774. break;
  775. case 't':
  776. b = GetArgParam(args[i], out shadowtrans);
  777. if (!b || (shadowtrans < 0.0 || shadowtrans > 1.0))
  778. {
  779. Console.WriteLine("Error: Incorrect command line option: -t");
  780. return;
  781. }
  782. break;
  783. case 'a':
  784. b = GetArgParam(args[i], out bkcolor);
  785. if (!b)
  786. {
  787. Console.WriteLine("Error: Incorrect command line option: -a");
  788. return;
  789. }
  790. break;
  791. case 'c':
  792. b = GetArgParam(args[i], out shadowcolor);
  793. if (!b)
  794. {
  795. Console.WriteLine("Error: Incorrect command line option: -c");
  796. return;
  797. }
  798. break;
  799. case 'd':
  800. b = GetArgParam(args[i], out bordercolor);
  801. if (!b)
  802. {
  803. Console.WriteLine("Error: Incorrect command line option: -d");
  804. return;
  805. }
  806. break;
  807. case 'n':
  808. softshadow = false;
  809. break;
  810. default:
  811. Console.WriteLine("Fatal: Illegal option: {0}", args[i]);
  812. return;
  813. }
  814. }
  815. else
  816. {
  817. // must be a file name
  818. if (inputfile == "")
  819. inputfile = args[i];
  820. else if (outputfile == "")
  821. outputfile = args[i];
  822. // ignore other file names
  823. }
  824. }
  825. if (inputfile == "")
  826. {
  827. Console.WriteLine("Error: No file names given");
  828. return;
  829. }
  830. FastBitmap tmp, bmp;
  831. // try-catch doesn't work like it should
  832. if (File.Exists(inputfile))
  833. {
  834. tmp = new FastBitmap(inputfile);
  835. }
  836. else
  837. {
  838. Console.WriteLine("Error: Could not find file '{0}'", inputfile);
  839. return;
  840. }
  841. bmp = new FastBitmap(tmp.Width + borderwidth * 2, tmp.Height + borderwidth * 2,
  842. PixelFormat.Format32bppArgb);
  843. // add border if necessary
  844. if (borderwidth > 0)
  845. {
  846. SolidBrush br = new SolidBrush(bordercolor);
  847. Graphics g = Graphics.FromImage(bmp._bitmap);
  848. g.FillRectangle(br, 0, 0, borderwidth * 2 + tmp.Width, borderwidth * 2 + tmp.Height);
  849. g.Dispose();
  850. br.Dispose();
  851. }
  852. tmp.CopyTo(bmp, borderwidth, borderwidth, 0, 0, tmp.Width, tmp.Height);
  853. tmp.Dispose();
  854. // create image
  855. Int32 width = bmp.Width + shadowwidth + margin * 2;
  856. Int32 height = bmp.Height + shadowwidth + margin * 2;
  857. LayeredImage image = new LayeredImage(width, height);
  858. Int32 shadowx = 0, shadowy = 0, imgx = 0, imgy = 0;
  859. if (softshadow)
  860. {
  861. switch (shadowdir)
  862. {
  863. case 0:
  864. shadowx = margin - shadowwidth / 2;
  865. shadowy = margin - shadowwidth / 2;
  866. imgx = margin;
  867. imgy = margin;
  868. break;
  869. case 1:
  870. shadowx = margin + shadowwidth - 3 * (shadowwidth / 2);
  871. shadowy = margin - shadowwidth / 2;
  872. imgx = margin + shadowwidth;
  873. imgy = margin;
  874. break;
  875. case 2:
  876. shadowx = margin + shadowwidth - 3 * (shadowwidth / 2);
  877. shadowy = margin + shadowwidth - 3 * (shadowwidth / 2);
  878. imgx = margin + shadowwidth;
  879. imgy = margin + shadowwidth;
  880. break;
  881. case 3:
  882. shadowx = margin - shadowwidth / 2;
  883. shadowy = margin + shadowwidth - 3 * (shadowwidth / 2);
  884. imgx = margin;
  885. imgy = margin + shadowwidth;
  886. break;
  887. }
  888. }
  889. else
  890. {
  891. switch (shadowdir)
  892. {
  893. case 0:
  894. shadowx = margin;
  895. shadowy = margin;
  896. imgx = margin;
  897. imgy = margin;
  898. break;
  899. case 1:
  900. shadowx = margin - shadowwidth;
  901. shadowy = margin;
  902. imgx = margin + shadowwidth;
  903. imgy = margin;
  904. break;
  905. case 2:
  906. shadowx = margin - shadowwidth;
  907. shadowy = margin - shadowwidth;
  908. imgx = margin + shadowwidth;
  909. imgy = margin + shadowwidth;
  910. break;
  911. case 3:
  912. shadowx = margin;
  913. shadowy = margin - shadowwidth;
  914. imgx = margin;
  915. imgy = margin + shadowwidth;
  916. break;
  917. }
  918. }
  919. // background
  920. Layer bg = image.Layers.Add();
  921. bg.Clear(bkcolor);
  922. // shadow -- layer must be larger because of blur
  923. Layer shadow = image.Layers.Add(width + shadowwidth, height + shadowwidth);
  924. SolidBrush brush = new SolidBrush(shadowcolor);
  925. shadow.FillRectangle(shadowwidth, shadowwidth, bmp.Width, bmp.Height, brush);
  926. if (softshadow)
  927. shadow.Blur(shadowwidth, shadowwidth);
  928. brush.Dispose();
  929. shadow.OffsetX = shadowx;
  930. shadow.OffsetY = shadowy;
  931. shadow.Opacity = 1.0 - shadowtrans;
  932. // image
  933. Layer img = image.Layers.Add(bmp);
  934. img.OffsetX = imgx;
  935. img.OffsetY = imgy;
  936. // result
  937. FastBitmap result = image.Flatten();
  938. // save
  939. String filename = outputfile != "" ? outputfile : inputfile;
  940. String ext = Path.GetExtension(filename);
  941. if (ext == "")
  942. ext = ".bmp";
  943. ext = ext.ToLower();
  944. ImageFormat imgf = ImageFormat.Bmp;
  945. switch (ext)
  946. {
  947. case ".bmp":
  948. ext = ".bmp";
  949. imgf = ImageFormat.Bmp;
  950. break;
  951. case ".jpg":
  952. ext = ".jpg";
  953. imgf = ImageFormat.Jpeg;
  954. break;
  955. case ".jpeg":
  956. ext = ".jpeg";
  957. imgf = ImageFormat.Jpeg;
  958. break;
  959. case ".png":
  960. ext = ".png";
  961. imgf = ImageFormat.Png;
  962. break;
  963. case ".gif":
  964. ext = ".gif";
  965. imgf = ImageFormat.Gif;
  966. break;
  967. default:
  968. ext = ".bmp";
  969. imgf = ImageFormat.Bmp;
  970. break;
  971. }
  972. filename = Path.GetFileNameWithoutExtension(filename);
  973. result.Save(filename + ext, imgf);
  974. }
  975. }