PageRenderTime 475ms CodeModel.GetById 31ms RepoModel.GetById 18ms app.codeStats 2ms

/miniwindow.cpp

https://github.com/Twisol/mushclient
C++ | 4371 lines | 2829 code | 1085 blank | 457 comment | 411 complexity | 856a718eeabe706282ec0916edc9de95 MD5 | raw file
  1. // MiniWindow.cpp : implementation of the CMiniWindow class
  2. //
  3. #include "stdafx.h"
  4. #include "MUSHclient.h"
  5. #include "doc.h"
  6. #include "MUSHview.h"
  7. #include "scripting\errors.h"
  8. #include "color.h"
  9. #include "mainfrm.h"
  10. #define PNG_NO_CONSOLE_IO
  11. #include "png\png.h"
  12. // constructor
  13. CMiniWindow::CMiniWindow () :
  14. m_oldBitmap (NULL),
  15. m_Bitmap (NULL),
  16. m_iWidth (0), m_iHeight (0),
  17. m_iPosition (0), m_iFlags (0),
  18. m_iBackgroundColour (0), m_bShow (false),
  19. m_rect (0, 0, 0, 0),
  20. m_bTemporarilyHide (false),
  21. m_last_mouseposition (0, 0),
  22. m_last_mouse_update (0),
  23. m_client_mouseposition (0, 0)
  24. {
  25. dc.CreateCompatibleDC(NULL);
  26. dc.SetTextAlign (TA_LEFT | TA_TOP);
  27. m_tDateInstalled = CTime::GetCurrentTime(); // when miniwindow loaded
  28. } // end CMiniWindow::CMiniWindow (constructor)
  29. CMiniWindow::~CMiniWindow () // destructor
  30. {
  31. // get rid of old one if any
  32. if (m_Bitmap)
  33. {
  34. dc.SelectObject(m_oldBitmap); // swap old one back
  35. m_Bitmap->DeleteObject (); // delete the one we made
  36. delete m_Bitmap;
  37. }
  38. // delete our fonts
  39. for (FontMapIterator fit = m_Fonts.begin ();
  40. fit != m_Fonts.end ();
  41. fit++)
  42. delete fit->second;
  43. m_Fonts.clear ();
  44. // delete our images
  45. for (ImageMapIterator it = m_Images.begin ();
  46. it != m_Images.end ();
  47. it++)
  48. delete it->second;
  49. m_Images.clear ();
  50. // delete our hotspots
  51. for (HotspotMapIterator hit = m_Hotspots.begin ();
  52. hit != m_Hotspots.end ();
  53. hit++)
  54. delete hit->second;
  55. m_Hotspots.clear ();
  56. } // end CMiniWindow::~CMiniWindow (destructor)
  57. // a negative or zero value for the RH side of a rectange is considered offset from the right edge
  58. // eg. -1 is 1 pixel in from right, 0 is the RH edge.
  59. long CMiniWindow::FixRight (const long Right)
  60. {
  61. if (Right <= 0)
  62. return m_iWidth + Right;
  63. return Right;
  64. } // end CMiniWindow::FixRight
  65. static int BytesPerLine (int nWidth, int nBitsPerPixel)
  66. {
  67. return ( (nWidth * nBitsPerPixel + 31) & (~31) ) / 8;
  68. }
  69. // helper function to make normal/geometric pens
  70. // legacy behaviour is to do what we always did
  71. // however if an endcap or join mask bit is on we juse the ExtCreatePen function
  72. static void MakeAPen (CPen & pen, long PenColour, long PenStyle, long PenWidth)
  73. {
  74. // legacy behaviour
  75. if ((PenStyle & PS_ENDCAP_MASK) == 0 &&
  76. (PenStyle & PS_JOIN_MASK) == 0)
  77. {
  78. pen.CreatePen (PenStyle, PenWidth, PenColour);
  79. }
  80. else
  81. {
  82. LOGBRUSH logbrush;
  83. logbrush.lbStyle = BS_SOLID;
  84. logbrush.lbColor = PenColour;
  85. logbrush.lbHatch = 0; // not applicable
  86. pen.Attach (::ExtCreatePen (PenStyle | PS_GEOMETRIC, PenWidth, &logbrush, 0, NULL));
  87. }
  88. } // end of MakeAPen
  89. // a negative or zero value for the bottom of a rectange is considered offset from the bottom edge
  90. long CMiniWindow::FixBottom (const long Bottom)
  91. {
  92. if (Bottom <= 0)
  93. return m_iHeight + Bottom;
  94. return Bottom;
  95. } // end CMiniWindow::FixBottom
  96. /* positions:
  97. 0 = strech to output view size
  98. 1 = stretch with aspect ratio
  99. 2 = strech to owner size
  100. 3 = stretch with aspect ratio
  101. -- going clockwise here:
  102. -- top
  103. 4 = top left
  104. 5 = center left-right at top
  105. 6 = top right
  106. -- rh side
  107. 7 = on right, center top-bottom
  108. 8 = on right, at bottom
  109. -- bottom
  110. 9 = center left-right at bottom
  111. -- lh side
  112. 10 = on left, at bottom
  113. 11 = on left, center top-bottom
  114. -- middle
  115. 12 = center all
  116. 13 = tile
  117. */
  118. // create (or re-create) a mini-window
  119. void CMiniWindow::Create (long Left, long Top, long Width, long Height,
  120. short Position, long Flags,
  121. COLORREF BackgroundColour)
  122. {
  123. m_Location.x = Left ;
  124. m_Location.y = Top ;
  125. m_iWidth = Width ;
  126. m_iHeight = Height ;
  127. m_iPosition = Position ;
  128. m_iFlags = Flags ;
  129. m_iBackgroundColour = BackgroundColour;
  130. // get rid of old one if any
  131. if (m_Bitmap)
  132. {
  133. dc.SelectObject(m_oldBitmap); // swap old one back
  134. m_Bitmap->DeleteObject ();
  135. delete m_Bitmap;
  136. }
  137. m_Bitmap = new CBitmap;
  138. // CreateBitmap with zero-dimensions creates a monochrome bitmap, so force to be at least 1x1
  139. m_Bitmap->CreateBitmap (MAX (m_iWidth, 1), MAX (m_iHeight, 1), 1, GetDeviceCaps(dc, BITSPIXEL), NULL);
  140. m_oldBitmap = dc.SelectObject (m_Bitmap);
  141. dc.SetWindowOrg(0, 0);
  142. dc.FillSolidRect (0, 0, m_iWidth, m_iHeight, m_iBackgroundColour);
  143. m_bShow = false;
  144. // a newly created window has no hotspots
  145. if ((Flags & MINIWINDOW_KEEP_HOTSPOTS) == 0)
  146. DeleteAllHotspots ();
  147. } // end of MiniWindow::Create
  148. // set/clear the show flag so the window becomes visible
  149. void CMiniWindow::Show (bool bShow)
  150. {
  151. m_bShow = bShow;
  152. } // end of CMiniWindow::Show
  153. /*
  154. Actions:
  155. 1 = FrameRect ( 1 pixel )
  156. 2 = FillRect
  157. 3 = InvertRect
  158. 4 = 3D Rect (Colour1 is top and left edge colour, Colour2 is bottom and right edge colour)
  159. 5 = DrawEdge (draws a 3d-style edge with optional fill)
  160. Colour1 = style of edge:
  161. EDGE_RAISED: // 5
  162. EDGE_ETCHED: // 6
  163. EDGE_BUMP: // 9
  164. EDGE_SUNKEN: // 10
  165. Colour2 = where to draw it:
  166. BF_TOPLEFT 0x3
  167. BF_TOPRIGHT 0x6
  168. BF_BOTTOMLEFT 0x9
  169. BF_BOTTOMRIGHT 0xC
  170. BF_RECT 0xF
  171. BF_DIAGONAL 0x0010
  172. // For diagonal lines, the BF_RECT flags specify the end point of the
  173. // vector bounded by the rectangle parameter.
  174. BF_DIAGONAL_ENDTOPLEFT 0x13
  175. BF_DIAGONAL_ENDTOPRIGHT 0x16
  176. BF_DIAGONAL_ENDBOTTOMLEFT 0x19
  177. BF_DIAGONAL_ENDBOTTOMRIGHT 0x1C
  178. Additional Colour2 flags:
  179. BF_MIDDLE 0x0800 Fill in the middle
  180. BF_SOFT 0x1000 For softer buttons
  181. BF_ADJUST 0x2000 Calculate the space left over
  182. BF_FLAT 0x4000 For flat rather than 3D borders
  183. BF_MONO 0x8000 For monochrome borders
  184. 6 = Flood Fill Border (fills to border specified by Colour1)
  185. 7 = Flood Fill Surface (fills while on surface specified by Colour1)
  186. */
  187. // various rectangle operations
  188. long CMiniWindow::RectOp (short Action, long Left, long Top, long Right, long Bottom, long Colour1, long Colour2)
  189. {
  190. switch (Action)
  191. {
  192. case 1: // frame
  193. {
  194. CBrush br1;
  195. br1.CreateSolidBrush (Colour1);
  196. dc.FrameRect (CRect (Left, Top, FixRight (Right), FixBottom (Bottom)), &br1);
  197. break;
  198. }
  199. case 2: // fill
  200. {
  201. CBrush br1;
  202. br1.CreateSolidBrush (Colour1);
  203. dc.FillRect (CRect (Left, Top, FixRight (Right), FixBottom (Bottom)), &br1);
  204. break;
  205. }
  206. case 3: // invert
  207. {
  208. dc.InvertRect (CRect (Left, Top, FixRight (Right), FixBottom (Bottom)));
  209. break;
  210. }
  211. case 4: // 3D rect
  212. {
  213. dc.Draw3dRect (CRect (Left, Top, FixRight (Right), FixBottom (Bottom)), Colour1, Colour2);
  214. break;
  215. }
  216. case 5: // DrawEdge
  217. {
  218. switch (Colour1) // nEdge
  219. {
  220. // must be one of these 4 flags
  221. case EDGE_RAISED: // 5
  222. case EDGE_ETCHED: // 6
  223. case EDGE_BUMP: // 9
  224. case EDGE_SUNKEN: // 10
  225. break;
  226. default: return eBadParameter;
  227. }
  228. if ((Colour2 & 0xFF) > 0x1F)
  229. return eBadParameter;
  230. dc.DrawEdge (CRect (Left, Top, FixRight (Right), FixBottom (Bottom)), Colour1, Colour2);
  231. break;
  232. }
  233. case 6: // Flood fill border
  234. {
  235. dc.FloodFill (Left, Top, Colour1);
  236. break;
  237. }
  238. case 7: // Flood fill surface
  239. {
  240. dc.ExtFloodFill (Left, Top, Colour1, FLOODFILLSURFACE);
  241. break;
  242. }
  243. default: return eUnknownOption;
  244. } // end of switch
  245. return eOK;
  246. } // end of CMiniWindow::RectOp
  247. static long ValidatePenStyle (const long PenStyle, const long PenWidth)
  248. {
  249. switch (PenStyle & PS_STYLE_MASK)
  250. {
  251. // must be one of these flags
  252. case PS_SOLID: // 0
  253. case PS_NULL: // 5
  254. case PS_INSIDEFRAME: // 6
  255. break;
  256. case PS_DASH: // 1 /* ------- */
  257. case PS_DOT: // 2 /* ....... */
  258. case PS_DASHDOT: // 3 /* _._._._ */
  259. case PS_DASHDOTDOT: // 4 /* _.._.._ */
  260. if (PenWidth > 1)
  261. return ePenStyleNotValid;
  262. break;
  263. default: return ePenStyleNotValid;
  264. }
  265. switch (PenStyle & PS_ENDCAP_MASK)
  266. {
  267. // must be one of these flags
  268. case PS_ENDCAP_ROUND: // 0x000
  269. case PS_ENDCAP_SQUARE: // 0x100
  270. case PS_ENDCAP_FLAT: // 0x200
  271. break;
  272. default: return ePenStyleNotValid;
  273. }
  274. switch (PenStyle & PS_JOIN_MASK)
  275. {
  276. // must be one of these flags
  277. case PS_JOIN_ROUND: // 0x0000
  278. case PS_JOIN_BEVEL: // 0x1000
  279. case PS_JOIN_MITER: // 0x2000
  280. break;
  281. default: return ePenStyleNotValid;
  282. }
  283. return eOK;
  284. }
  285. long ValidateBrushStyle (const long BrushStyle,
  286. const long PenColour,
  287. const long BrushColour,
  288. CBrush & br)
  289. {
  290. LOGBRUSH lb;
  291. lb.lbColor = PenColour;
  292. switch (BrushStyle)
  293. {
  294. // must be one of these flags
  295. case BS_SOLID: // 0
  296. lb.lbStyle = BS_SOLID;
  297. lb.lbColor = BrushColour;
  298. break;
  299. case BS_NULL: // 1
  300. lb.lbStyle = BS_NULL;
  301. break;
  302. // hatched styles:
  303. case 2:
  304. lb.lbStyle = BS_HATCHED;
  305. lb.lbHatch = HS_HORIZONTAL;
  306. break;
  307. case 3:
  308. lb.lbStyle = BS_HATCHED;
  309. lb.lbHatch = HS_VERTICAL;
  310. break;
  311. case 4:
  312. lb.lbStyle = BS_HATCHED;
  313. lb.lbHatch = HS_FDIAGONAL;
  314. break;
  315. case 5:
  316. lb.lbStyle = BS_HATCHED;
  317. lb.lbHatch = HS_BDIAGONAL;
  318. break;
  319. case 6:
  320. lb.lbStyle = BS_HATCHED;
  321. lb.lbHatch = HS_CROSS;
  322. break;
  323. case 7:
  324. lb.lbStyle = BS_HATCHED;
  325. lb.lbHatch = HS_DIAGCROSS;
  326. break;
  327. // each byte is a line, so 0xAA would be 10101010 (top line)
  328. // 0x55 would be 01010101 (next line)
  329. case 8: // fine hatch
  330. {
  331. CBitmap bitmap;
  332. WORD wBits[] = { 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55 };
  333. bitmap.CreateBitmap (8, 8, 1, 1, wBits);
  334. br.CreatePatternBrush (&bitmap);
  335. return eOK;
  336. }
  337. case 9: // medium hatch
  338. {
  339. CBitmap bitmap;
  340. WORD wBits[] = { 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, };
  341. bitmap.CreateBitmap (8, 8, 1, 1, wBits);
  342. br.CreatePatternBrush (&bitmap);
  343. return eOK;
  344. }
  345. case 10: // coarse hatch
  346. {
  347. CBitmap bitmap;
  348. WORD wBits[] = { 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0 };
  349. bitmap.CreateBitmap (8, 8, 1, 1, wBits);
  350. br.CreatePatternBrush (&bitmap);
  351. return eOK;
  352. }
  353. case 11: // waves - horizontal
  354. {
  355. CBitmap bitmap;
  356. WORD wBits[] = { 0xCC, 0x33, 0x00, 0x00, 0xCC, 0x33, 0x00, 0x00 };
  357. bitmap.CreateBitmap (8, 8, 1, 1, wBits);
  358. br.CreatePatternBrush (&bitmap);
  359. return eOK;
  360. }
  361. case 12: // waves - vertical
  362. {
  363. CBitmap bitmap;
  364. WORD wBits[] = { 0x11, 0x11, 0x22, 0x22, 0x11, 0x11, 0x22, 0x22 };
  365. bitmap.CreateBitmap (8, 8, 1, 1, wBits);
  366. br.CreatePatternBrush (&bitmap);
  367. return eOK;
  368. }
  369. default: return eBrushStyleNotValid;
  370. }
  371. br.CreateBrushIndirect (&lb);
  372. return eOK;
  373. }
  374. /*
  375. Actions:
  376. 1 = Ellipse
  377. 2 = Rectangle (can have thicker pen style and be filled)
  378. 3 = Round Rectangle
  379. 4 = Chord (a closed figure bounded by the intersection of an ellipse and a line segment)
  380. 5 = Pie (Draws a pie-shaped wedge by drawing an elliptical arc whose center and two endpoints are joined by lines)
  381. */
  382. // various circle/ellipse/pie operations
  383. long CMiniWindow::CircleOp (short Action,
  384. long Left, long Top, long Right, long Bottom,
  385. long PenColour, long PenStyle, long PenWidth,
  386. long BrushColour, long BrushStyle,
  387. long Extra1, long Extra2, long Extra3, long Extra4)
  388. {
  389. long iResult = eUnknownOption;
  390. if (ValidatePenStyle (PenStyle, PenWidth))
  391. return ePenStyleNotValid;
  392. // validate and create requested bruch
  393. CBrush br;
  394. if (ValidateBrushStyle (BrushStyle, PenColour, BrushColour, br))
  395. return eBrushStyleNotValid;
  396. // create requested pen
  397. CPen pen;
  398. MakeAPen (pen, PenColour, PenStyle, PenWidth);
  399. // select into DC
  400. CPen* oldPen = dc.SelectObject(&pen);
  401. CBrush* oldBrush = dc.SelectObject(&br);
  402. if (BrushStyle > 1 && BrushStyle <= 7)
  403. {
  404. dc.SetBkColor (BrushColour); // for hatched brushes this is the background colour
  405. }
  406. else
  407. if (BrushStyle > 7) // pattern brushes
  408. {
  409. dc.SetTextColor (BrushColour); // for patterned brushes
  410. dc.SetBkColor (PenColour); // for hatched brushes and patterned brushes
  411. }
  412. if (BrushColour != -1)
  413. dc.SetBkMode (OPAQUE);
  414. else
  415. dc.SetBkMode (TRANSPARENT);
  416. switch (Action)
  417. {
  418. case 1: // ellipse
  419. {
  420. dc.Ellipse (CRect (Left, Top, FixRight (Right), FixBottom (Bottom)));
  421. iResult = eOK;
  422. break;
  423. }
  424. case 2: // rectangle
  425. {
  426. dc.Rectangle (CRect (Left, Top, FixRight (Right), FixBottom (Bottom)));
  427. iResult = eOK;
  428. break;
  429. }
  430. case 3: // round rectangle
  431. {
  432. dc.RoundRect (CRect (Left, Top, FixRight (Right), FixBottom (Bottom)), CPoint (Extra1, Extra2));
  433. iResult = eOK;
  434. break;
  435. }
  436. case 4: // chord
  437. {
  438. dc.Chord (CRect (Left, Top, FixRight (Right), FixBottom (Bottom)),
  439. CPoint (Extra1, Extra2),
  440. CPoint (Extra3, Extra4));
  441. iResult = eOK;
  442. break;
  443. }
  444. case 5: // pie
  445. {
  446. dc.Pie (CRect (Left, Top, FixRight (Right), FixBottom (Bottom)),
  447. CPoint (Extra1, Extra2),
  448. CPoint (Extra3, Extra4));
  449. iResult = eOK;
  450. break;
  451. }
  452. } // end of switch
  453. // put things back
  454. dc.SelectObject (oldPen);
  455. dc.SelectObject (oldBrush);
  456. return iResult;
  457. } // end of CMiniWindow::CircleOp
  458. // add a font to our map of fonts by name (eg. "inventory")
  459. long CMiniWindow::Font (LPCTSTR FontId, // eg. "inventory"
  460. LPCTSTR FontName, // eg. "Courier New"
  461. double Size,
  462. BOOL Bold, BOOL Italic, BOOL Underline, BOOL Strikeout,
  463. short Charset, short PitchAndFamily)
  464. {
  465. FontMapIterator it = m_Fonts.find (FontId);
  466. if (it != m_Fonts.end ())
  467. {
  468. delete it->second; // delete existing font
  469. m_Fonts.erase (it);
  470. }
  471. // no font name, and zero point size requested - delete old font
  472. if (FontName [0] == 0 && Size == 0.0)
  473. return eOK;
  474. CFont * pFont = new CFont;
  475. int lfHeight = -MulDiv(Size ? Size : 10.0, dc.GetDeviceCaps(LOGPIXELSY), 72);
  476. if (pFont->CreateFont (lfHeight,
  477. 0, // int nWidth,
  478. 0, // int nEscapement,
  479. 0, // int nOrientation,
  480. Bold ? FW_BOLD : FW_NORMAL, // int nWeight,
  481. Italic, // BYTE bItalic,
  482. Underline, // BYTE bUnderline,
  483. Strikeout, // BYTE cStrikeOut,
  484. Charset,
  485. 0, // BYTE nOutPrecision,
  486. 0, // BYTE nClipPrecision,
  487. 0, // BYTE nQuality,
  488. PitchAndFamily,
  489. FontName) == 0)
  490. {
  491. delete pFont;
  492. return eCannotAddFont;
  493. }
  494. m_Fonts [FontId] = pFont;
  495. return eOK;
  496. } // end of CMiniWindow::Font
  497. static void SetUpVariantLong (VARIANT & tVariant, const long iContents)
  498. {
  499. VariantClear (&tVariant);
  500. tVariant.vt = VT_I4;
  501. tVariant.lVal = iContents;
  502. } // end of SetUpVariantLong
  503. static void SetUpVariantString (VARIANT & tVariant, const CString & strContents)
  504. {
  505. VariantClear (&tVariant);
  506. tVariant.vt = VT_BSTR;
  507. tVariant.bstrVal = strContents.AllocSysString ();
  508. } // end of SetUpVariantString
  509. static void SetUpVariantBool (VARIANT & tVariant, const BOOL iContents)
  510. {
  511. VariantClear (&tVariant);
  512. tVariant.vt = VT_BOOL;
  513. tVariant.boolVal = iContents;
  514. } // end of SetUpVariantBool
  515. static void SetUpVariantDate (VARIANT & tVariant, const COleDateTime iContents)
  516. {
  517. VariantClear (&tVariant);
  518. tVariant.vt = VT_DATE;
  519. tVariant.date = iContents;
  520. } // end of SetUpVariantDate
  521. // return info about the select font
  522. void CMiniWindow::FontInfo (LPCTSTR FontId, long InfoType, VARIANT & vaResult)
  523. {
  524. FontMapIterator it = m_Fonts.find (FontId);
  525. if (it == m_Fonts.end ())
  526. return; // no such font
  527. CFont* oldFont = dc.SelectObject(it->second); // select in the requested font
  528. TEXTMETRIC tm;
  529. dc.GetTextMetrics(&tm);
  530. CString rString;
  531. dc.GetTextFace (rString);
  532. switch (InfoType)
  533. {
  534. case 1: SetUpVariantLong (vaResult, tm.tmHeight); break;
  535. case 2: SetUpVariantLong (vaResult, tm.tmAscent); break;
  536. case 3: SetUpVariantLong (vaResult, tm.tmDescent); break;
  537. case 4: SetUpVariantLong (vaResult, tm.tmInternalLeading); break;
  538. case 5: SetUpVariantLong (vaResult, tm.tmExternalLeading); break;
  539. case 6: SetUpVariantLong (vaResult, tm.tmAveCharWidth); break;
  540. case 7: SetUpVariantLong (vaResult, tm.tmMaxCharWidth); break;
  541. case 8: SetUpVariantLong (vaResult, tm.tmWeight); break;
  542. case 9: SetUpVariantLong (vaResult, tm.tmOverhang); break;
  543. case 10: SetUpVariantLong (vaResult, tm.tmDigitizedAspectX); break;
  544. case 11: SetUpVariantLong (vaResult, tm.tmDigitizedAspectY); break;
  545. case 12: SetUpVariantLong (vaResult, tm.tmFirstChar); break;
  546. case 13: SetUpVariantLong (vaResult, tm.tmLastChar); break;
  547. case 14: SetUpVariantLong (vaResult, tm.tmDefaultChar); break;
  548. case 15: SetUpVariantLong (vaResult, tm.tmBreakChar); break;
  549. case 16: SetUpVariantLong (vaResult, tm.tmItalic); break;
  550. case 17: SetUpVariantLong (vaResult, tm.tmUnderlined); break;
  551. case 18: SetUpVariantLong (vaResult, tm.tmStruckOut); break;
  552. case 19: SetUpVariantLong (vaResult, tm.tmPitchAndFamily); break;
  553. case 20: SetUpVariantLong (vaResult, tm.tmCharSet); break;
  554. case 21: SetUpVariantString (vaResult, rString); break;
  555. default:
  556. vaResult.vt = VT_NULL;
  557. break;
  558. } // end of switch
  559. dc.SelectObject(oldFont);
  560. } // end of CMiniWindow::FontInfo
  561. // return list of fonts we installed
  562. void CMiniWindow::FontList (VARIANT & vaResult)
  563. {
  564. COleSafeArray sa; // for array list
  565. long iCount = 0;
  566. // put the arrays into the array
  567. if (!m_Fonts.empty ()) // cannot create empty dimension
  568. {
  569. sa.CreateOneDim (VT_VARIANT, m_Fonts.size ());
  570. for (FontMapIterator it = m_Fonts.begin ();
  571. it != m_Fonts.end ();
  572. it++)
  573. {
  574. // the array must be a bloody array of variants, or VBscript kicks up
  575. COleVariant v (it->first.c_str ());
  576. sa.PutElement (&iCount, &v);
  577. iCount++;
  578. }
  579. } // end of having at least one
  580. vaResult = sa.Detach ();
  581. } // end of CMiniWindow::FontList
  582. //helper function to calculate length of UTF8 string
  583. static long CalculateUTF8length (LPCTSTR Text, size_t length)
  584. {
  585. int iBad = _pcre_valid_utf8 ((unsigned char *) Text, length);
  586. if (iBad >= 0)
  587. return -1;
  588. // string is OK, calculate its length
  589. int i = 0; // length
  590. // this algorithm assumes the UTF-8 is OK, based on the earlier check
  591. for (register const unsigned char *p = (const unsigned char *) Text ;
  592. length-- > 0;
  593. i++)
  594. {
  595. register int ab; // additional bytes
  596. register int c = *p++; // this byte
  597. if (c < 128)
  598. continue; // zero additional bytes
  599. ab = _pcre_utf8_table4 [c & 0x3f]; /* Number of additional bytes */
  600. length -= ab; // we know string is valid already, so just skip the additional bytes (ab)
  601. p += ab;
  602. }
  603. return i;
  604. } // end of CalculateUTF8length
  605. // output text, ordinary or UTF8 - returns length of text
  606. long CMiniWindow::Text (LPCTSTR FontId, // which previously-created font
  607. LPCTSTR Text, // what to say
  608. long Left, long Top, long Right, long Bottom, // where to say it
  609. long Colour, // colour to show it in
  610. BOOL Unicode) // true if UTF8
  611. {
  612. FontMapIterator it = m_Fonts.find (FontId);
  613. if (it == m_Fonts.end ())
  614. return -2;
  615. size_t length = strlen (Text);
  616. long utf8_length = 0;
  617. // give up if no text
  618. if (length <= 0)
  619. return 0;
  620. // quick sanity check on our UTF8 stuff
  621. if (Unicode)
  622. {
  623. utf8_length = CalculateUTF8length (Text, length);
  624. if (utf8_length < 0)
  625. return -3; // ohno!
  626. }
  627. CFont* oldFont = dc.SelectObject(it->second); // select in the requested font
  628. CSize textsize;
  629. dc.SetTextColor (Colour);
  630. dc.SetBkMode (TRANSPARENT);
  631. if (Unicode)
  632. {
  633. vector<WCHAR> v (utf8_length); // get correct size vector
  634. int iUnicodeCharacters = MultiByteToWideChar (CP_UTF8, 0,
  635. Text, length, // input
  636. &v [0], utf8_length); // output
  637. ExtTextOutW (dc.m_hDC, Left, Top, ETO_CLIPPED, CRect (Left, Top, FixRight (Right), FixBottom (Bottom) ),
  638. &v [0], iUnicodeCharacters, NULL);
  639. // now calculate width of Unicode pixels
  640. GetTextExtentPoint32W(
  641. dc.m_hDC, // handle to device context
  642. &v [0], // pointer to text string
  643. iUnicodeCharacters, // number of characters in string
  644. &textsize // pointer to structure for string size
  645. );
  646. }
  647. else
  648. {
  649. dc.ExtTextOut (Left, Top, ETO_CLIPPED, CRect (Left, Top, FixRight (Right), FixBottom (Bottom)),
  650. Text, length, NULL);
  651. textsize = dc.GetTextExtent (Text, length);
  652. }
  653. dc.SelectObject(oldFont);
  654. return min (textsize.cx, FixRight (Right) - Left); // if clipped, length is width of rectangle
  655. } // end of CMiniWindow::Text
  656. // measure text, ordinary or UTF8
  657. long CMiniWindow::TextWidth (LPCTSTR FontId, // which previously-created font
  658. LPCTSTR Text, // what to measure
  659. BOOL Unicode) // true if UTF8
  660. {
  661. FontMapIterator it = m_Fonts.find (FontId);
  662. if (it == m_Fonts.end ())
  663. return -2;
  664. size_t length = strlen (Text);
  665. long utf8_length = 0;
  666. // give up if no text
  667. if (length <= 0)
  668. return 0;
  669. // quick sanity check on our UTF8 stuff
  670. if (Unicode)
  671. {
  672. utf8_length = CalculateUTF8length (Text, length);
  673. if (utf8_length < 0)
  674. return -3; // ohno!
  675. }
  676. CFont* oldFont = dc.SelectObject(it->second); // select in the requested font
  677. CSize textsize;
  678. if (Unicode)
  679. {
  680. vector<WCHAR> v (utf8_length); // get correct size vector
  681. int iUnicodeCharacters = MultiByteToWideChar (CP_UTF8, 0,
  682. Text, length, // input
  683. &v [0], utf8_length); // output
  684. // now calculate width of Unicode pixels
  685. GetTextExtentPoint32W(
  686. dc.m_hDC, // handle to device context
  687. &v [0], // pointer to text string
  688. iUnicodeCharacters, // number of characters in string
  689. &textsize // pointer to structure for string size
  690. );
  691. }
  692. else
  693. textsize = dc.GetTextExtent (Text, length);
  694. dc.SelectObject(oldFont);
  695. return textsize.cx;
  696. } // end of CMiniWindow::TextWidth
  697. // draws a straight line
  698. long CMiniWindow::Line (long x1, long y1, long x2, long y2,
  699. long PenColour, long PenStyle, long PenWidth)
  700. {
  701. if (ValidatePenStyle (PenStyle, PenWidth))
  702. return ePenStyleNotValid;
  703. dc.SetBkMode (TRANSPARENT);
  704. // create requested pen
  705. CPen pen;
  706. MakeAPen (pen, PenColour, PenStyle, PenWidth);
  707. CPen* oldPen = dc.SelectObject(&pen);
  708. dc.MoveTo (x1, y1);
  709. dc.LineTo (x2, y2); // note NOT: FixRight (x2), FixBottom (y2) (as at version 4.38)
  710. // put things back
  711. dc.SelectObject (oldPen);
  712. return eOK;
  713. } // end of CMiniWindow::Line
  714. // draws an arc
  715. long CMiniWindow::Arc (long Left, long Top, long Right, long Bottom,
  716. long x1, long y1,
  717. long x2, long y2,
  718. long PenColour, long PenStyle, long PenWidth)
  719. {
  720. if (ValidatePenStyle (PenStyle, PenWidth))
  721. return ePenStyleNotValid;
  722. dc.SetBkMode (TRANSPARENT);
  723. // create requested pen
  724. CPen pen;
  725. MakeAPen (pen, PenColour, PenStyle, PenWidth);
  726. CPen* oldPen = dc.SelectObject(&pen);
  727. dc.Arc(Left, Top, FixRight (Right), FixBottom (Bottom),
  728. x1, y1, // from
  729. FixRight (x2), FixBottom (y2)); // to
  730. // put things back
  731. dc.SelectObject (oldPen);
  732. return eOK;
  733. } // end of CMiniWindow::Arc
  734. // return info about the window
  735. void CMiniWindow::Info (long InfoType, VARIANT & vaResult)
  736. {
  737. switch (InfoType)
  738. {
  739. case 1: SetUpVariantLong (vaResult, m_Location.x); break; // left
  740. case 2: SetUpVariantLong (vaResult, m_Location.y); break; // top
  741. case 3: SetUpVariantLong (vaResult, m_iWidth); break; // width
  742. case 4: SetUpVariantLong (vaResult, m_iHeight); break; // height
  743. case 5: SetUpVariantBool (vaResult, m_bShow); break; // show flag
  744. case 6: SetUpVariantBool (vaResult, m_bTemporarilyHide); break; // is it hidden right now?
  745. case 7: SetUpVariantLong (vaResult, m_iPosition); break; // layout mode
  746. case 8: SetUpVariantLong (vaResult, m_iFlags); break; // flags
  747. case 9: SetUpVariantLong (vaResult, m_iBackgroundColour); break; // background colour
  748. case 10: SetUpVariantLong (vaResult, m_rect.left); break; // where it is right now
  749. case 11: SetUpVariantLong (vaResult, m_rect.top); break; // "
  750. case 12: SetUpVariantLong (vaResult, m_rect.right); break; // "
  751. case 13: SetUpVariantLong (vaResult, m_rect.bottom); break; // "
  752. case 14: SetUpVariantLong (vaResult, m_last_mouseposition.x); break; // last mouse x position
  753. case 15: SetUpVariantLong (vaResult, m_last_mouseposition.y); break; // last mouse y position
  754. case 16: SetUpVariantLong (vaResult, m_last_mouse_update); break; // last position update count
  755. case 17: SetUpVariantLong (vaResult, m_client_mouseposition.x); break; // last client mouse x position
  756. case 18: SetUpVariantLong (vaResult, m_client_mouseposition.y); break; // last client mouse y position
  757. case 19: SetUpVariantString (vaResult, m_sMouseOverHotspot.c_str ()); break; // mouse-over hotspot
  758. case 20: SetUpVariantString (vaResult, m_sMouseDownHotspot.c_str ()); break; // mouse-down hotspot
  759. case 21: SetUpVariantDate (vaResult, COleDateTime (m_tDateInstalled.GetTime ())); break;
  760. default:
  761. vaResult.vt = VT_NULL;
  762. break;
  763. } // end of switch
  764. } // end of CMiniWindow::Info
  765. // loads an image file, ready for drawing into window
  766. long CMiniWindow::LoadImage (LPCTSTR ImageId, LPCTSTR FileName)
  767. {
  768. ImageMapIterator it = m_Images.find (ImageId);
  769. if (it != m_Images.end ())
  770. {
  771. delete it->second; // delete existing image
  772. m_Images.erase (it);
  773. }
  774. CString strFileName = FileName;
  775. strFileName.TrimLeft ();
  776. strFileName.TrimRight ();
  777. // no file name means get rid of image
  778. if (strFileName.IsEmpty ())
  779. return eOK;
  780. // have to be long enough to have x.bmp
  781. if (strFileName.GetLength () < 5)
  782. return eBadParameter;
  783. // handle PNG files separately
  784. if (strFileName.Right (4).CompareNoCase (".png") == 0)
  785. return LoadPngImage (ImageId, FileName);
  786. // must be .bmp or .png file
  787. if (strFileName.Right (4).CompareNoCase (".bmp") != 0)
  788. return eBadParameter;
  789. HBITMAP hBmp = (HBITMAP)::LoadImage(
  790. NULL,
  791. FileName,
  792. IMAGE_BITMAP,
  793. 0,
  794. 0,
  795. LR_LOADFROMFILE|LR_CREATEDIBSECTION
  796. );
  797. if (hBmp)
  798. {
  799. CBitmap * pImage = new CBitmap;
  800. pImage->Attach (hBmp);
  801. m_Images [ImageId] = pImage;
  802. return eOK;
  803. } // end of having a bitmap loaded
  804. if (GetLastError () == 2)
  805. return eFileNotFound;
  806. return eUnableToLoadImage;
  807. } // end of CMiniWindow::LoadImage
  808. // loads an image from memory, ready for drawing into window
  809. long CMiniWindow::LoadImageMemory(LPCTSTR ImageId,
  810. unsigned char * Buffer,
  811. const size_t Length,
  812. const bool bAlpha)
  813. {
  814. ImageMapIterator it = m_Images.find (ImageId);
  815. if (it != m_Images.end ())
  816. {
  817. delete it->second; // delete existing image
  818. m_Images.erase (it);
  819. }
  820. HBITMAP hbmp;
  821. long result = LoadPngMemory (Buffer, Length, hbmp, bAlpha);
  822. if (result != eOK)
  823. return result;
  824. // make bitmap to add to images list
  825. CBitmap * pImage = new CBitmap;
  826. pImage->Attach (hbmp);
  827. m_Images [ImageId] = pImage;
  828. return eOK;
  829. } // end of CMiniWindow::LoadImageMemory
  830. static void user_error_fn(png_structp png_ptr,
  831. png_const_charp error_msg)
  832. {
  833. // AfxMessageBox (error_msg);
  834. }
  835. static void user_warning_fn(png_structp png_ptr,
  836. png_const_charp warning_msg)
  837. {
  838. // AfxMessageBox (warning_msg);
  839. }
  840. long CMiniWindow::LoadPngImage (LPCTSTR ImageId, LPCTSTR FileName)
  841. {
  842. HBITMAP hbmp;
  843. long result = LoadPng (FileName, hbmp);
  844. if (result != eOK)
  845. return result;
  846. // make bitmap to add to images list
  847. CBitmap * pImage = new CBitmap;
  848. pImage->Attach (hbmp);
  849. m_Images [ImageId] = pImage;
  850. return eOK;
  851. } // end of CMiniWindow::LoadPngImage
  852. // saves an image file
  853. long CMiniWindow::Write (LPCTSTR FileName)
  854. {
  855. CString strFileName = FileName;
  856. strFileName.TrimLeft ();
  857. strFileName.TrimRight ();
  858. if (strFileName.IsEmpty ())
  859. return eNoNameSpecified;
  860. // have to be long enough to have x.bmp
  861. if (strFileName.GetLength () < 5)
  862. return eBadParameter;
  863. // must be .bmp or .png file
  864. if (strFileName.Right (4).CompareNoCase (".bmp") != 0 &&
  865. strFileName.Right (4).CompareNoCase (".png") != 0)
  866. return eBadParameter;
  867. // get window data
  868. CDC gDC;
  869. gDC.CreateCompatibleDC(&dc);
  870. CBitmap gbmp;
  871. BITMAPINFO bmi;
  872. ZeroMemory (&bmi, sizeof bmi);
  873. bmi.bmiHeader.biSize = sizeof bmi;
  874. bmi.bmiHeader.biWidth = m_iWidth;
  875. bmi.bmiHeader.biHeight = m_iHeight;
  876. bmi.bmiHeader.biPlanes = 1;
  877. bmi.bmiHeader.biBitCount = 24;
  878. bmi.bmiHeader.biCompression = BI_RGB;
  879. bmi.bmiHeader.biSizeImage = m_iHeight * BytesPerLine (m_iWidth, 24);
  880. unsigned char * pA = NULL;
  881. HBITMAP hbmG = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**) &pA, NULL, 0);
  882. HBITMAP hOldAbmp = (HBITMAP) SelectObject(gDC.m_hDC, hbmG);
  883. // copy image from miniwindow to bitmap
  884. gDC.BitBlt (0, 0, m_iWidth, m_iHeight, &dc, 0, 0, SRCCOPY);
  885. long iReturn = eOK;
  886. // must be .bmp or .png file
  887. if (strFileName.Right (4).CompareNoCase (".png") == 0)
  888. iReturn = WritePng (FileName, &bmi, pA);
  889. else
  890. { // write BMP file
  891. // create requested file
  892. CFile file;
  893. if( !file.Open (FileName, CFile::modeWrite | CFile::modeCreate))
  894. return eCouldNotOpenFile;
  895. BITMAPFILEHEADER hdr;
  896. ZeroMemory (&hdr, sizeof hdr);
  897. // Fill in the fields of the file header
  898. hdr.bfType = ((WORD) ('M' << 8) | 'B'); // is always "BM"
  899. hdr.bfOffBits = sizeof hdr + sizeof bmi.bmiHeader;
  900. hdr.bfSize = hdr.bfOffBits + bmi.bmiHeader.biSizeImage;
  901. try
  902. {
  903. // Write the file header
  904. file.Write( &hdr, sizeof hdr);
  905. // Write the bitmap info header
  906. file.Write( &bmi.bmiHeader, sizeof bmi.bmiHeader);
  907. // Write the bits
  908. file.Write( pA, bmi.bmiHeader.biSizeImage);
  909. }
  910. catch (CFileException * e)
  911. {
  912. e->Delete ();
  913. iReturn = eLogFileBadWrite;
  914. } // end of catching a file exception
  915. } // end of writing BMP file
  916. // finished with bitmap
  917. SelectObject(gDC.m_hDC, hOldAbmp);
  918. DeleteObject (hbmG);
  919. return iReturn;
  920. } // end of CMiniWindow::Write
  921. long CMiniWindow::WritePng (LPCTSTR FileName, const BITMAPINFO * bmi, unsigned char * pData)
  922. {
  923. // open file
  924. FILE *fp = fopen(FileName, "wb");
  925. if (!fp)
  926. return (eCouldNotOpenFile);
  927. // create PNG structure
  928. png_structp png_ptr = png_create_write_struct
  929. (PNG_LIBPNG_VER_STRING, NULL,
  930. user_error_fn, user_warning_fn);
  931. if (!png_ptr)
  932. {
  933. fclose (fp);
  934. return eLogFileBadWrite;
  935. }
  936. // create info structure
  937. png_infop info_ptr = png_create_info_struct(png_ptr);
  938. if (!info_ptr)
  939. {
  940. png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
  941. fclose (fp);
  942. return eLogFileBadWrite;
  943. }
  944. #pragma warning (push)
  945. #pragma warning (disable : 4611) // interaction between '_setjmp' and C++ object destruction is non-portable
  946. // if png fails it will longjmp back to here, so we destroy the structure,
  947. // close the file, and wrap up
  948. if (setjmp(png_jmpbuf(png_ptr)))
  949. {
  950. png_destroy_write_struct(&png_ptr, &info_ptr);
  951. fclose (fp);
  952. return eLogFileBadWrite;
  953. }
  954. #pragma warning (pop)
  955. // initialize IO
  956. png_init_io (png_ptr, fp);
  957. // tell PNG the file format
  958. png_set_IHDR (png_ptr,
  959. info_ptr,
  960. bmi->bmiHeader.biWidth,
  961. bmi->bmiHeader.biHeight,
  962. 8, // bits per pixel
  963. PNG_COLOR_TYPE_RGB,
  964. PNG_INTERLACE_NONE,
  965. PNG_COMPRESSION_TYPE_DEFAULT,
  966. PNG_FILTER_TYPE_DEFAULT);
  967. // set up pointer to each row of data
  968. vector<png_byte *> row_pointers;
  969. row_pointers.resize (bmi->bmiHeader.biHeight);
  970. long bpl = BytesPerLine (bmi->bmiHeader.biWidth, 24);
  971. long row;
  972. unsigned char * p = pData;
  973. // have to reverse row order
  974. for (row = 0; row < info_ptr->height; row++, p += bpl)
  975. row_pointers [bmi->bmiHeader.biHeight - row - 1] = p;
  976. // tell png where our pixel data is
  977. png_set_rows (png_ptr, info_ptr, &row_pointers [0]);
  978. // write out the image data
  979. png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_BGR, NULL);
  980. // wrap up
  981. png_write_end (png_ptr, info_ptr);
  982. // done with data
  983. png_destroy_write_struct (&png_ptr, &info_ptr);
  984. // done with file
  985. fclose (fp);
  986. return eOK;
  987. } // end of CMiniWindow::WritePng
  988. /*
  989. for monitors with 256 colours or less, consider:
  990. HDRAWDIB hdd = DrawDibOpen();
  991. DrawDibDraw(hdd, hPaintDC,0,0,BITMAP_WIDTH,BITMAP_HEIGHT,&m_BitmapInfo,m_pBits,0,0,BITMAP_WIDTH,BITMAP_HEIGHT,DDF_HALFTONE);
  992. DrawDibClose(hdd);
  993. */
  994. // draw a previously-loaded image into the window
  995. long CMiniWindow::DrawImage(LPCTSTR ImageId,
  996. long Left, long Top, long Right, long Bottom,
  997. short Mode,
  998. long SrcLeft, long SrcTop, long SrcRight, long SrcBottom)
  999. {
  1000. ImageMapIterator it = m_Images.find (ImageId);
  1001. if (it == m_Images.end ())
  1002. return eImageNotInstalled;
  1003. CBitmap * bitmap = it->second;
  1004. dc.SetBkMode (TRANSPARENT);
  1005. dc.SetStretchBltMode (HALFTONE); // looks better when squashed
  1006. SetBrushOrgEx(dc.m_hDC, 0, 0, NULL); // as recommended after SetStretchBltMode
  1007. BITMAP bi;
  1008. bitmap->GetBitmap(&bi);
  1009. CDC bmDC;
  1010. bmDC.CreateCompatibleDC(&dc);
  1011. CBitmap *pOldbmp = bmDC.SelectObject(bitmap);
  1012. // adjust so that -1 means 1 from right
  1013. if (SrcRight <= 0)
  1014. SrcRight = bi.bmWidth + SrcRight;
  1015. if (SrcBottom <= 0)
  1016. SrcBottom = bi.bmHeight + SrcBottom;
  1017. // calculate size of desired rectangle
  1018. long iWidth = SrcRight - SrcLeft;
  1019. long iHeight = SrcBottom - SrcTop;
  1020. if (iWidth > 0 && iHeight > 0) // sanity check
  1021. switch (Mode)
  1022. {
  1023. case 1: dc.BitBlt (Left, Top, iWidth, iHeight, &bmDC, SrcLeft, SrcTop, SRCCOPY);
  1024. break; // straight copy
  1025. case 2: dc.StretchBlt (Left, Top, FixRight (Right) - Left, FixBottom (Bottom) - Top, &bmDC,
  1026. SrcLeft, SrcTop, SrcRight - SrcLeft, SrcBottom - SrcTop, SRCCOPY);
  1027. break; // stretch
  1028. case 3: // transparency, och!
  1029. {
  1030. COLORREF crOldBack = dc.SetBkColor (RGB (255, 255, 255)); // white
  1031. COLORREF crOldText = dc.SetTextColor (RGB (0, 0, 0)); // black
  1032. CDC dcTrans; // transparency mask
  1033. // Create a memory dc for the mask
  1034. dcTrans.CreateCompatibleDC(&dc);
  1035. // Create the mask bitmap for the subset of the main image
  1036. CBitmap bitmapTrans;
  1037. bitmapTrans.CreateBitmap(iWidth, iHeight, 1, 1, NULL);
  1038. // Select the mask bitmap into the appropriate dc
  1039. CBitmap* pOldBitmapTrans = dcTrans.SelectObject(&bitmapTrans);
  1040. // Our transparent pixel will be at 0,0 (top left corner) of original image (not subimage)
  1041. COLORREF crOldBackground = bmDC.SetBkColor (::GetPixel (bmDC, 0, 0));
  1042. // Build mask based on transparent colour at location 0, 0
  1043. dcTrans.BitBlt (0, 0, iWidth, iHeight, &bmDC, SrcLeft, SrcTop, SRCCOPY);
  1044. // Do the work
  1045. dc.BitBlt (Left, Top, iWidth, iHeight, &bmDC, SrcLeft, SrcTop, SRCINVERT);
  1046. dc.BitBlt (Left, Top, iWidth, iHeight, &dcTrans, 0, 0, SRCAND);
  1047. dc.BitBlt (Left, Top, iWidth, iHeight, &bmDC, SrcLeft, SrcTop, SRCINVERT);
  1048. // Restore settings
  1049. dcTrans.SelectObject(pOldBitmapTrans);
  1050. dc.SetBkColor(crOldBack);
  1051. dc.SetTextColor(crOldText);
  1052. bmDC.SetBkColor(crOldBackground);
  1053. }
  1054. break;
  1055. default: return eBadParameter;
  1056. } // end of switch
  1057. bmDC.SelectObject(pOldbmp);
  1058. return eOK;
  1059. } // end of CMiniWindow::DrawImage
  1060. // return list of images
  1061. void CMiniWindow::ImageList(VARIANT & vaResult)
  1062. {
  1063. COleSafeArray sa; // for array list
  1064. long iCount = 0;
  1065. // put the arrays into the array
  1066. if (!m_Images.empty ()) // cannot create empty dimension
  1067. {
  1068. sa.CreateOneDim (VT_VARIANT, m_Images.size ());
  1069. for (ImageMapIterator it = m_Images.begin ();
  1070. it != m_Images.end ();
  1071. it++)
  1072. {
  1073. // the array must be a bloody array of variants, or VBscript kicks up
  1074. COleVariant v (it->first.c_str ());
  1075. sa.PutElement (&iCount, &v);
  1076. iCount++;
  1077. }
  1078. } // end of having at least one
  1079. vaResult = sa.Detach ();
  1080. } // end of CMiniWindow::ImageList
  1081. // return info about the selected image
  1082. void CMiniWindow::ImageInfo (LPCTSTR ImageId, long InfoType, VARIANT & vaResult)
  1083. {
  1084. ImageMapIterator it = m_Images.find (ImageId);
  1085. if (it == m_Images.end ())
  1086. return; // no such Image
  1087. CBitmap * bitmap = it->second;
  1088. BITMAP bi;
  1089. bitmap->GetBitmap(&bi);
  1090. switch (InfoType)
  1091. {
  1092. case 1: SetUpVariantLong (vaResult, bi.bmType); break;
  1093. case 2: SetUpVariantLong (vaResult, bi.bmWidth); break;
  1094. case 3: SetUpVariantLong (vaResult, bi.bmHeight); break;
  1095. case 4: SetUpVariantLong (vaResult, bi.bmWidthBytes);break;
  1096. case 5: SetUpVariantLong (vaResult, bi.bmPlanes); break;
  1097. case 6: SetUpVariantLong (vaResult, bi.bmBitsPixel); break;
  1098. default:
  1099. vaResult.vt = VT_NULL;
  1100. break;
  1101. } // end of switch
  1102. } // end of CMiniWindow::ImageInfo
  1103. // draw bezier curves
  1104. long CMiniWindow::Bezier(LPCTSTR Points, long PenColour, long PenStyle, long PenWidth)
  1105. {
  1106. if (ValidatePenStyle (PenStyle, PenWidth))
  1107. return ePenStyleNotValid;
  1108. vector<string> v;
  1109. StringToVector (Points, v, ",");
  1110. int iCount = v.size ();
  1111. // must have at least a start point (2 items), plus one extra (3 points)
  1112. if (iCount < 8)
  1113. return eInvalidNumberOfPoints;
  1114. // and has to be 2 more than 6 items (1 more than 3n points)
  1115. if ((iCount % 6) != 2)
  1116. return eInvalidNumberOfPoints;
  1117. iCount = iCount / 2; // number of points
  1118. vector<POINT> points (iCount);
  1119. int iCurrent = 0;
  1120. for (vector<string>::const_iterator i = v.begin (); i != v.end (); i++)
  1121. {
  1122. if (!IsStringNumber (*i, true))
  1123. return eInvalidPoint;
  1124. points [iCurrent].x = atol (i->c_str ());
  1125. i++; // we know this is safe becaue of earlier check (we must have pairs of numbers)
  1126. if (!IsStringNumber (*i, true))
  1127. return eInvalidPoint;
  1128. points [iCurrent].y = atol (i->c_str ());
  1129. iCurrent++; // onto next point
  1130. }
  1131. dc.SetBkMode (TRANSPARENT);
  1132. // create requested pen
  1133. CPen pen;
  1134. MakeAPen (pen, PenColour, PenStyle, PenWidth);
  1135. CPen* oldPen = dc.SelectObject(&pen);
  1136. dc.PolyBezier(&points [0], iCount);
  1137. // put things back
  1138. dc.SelectObject (oldPen);
  1139. return eOK;
  1140. } // end of CMiniWindow::Bezier
  1141. // draw a polygon (straight lines)
  1142. long CMiniWindow::Polygon(LPCTSTR Points,
  1143. long PenColour, short PenStyle, long PenWidth,
  1144. long BrushColour, long BrushStyle,
  1145. BOOL Close,
  1146. BOOL Winding)
  1147. {
  1148. if (ValidatePenStyle (PenStyle, PenWidth))
  1149. return ePenStyleNotValid;
  1150. // validate and create requested bruch
  1151. CBrush br;
  1152. if (ValidateBrushStyle (BrushStyle, PenColour, BrushColour, br))
  1153. return eBrushStyleNotValid;
  1154. vector<string> v;
  1155. StringToVector (Points, v, ",");
  1156. int iCount = v.size ();
  1157. // must have at least a start point (2 items), plus one extra (1 point)
  1158. if (iCount < 4)
  1159. return eInvalidNumberOfPoints;
  1160. // it has to be divisible by 2
  1161. if ((iCount % 2) != 0)
  1162. return eInvalidNumberOfPoints;
  1163. iCount = iCount / 2; // number of points
  1164. vector<POINT> points (iCount);
  1165. int iCurrent = 0;
  1166. for (vector<string>::const_iterator i = v.begin (); i != v.end (); i++)
  1167. {
  1168. if (!IsStringNumber (*i, true))
  1169. return eInvalidPoint;
  1170. points [iCurrent].x = atol (i->c_str ());
  1171. i++; // we know this is safe becaue of earlier check (we must have pairs of numbers)
  1172. if (!IsStringNumber (*i, true))
  1173. return eInvalidPoint;
  1174. points [iCurrent].y = atol (i->c_str ());
  1175. iCurrent++; // onto next point
  1176. }
  1177. // create requested pen
  1178. CPen pen;
  1179. MakeAPen (pen, PenColour, PenStyle, PenWidth);
  1180. // select pen and brush into device context
  1181. CPen* oldPen = dc.SelectObject(&pen);
  1182. CBrush* oldBrush = dc.SelectObject(&br);
  1183. dc.SetPolyFillMode (Winding ? WINDING : ALTERNATE);
  1184. if (BrushStyle > 1 && BrushStyle <= 7)
  1185. {
  1186. dc.SetBkColor (BrushColour); // for hatched brushes this is the background colour
  1187. }
  1188. else
  1189. if (BrushStyle > 7) // pattern brushes
  1190. {
  1191. dc.SetTextColor (BrushColour); // for patterned brushes
  1192. dc.SetBkColor (PenColour); // for hatched brushes and patterned brushes
  1193. }
  1194. if (BrushColour != -1)
  1195. dc.SetBkMode (OPAQUE);
  1196. else
  1197. dc.SetBkMode (TRANSPARENT);
  1198. if (Close)
  1199. dc.Polygon(&points [0], iCount);
  1200. else
  1201. dc.Polyline(&points [0], iCount);
  1202. // put things back
  1203. dc.SelectObject (oldPen);
  1204. dc.SelectObject (oldBrush);
  1205. return eOK;
  1206. } // end of CMiniWindow::Polygon
  1207. // reposition window
  1208. long CMiniWindow::Position(long Left, long Top,
  1209. short Position,
  1210. long Flags)
  1211. {
  1212. m_Location.x = Left ;
  1213. m_Location.y = Top ;
  1214. m_iPosition = Position ;
  1215. m_iFlags = Flags ;
  1216. return eOK;
  1217. } // end of CMiniWindow::Position
  1218. /*
  1219. Cursor values:
  1220. 0: arrow
  1221. 1: hand
  1222. 2: I-beam
  1223. 3: + symbol
  1224. 4: wait (hour-glass)
  1225. 5: up arrow
  1226. 6: arrow nw-se
  1227. 7: arrow ne-sw
  1228. 8: arrow e-w
  1229. 9: arrow n-s
  1230. 10: arrow - all ways
  1231. 11: (X) no, no, I won't do that, but ...
  1232. 12: help (? symbol)
  1233. Flags:
  1234. 0x01 : report all mouseovers, not just when first entering the hotspot
  1235. */
  1236. // add a hotspot for handling mouse-over, mouse up/down events
  1237. long CMiniWindow::AddHotspot(CMUSHclientDoc * pDoc,
  1238. LPCTSTR HotspotId,
  1239. string sPluginID,
  1240. long Left, long Top, long Right, long Bottom,
  1241. LPCTSTR MouseOver,
  1242. LPCTSTR CancelMouseOver,
  1243. LPCTSTR MouseDown,
  1244. LPCTSTR CancelMouseDown,
  1245. LPCTSTR MouseUp,
  1246. LPCTSTR TooltipText,
  1247. long Cursor,
  1248. long Flags)
  1249. {
  1250. if (strlen (MouseOver) > 0 && CheckLabel (MouseOver, true))
  1251. return eInvalidObjectLabel;
  1252. if (strlen (CancelMouseOver) > 0 && CheckLabel (CancelMouseOver, true))
  1253. return eInvalidObjectLabel;
  1254. if (strlen (MouseDown) > 0 && CheckLabel (MouseDown, true))
  1255. return eInvalidObjectLabel;
  1256. if (strlen (CancelMouseDown) > 0 && CheckLabel (CancelMouseDown, true))
  1257. return eInvalidObjectLabel;
  1258. if (strlen (MouseUp) > 0 && CheckLabel (MouseUp, true))
  1259. return eInvalidObjectLabel;
  1260. // can't switch plugins here :)
  1261. if (!m_sCallbackPlugin.empty () && m_sCallbackPlugin != sPluginID)
  1262. return eHotspotPluginChanged;
  1263. m_sCallbackPlugin = sPluginID;
  1264. HotspotMapIterator it = m_Hotspots.find (HotspotId);
  1265. if (it != m_Hotspots.end ())
  1266. {
  1267. delete it->second; // delete existing hotspot
  1268. m_Hotspots.erase (it);
  1269. if (m_sMouseOverHotspot == HotspotId)
  1270. m_sMouseOverHotspot.erase ();
  1271. if (m_sMouseDownHotspot == HotspotId)
  1272. m_sMouseDownHotspot.erase ();
  1273. }
  1274. CHotspot * pHotspot = new CHotspot;
  1275. pHotspot->m_rect = CRect (Left, Top, FixRight (Right), FixBottom (Bottom));
  1276. pHotspot->m_sMouseOver = MouseOver;
  1277. pHotspot->m_sCancelMouseOver = CancelMouseOver;
  1278. pHotspot->m_sMouseDown = MouseDown;
  1279. pHotspot->m_sCancelMouseDown = CancelMouseDown;
  1280. pHotspot->m_sMouseUp = MouseUp;
  1281. pHotspot->m_sTooltipText = TooltipText;
  1282. pHotspot->m_Cursor = Cursor;
  1283. pHotspot->m_Flags = Flags;
  1284. // if not in a plugin, look in main world for hotspot callbacks, and remember the dispatch ID
  1285. if (sPluginID.empty ())
  1286. {
  1287. CString strErrorMessage;
  1288. pHotspot->m_dispid_MouseOver = pDoc->GetProcedureDispid (MouseOver, "mouse over", "", strErrorMessage);
  1289. pHotspot->m_dispid_CancelMouseOver = pDoc->GetProcedureDispid (CancelMouseOver, "cancel mouse over", "", strErrorMessage);
  1290. pHotspot->m_dispid_MouseDown = pDoc->GetProcedureDispid (MouseDown, "mouse down", "", strErrorMessage);
  1291. pHotspot->m_dispid_CancelMouseDown = pDoc->GetProcedureDispid (CancelMouseDown, "cancel mouse down", "", strErrorMessage);
  1292. pHotspot->m_dispid_MouseUp = pDoc->GetProcedureDispid (MouseUp, "mouse up", "", strErrorMessage);
  1293. }
  1294. m_Hotspots [HotspotId] = pHotspot;
  1295. return eOK;
  1296. } // end of CMiniWindow::AddHotspot
  1297. // remove a previously-installed hotspot
  1298. long CMiniWindow::DeleteHotspot(LPCTSTR HotspotId)
  1299. {
  1300. HotspotMapIterator it = m_Hotspots.find (HotspotId);
  1301. if (it == m_Hotspots.end ())
  1302. return eHotspotNotInstalled; // no such hotspot
  1303. delete it->second;
  1304. m_Hotspots.erase (it);
  1305. if (m_sMouseOverHotspot == HotspotId)
  1306. m_sMouseOverHotspot.erase ();
  1307. if (m_sMouseDownHotspot == HotspotId)
  1308. m_sMouseDownHotspot.erase ();
  1309. if (m_Hotspots.empty ())
  1310. m_sCallbackPlugin.erase ();
  1311. return eOK;
  1312. } // end of CMiniWindow::DeleteHotspot
  1313. // return list of all hotspots in this miniwindow
  1314. void CMiniWindow::HotspotList(VARIANT & vaResult)
  1315. {
  1316. COleSafeArray sa; // for array list
  1317. long iCount = 0;
  1318. // put the arrays into the array
  1319. if (!m_Hotspots.empty ()) // cannot create empty dimension
  1320. {
  1321. sa.CreateOneDim (VT_VARIANT, m_Hotspots.size ());
  1322. for (HotspotMapIterator it = m_Hotspots.begin ();
  1323. it != m_Hotspots.end ();
  1324. it++)
  1325. {
  1326. // the array must be a bloody array of variants, or VBscript kicks up
  1327. COleVariant v (it->first.c_str ());
  1328. sa.PutElement (&iCount, &v);
  1329. iCount++;
  1330. }
  1331. } // end of having at least one
  1332. vaResult = sa.Detach ();
  1333. } // end of CMiniWindow::HotspotList
  1334. // delete all hotspots
  1335. long CMiniWindow::DeleteAllHotspots()
  1336. {
  1337. // delete our hotspots
  1338. for (HotspotMapIterator hit = m_Hotspots.begin ();
  1339. hit != m_Hotspots.end ();
  1340. hit++)
  1341. delete hit->second;
  1342. m_Hotspots.clear ();
  1343. m_sMouseOverHotspot.erase ();
  1344. m_sMouseDownHotspot.erase ();
  1345. m_sCallbackPlugin.erase ();
  1346. return eOK;
  1347. } // end of CMiniWindow::DeleteAllHotspots
  1348. // get information about a hotspot
  1349. void CMiniWindow::HotspotInfo(LPCTSTR HotspotId, long InfoType, VARIANT & vaResult)
  1350. {
  1351. HotspotMapIterator it = m_Hotspots.find (HotspotId);
  1352. if (it == m_Hotspots.end ())
  1353. return; // no such hotspot
  1354. CHotspot * pHotspot = it->second;
  1355. switch (InfoType)
  1356. {
  1357. case 1: SetUpVariantLong (vaResult, pHotspot->m_rect.left); break; // left
  1358. case 2: SetUpVariantLong (vaResult, pHotspot->m_rect.top); break; // top
  1359. case 3: SetUpVariantLong (vaResult, pHotspot->m_rect.right); break; // right
  1360. case 4: SetUpVariantLong (vaResult, pHotspot->m_rect.bottom); break; // bottom
  1361. case 5: SetUpVariantString (vaResult, pHotspot->m_sMouseOver.c_str ()); break;
  1362. case 6: SetUpVariantString (vaResult, pHotspot->m_sCancelMouseOver.c_str ()); break;
  1363. case 7: SetUpVariantString (vaResult, pHotspot->m_sMouseDown.c_str ()); break;
  1364. case 8: SetUpVariantString (vaResult, pHotspot->m_sCancelMouseDown.c_str ()); break;
  1365. case 9: SetUpVariantString (vaResult, pHotspot->m_sMouseUp.c_str ()); break;
  1366. case 10: SetUpVariantString (vaResult, pHotspot->m_sTooltipText.c_str ()); break;
  1367. case 11: SetUpVariantLong (vaResult, pHotspot->m_Cursor); break; // cursor code
  1368. case 12: SetUpVariantLong (vaResult, pHotspot->m_Flags); break; // flags
  1369. case 13: SetUpVariantString (vaResult, pHotspot->m_sMoveCallback.c_str ()); break; // drag-and-drop move callback
  1370. case 14: SetUpVariantString (vaResult, pHotspot->m_sReleaseCallback.c_str ()); break; // drag-and-drop release callback
  1371. case 15: SetUpVariantLong (vaResult, pHotspot->m_DragFlags); break; // drag-and-drop flags
  1372. default:
  1373. vaResult.vt = VT_NULL;
  1374. break;
  1375. } // end of switch
  1376. } // end of CMiniWindow::HotspotInfo
  1377. long CMiniWindow::ImageOp(short Action,
  1378. long Left, long Top, long Right, long Bottom,
  1379. long PenColour, long PenStyle, long PenWidth,
  1380. long BrushColour, LPCTSTR ImageId,
  1381. long EllipseWidth, long EllipseHeight)
  1382. {
  1383. long iResult = eUnknownOption;
  1384. ImageMapIterator it = m_Images.find (ImageId);
  1385. if (it == m_Images.end ())
  1386. return eImageNotInstalled;
  1387. CBitmap * bitmap = it->second;
  1388. dc.SetBkMode (OPAQUE);
  1389. // for monochrome bitmaps, 1-bits will come out in pen colour, and 0-bits will come out in background colour
  1390. dc.SetTextColor (BrushColour); // for patterned brushes
  1391. dc.SetBkColor (PenColour); // for hatched brushes and patterned brushes
  1392. if (ValidatePenStyle (PenStyle, PenWidth))
  1393. return ePenStyleNotValid;
  1394. CBrush br;
  1395. br.CreatePatternBrush (bitmap);
  1396. // create requested pen
  1397. CPen pen;
  1398. MakeAPen (pen, PenColour, PenStyle, PenWidth);
  1399. // select into DC
  1400. CPen* oldPen = dc.SelectObject(&pen);
  1401. CBrush* oldBrush = dc.SelectObject(&br);
  1402. switch (Action)
  1403. {
  1404. case 1: // ellipse
  1405. {
  1406. dc.Ellipse (CRect (Left, Top, FixRight (Right), FixBottom (Bottom)));
  1407. iResult = eOK;
  1408. break;
  1409. }
  1410. case 2: // rectangle
  1411. {
  1412. dc.Rectangle (CRect (Left, Top, FixRight (Right), FixBottom (Bottom)));
  1413. iResult = eOK;
  1414. break;
  1415. }
  1416. case 3: // round rectangle
  1417. {
  1418. dc.RoundRect (CRect (Left, Top, FixRight (Right), FixBottom (Bottom)), CPoint (EllipseWidth, EllipseHeight));
  1419. iResult = eOK;
  1420. break;
  1421. }
  1422. } // end of switch
  1423. // put things back
  1424. dc.SelectObject (oldPen);
  1425. dc.SelectObject (oldBrush);
  1426. return iResult;
  1427. } // end of CMiniWindow::ImageOp
  1428. long CMiniWindow::CreateImage(LPCTSTR ImageId, long Row1, long Row2, long Row3, long Row4, long Row5, long Row6, long Row7, long Row8)
  1429. {
  1430. ImageMapIterator it = m_Images.find (ImageId);
  1431. if (it != m_Images.end ())
  1432. {
  1433. delete it->second; // delete existing image
  1434. m_Images.erase (it);
  1435. }
  1436. CBitmap * pImage = new CBitmap;
  1437. // bottom row comes first in a bitmap
  1438. WORD wBits[8] = { Row8, Row7, Row6, Row5, Row4, Row3, Row2, Row1 };
  1439. pImage->CreateBitmap (8, 8, 1, 1, wBits);
  1440. m_Images [ImageId] = pImage;
  1441. return eOK;
  1442. } // end of CMiniWindow::CreateImage
  1443. // see: http://www.nathanm.com/photoshop-blending-math/
  1444. // and: http://www.pegtop.net/delphi/articles/blendmodes/
  1445. #define Blend_It(Op) \
  1446. do \
  1447. if (Opacity < 1.0) \
  1448. for (i = 0; i < count; i++) \
  1449. pB [i] = Blend_Opacity (pA [i], pB [i], Op, Opacity);\
  1450. else\
  1451. for (i = 0; i < count; i ++)\
  1452. pB [i] = Op (pA [i], pB [i]);\
  1453. while (false)
  1454. // we have to do this a row at a time, because there might be a filler at the end of each row
  1455. // and thus, a discontinuity which throws out the r/g/b sequence
  1456. #define Colour_Op(fR,fG,fB) \
  1457. do \
  1458. { \
  1459. for (row = 0; row < iHeight; row++)\
  1460. {\
  1461. long base = row * perline;\
  1462. unsigned char rA, gA, bA, rB, gB, bB; \
  1463. if (Opacity < 1.0) \
  1464. for (i = 0; i < perline - 2; ) \
  1465. {\
  1466. bA = pA [base + i]; \
  1467. gA = pA [base + i + 1]; \
  1468. rA = pA [base + i + 2]; \
  1469. bB = pB [base + i]; \
  1470. gB = pB [base + i + 1]; \
  1471. rB = pB [base + i + 2]; \
  1472. pB [base + i] = Simple_Opacity (bB, fB, Opacity); \
  1473. i++;\
  1474. pB [base + i] = Simple_Opacity (gB, fG, Opacity); \
  1475. i++;\
  1476. pB [base + i] = Simple_Opacity (rB, fR, Opacity); \
  1477. i++;\
  1478. }\
  1479. else\
  1480. for (i = 0; i < perline - 2;)\
  1481. {\
  1482. bA = pA [base + i]; \
  1483. gA = pA [base + i + 1]; \
  1484. rA = pA [base + i + 2]; \
  1485. bB = pB [base + i]; \
  1486. gB = pB [base + i + 1]; \
  1487. rB = pB [base + i + 2]; \
  1488. pB [base + i] = fB; \
  1489. i++;\
  1490. pB [base + i] = fG; \
  1491. i++;\
  1492. pB [base + i] = fR; \
  1493. i++;\
  1494. }\
  1495. }\
  1496. }\
  1497. while (false)
  1498. // see also: CMUSHclientDoc::BlendPixel
  1499. long CMiniWindow::BlendImage(LPCTSTR ImageId,
  1500. long Left, long Top, long Right, long Bottom,
  1501. short Mode, double Opacity,
  1502. long SrcLeft, long SrcTop, long SrcRight, long SrcBottom)
  1503. {
  1504. // constrain to what we actually have
  1505. if (Left < 0)
  1506. Left = 0;
  1507. if (Top < 0)
  1508. Top = 0;
  1509. if (Right > m_iWidth)
  1510. Right = m_iWidth;
  1511. if (Bottom > m_iHeight)
  1512. Bottom = m_iHeight;
  1513. ImageMapIterator it = m_Images.find (ImageId);
  1514. if (it == m_Images.end ())
  1515. return eImageNotInstalled;
  1516. if (Opacity < 0.0 || Opacity > 1.0)
  1517. return eBadParameter;
  1518. CBitmap * bitmap = it->second;
  1519. BITMAP bi;
  1520. bitmap->GetBitmap(&bi);
  1521. // calculate size of desired rectangle
  1522. long iWidth = FixRight (Right) - Left;
  1523. long iHeight = FixBottom (Bottom) - Top;
  1524. // constrain to what we actually have
  1525. if (SrcLeft < 0)
  1526. SrcLeft = 0;
  1527. if (SrcTop < 0)
  1528. SrcTop = 0;
  1529. if (SrcRight > bi.bmWidth )
  1530. SrcRight = bi.bmWidth ;
  1531. if (SrcBottom > bi.bmHeight)
  1532. SrcBottom = bi.bmHeight;
  1533. // adjust so that -1 means 1 from right
  1534. if (SrcRight <= 0)
  1535. SrcRight = bi.bmWidth + SrcRight;
  1536. if (SrcBottom <= 0)
  1537. SrcBottom = bi.bmHeight + SrcBottom;
  1538. // width and height are the smaller of the two rectangles
  1539. iWidth = min (iWidth, SrcRight - SrcLeft);
  1540. iHeight = min (iHeight, SrcBottom - SrcTop);
  1541. if (iWidth <= 0 || iHeight <= 0) // sanity check
  1542. return eOK;
  1543. BITMAPINFO bmi;
  1544. ZeroMemory (&bmi, sizeof bmi);
  1545. bmi.bmiHeader.biSize = sizeof bmi;
  1546. bmi.bmiHeader.biWidth = iWidth;
  1547. bmi.bmiHeader.biHeight = iHeight;
  1548. bmi.bmiHeader.biPlanes = 1;
  1549. bmi.bmiHeader.biBitCount = 24;
  1550. bmi.bmiHeader.biCompression = BI_RGB;
  1551. bmi.bmiHeader.biSizeImage = iHeight * BytesPerLine (iWidth, 24);
  1552. // upper layer (from image id)
  1553. CDC A_DC;
  1554. A_DC.CreateCompatibleDC(&dc);
  1555. unsigned char * pA = NULL;
  1556. HBITMAP hbmA = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**) &pA, NULL, 0);
  1557. HBITMAP hOldAbmp = (HBITMAP) SelectObject(A_DC.m_hDC, hbmA);
  1558. //copy part from image to upper layer
  1559. CDC bmDC;
  1560. bmDC.CreateCompatibleDC(&dc);
  1561. CBitmap *pOldbmp = bmDC.SelectObject(bitmap);
  1562. A_DC.BitBlt (0, 0, iWidth, iHeight, &bmDC, SrcLeft, SrcTop, SRCCOPY);
  1563. bmDC.SelectObject(pOldbmp);
  1564. // base image (from miniwindow)
  1565. CDC B_DC;
  1566. B_DC.CreateCompatibleDC(&dc);
  1567. CBitmap B_bmp;
  1568. unsigned char * pB = NULL;
  1569. HBITMAP hbmB = CreateDIBSection(dc.m_hDC, &bmi, DIB_RGB_COLORS, (void**) &pB, NULL, 0);
  1570. HBITMAP hOldBbmp = (HBITMAP) SelectObject(B_DC.m_hDC, hbmB);
  1571. // copy base image from miniwindow to bitmap
  1572. B_DC.BitBlt (0, 0, iWidth, iHeight, &dc, Left, Top, SRCCOPY);
  1573. // manipulate image here A = blend, B = base
  1574. long count = bmi.bmiHeader.biSizeImage;
  1575. long perline = BytesPerLine (iWidth, 24);
  1576. long i, row;
  1577. // precompute cos table
  1578. static unsigned char cos_table [256];
  1579. static bool bTableComputed = false;
  1580. if (!bTableComputed)
  1581. {
  1582. double pi_div255 = 3.1415926535898 / 255.0;
  1583. for (i = 0; i < 256; i++)
  1584. {
  1585. double a = 64.0 - cos ((double) i * pi_div255) * 64.0;
  1586. cos_table [i] = (uint8) (a + 0.5); // round
  1587. }
  1588. bTableComputed = true;
  1589. } // table needs computing
  1590. switch (Mode)
  1591. {
  1592. // normal modes
  1593. case 1: Blend_It (Blend_Normal); break;
  1594. case 2: Blend_It (Blend_Average); break;
  1595. case 3: Blend_It (Blend_Interpolate); break;
  1596. case 4: // dissolve - randomly choose pixels based on opacity
  1597. {
  1598. for (row = 0; row < iHeight; row++)
  1599. {
  1600. long base = row * perline;
  1601. unsigned char rA, gA, bA, rB, gB, bB;
  1602. for (i = 0; i < perline - 2; )
  1603. {
  1604. double rnd = genrand ();
  1605. bA = pA [base + i];
  1606. gA = pA [base + i + 1];
  1607. rA = pA [base + i + 2];
  1608. bB = pB [base + i];
  1609. gB = pB [base + i + 1];
  1610. rB = pB [base + i + 2];
  1611. pB [base + i] = (rnd < Opacity) ? bA : bB;;
  1612. i++;
  1613. pB [base + i] = (rnd < Opacity) ? gA : gB;
  1614. i++;
  1615. pB [base + i] = (rnd < Opacity) ? rA : rB;
  1616. i++;
  1617. }
  1618. } // end for each row
  1619. }
  1620. break;
  1621. // darkening modes
  1622. case 5: Blend_It (Blend_Darken); break;
  1623. case 6: Blend_It (Blend_Multiply); break;
  1624. case 7: Blend_It (Blend_ColorBurn); break;
  1625. case 8: Blend_It (Blend_LinearBurn); break;
  1626. case 9: Blend_It (Blend_InverseColorBurn); break;
  1627. case 10: Blend_It (Blend_Subtract); break;
  1628. // lightening modes
  1629. case 11: Blend_It (Blend_Lighten); break;
  1630. case 12: Blend_It (Blend_Screen); break;
  1631. case 13: Blend_It (Blend_ColorDodge); break;
  1632. case 14: Blend_It (Blend_LinearDodge);break;
  1633. case 15: Blend_It (Blend_InverseColorDodge); break;
  1634. case 16: Blend_It (Blend_Add); break;
  1635. // soft/hard light etc.
  1636. case 17: Blend_It (Blend_Overlay); break;
  1637. case 18: Blend_It (Blend_SoftLight); break;
  1638. case 19: Blend_It (Blend_HardLight); break;
  1639. case 20: Blend_It (Blend_VividLight); break;
  1640. case 21: Blend_It (Blend_LinearLight);break;
  1641. case 22: Blend_It (Blend_PinLight); break;
  1642. case 23: Blend_It (Blend_HardMix); break;
  1643. // difference modes
  1644. case 24: Blend_It (Blend_Difference); break;
  1645. case 25: Blend_It (Blend_Exclusion); break;
  1646. // glow modes
  1647. case 26: Blend_It (Blend_Reflect); break;
  1648. case 27: Blend_It (Blend_Glow); break;
  1649. case 28: Blend_It (Blend_Freeze); break;
  1650. case 29: Blend_It (Blend_Heat); break;
  1651. case 30: Blend_It (Blend_Negation); break;
  1652. case 31: Blend_It (Blend_Phoenix); break;
  1653. case 32: Blend_It (Blend_Stamp); break;
  1654. // logical modes
  1655. case 33: Blend_It (Blend_Xor); break;
  1656. case 34: Blend_It (Blend_And); break;
  1657. case 35: Blend_It (Blend_Or); break;
  1658. // the follow modes take one colour from the blend and retain 2 from the base
  1659. case 36: Colour_Op (rA, gB, bB); break; // red
  1660. case 37: Colour_Op (rB, gA, bB); break; // green
  1661. case 38: Colour_Op (rB, gB, bA); break; // blue
  1662. // the follow modes take two colours from the blend and retain 1 from the base
  1663. case 39: Colour_Op (rA, gA, bB); break; // yellow
  1664. case 40: Colour_Op (rB, gA, bA); break; // cyan
  1665. case 41: Colour_Op (rA, gB, bA); break; // magenta
  1666. // limit green
  1667. case 42: Colour_Op (rA, (gA > rA) ? rA : gA, bA); break; // green limited by red
  1668. case 43: Colour_Op (rA, (gA > bA) ? bA : gA, bA); break; // green limited by blue
  1669. case 44: Colour_Op (rA, (gA > ((rA + bA) / 2)) ? ((rA + bA) / 2) : gA, bA); break; // green limited by average of red and blue
  1670. // limit blue
  1671. case 45: Colour_Op (rA, gA, (bA > rA) ? rA : bA); break; // blue limited by red
  1672. case 46: Colour_Op (rA, gA, (bA > gA) ? gA : bA); break; // blue limited by green
  1673. case 47: Colour_Op (rA, gA, (bA > ((rA + gA) / 2)) ? ((rA + gA) / 2) : bA); break; // blue limited by average of red and green
  1674. // limit red
  1675. case 48: Colour_Op ((rA > gA) ? gA : rA, gA, bA); break; // red limited by green
  1676. case 49: Colour_Op ((rA > bA) ? bA : rA, gA, bA); break; // red limited by blue
  1677. case 50: Colour_Op ((rA > ((gA + bA) / 2)) ? ((gA + bA) / 2) : rA, gA, bA); break; // red limited by average of green and blue
  1678. // select single colour
  1679. case 51: Colour_Op (rA, 0, 0); break; // red only (looks red)
  1680. case 52: Colour_Op (0, gA, 0); break; // green only (looks green)
  1681. case 53: Colour_Op (0, 0, bA); break; // blue only (looks blue)
  1682. // discard single colour
  1683. case 54: Colour_Op (0, gA, bA); break; // discard red (looks cyan)
  1684. case 55: Colour_Op (rA, 0, bA); break; // discard green (looks magenta)
  1685. case 56: Colour_Op (rA, gA, 0); break; // discard blue (looks yellow)
  1686. // one colour to all channels (ie. it looks grey)
  1687. case 57: Colour_Op (rA, rA, rA); break; // all red
  1688. case 58: Colour_Op (gA, gA, gA); break; // all green
  1689. case 59: Colour_Op (bA, bA, bA); break; // all blue
  1690. case 60: // Hue mode
  1691. {
  1692. for (row = 0; row < iHeight; row++)
  1693. {
  1694. long base = row * perline;
  1695. unsigned char rA, gA, bA, rB, gB, bB;
  1696. for (i = 0; i < perline - 2; )
  1697. {
  1698. CColor cA, cB; // A = blend, B = base
  1699. bA = pA [base + i];
  1700. gA = pA [base + i + 1];
  1701. rA = pA [base + i + 2];
  1702. bB = pB [base + i];
  1703. gB = pB [base + i + 1];
  1704. rB = pB [base + i + 2];
  1705. cA.SetRGB (rA, gA, bA);
  1706. cB.SetRGB (rB, gB, bB);
  1707. cB.SetHue (cA.GetHue ()); // hue of blend image, others from base image
  1708. pB [base + i] = Simple_Opacity (bB, cB.GetBlue (), Opacity);
  1709. i++;
  1710. pB [base + i] = Simple_Opacity (gB, cB.GetGreen (), Opacity);
  1711. i++;
  1712. pB [base + i] = Simple_Opacity (rB, cB.GetRed (), Opacity);
  1713. i++;
  1714. }
  1715. } // end for each row
  1716. }
  1717. break;
  1718. case 61: // Saturation mode
  1719. {
  1720. for (row = 0; row < iHeight; row++)
  1721. {
  1722. long base = row * perline;
  1723. unsigned char rA, gA, bA, rB, gB, bB;
  1724. for (i = 0; i < perline - 2; )
  1725. {
  1726. CColor cA, cB; // A = blend, B = base
  1727. bA = pA [base + i];
  1728. gA = pA [base + i + 1];
  1729. rA = pA [base + i + 2];
  1730. bB = pB [base + i];
  1731. gB = pB [base + i + 1];
  1732. rB = pB [base + i + 2];
  1733. cA.SetRGB (rA, gA, bA);
  1734. cB.SetRGB (rB, gB, bB);
  1735. cB.SetSaturation (cA.GetSaturation ()); // saturation of blend image, others from base image
  1736. pB [base + i] = Simple_Opacity (bB, cB.GetBlue (), Opacity);
  1737. i++;
  1738. pB [base + i] = Simple_Opacity (gB, cB.GetGreen (), Opacity);
  1739. i++;
  1740. pB [base + i] = Simple_Opacity (rB, cB.GetRed (), Opacity);
  1741. i++;
  1742. }
  1743. } // end for each row
  1744. }
  1745. break;
  1746. case 62: // Colour mode
  1747. {
  1748. for (row = 0; row < iHeight; row++)
  1749. {
  1750. long base = row * perline;
  1751. unsigned char rA, gA, bA, rB, gB, bB;
  1752. for (i = 0; i < perline - 2; )
  1753. {
  1754. CColor cA, cB; // A = blend, B = base
  1755. bA = pA [base + i];
  1756. gA = pA [base + i + 1];
  1757. rA = pA [base + i + 2];
  1758. bB = pB [base + i];
  1759. gB = pB [base + i + 1];
  1760. rB = pB [base + i + 2];
  1761. cA.SetRGB (rA, gA, bA);
  1762. cB.SetRGB (rB, gB, bB);
  1763. cB.SetHue (cA.GetHue ()); // hue of blend image,
  1764. cB.SetSaturation (cA.GetSaturation ()); // saturation of blend image, luminance from base image
  1765. pB [base + i] = Simple_Opacity (bB, cB.GetBlue (), Opacity);
  1766. i++;
  1767. pB [base + i] = Simple_Opacity (gB, cB.GetGreen (), Opacity);
  1768. i++;
  1769. pB [base + i] = Simple_Opacity (rB, cB.GetRed (), Opacity);
  1770. i++;
  1771. }
  1772. } // end for each row
  1773. }
  1774. break;
  1775. /*
  1776. Note: I don't get the same results here as using Photoshop on the same images. Not quite sure why,
  1777. some test code below seems to indicate that for all possible values of RGB, they map to HSL and back
  1778. again without error.
  1779. In the absence of any obvious bug, I am assuming that by plugging the Luminance from one colour into the
  1780. L channel of another, I am creating an out-of-gamut colour, which then cannot be represented properly.
  1781. I can't find, so far, any code that would limit the value to be in-gamut, so for now this Luminance (and
  1782. Colour, Saturation and Hue too, probably) should be regarded as experimental. - NJG - 2/Aug/2008.
  1783. */
  1784. case 63: // Luminance mode
  1785. {
  1786. for (row = 0; row < iHeight; row++)
  1787. {
  1788. long base = row * perline;
  1789. unsigned char rA, gA, bA, rB, gB, bB;
  1790. for (i = 0; i < perline - 2; )
  1791. {
  1792. CColor cA, cB; // A = blend, B = base
  1793. bA = pA [base + i];
  1794. gA = pA [base + i + 1];
  1795. rA = pA [base + i + 2];
  1796. bB = pB [base + i];
  1797. gB = pB [base + i + 1];
  1798. rB = pB [base + i + 2];
  1799. // bit of a fudge here, but what can you do?
  1800. // this is going to brighten the really dark pixels, however at least then they
  1801. // should retain their base colour, rather than be promoted up to something garish
  1802. // if (rB < 12) rB = 12;
  1803. // if (gB < 12) gB = 12;
  1804. // if (bB < 12) bB = 12;
  1805. cA.SetRGB (rA, gA, bA);
  1806. cB.SetRGB (rB, gB, bB);
  1807. cB.SetLuminance (cA.GetLuminance ()); // luminance from blend image, others from base image
  1808. pB [base + i] = Simple_Opacity (bB, cB.GetBlue (), Opacity);
  1809. i++;
  1810. pB [base + i] = Simple_Opacity (gB, cB.GetGreen (), Opacity);
  1811. i++;
  1812. pB [base + i] = Simple_Opacity (rB, cB.GetRed (), Opacity);
  1813. i++;
  1814. }
  1815. } // end for each row
  1816. }
  1817. break;
  1818. case 64: // HSL (hue to red, saturation to green, luminance to blue)
  1819. {
  1820. for (row = 0; row < iHeight; row++)
  1821. {
  1822. long base = row * perline;
  1823. unsigned char rA, gA, bA, rB, gB, bB;
  1824. for (i = 0; i < perline - 2; )
  1825. {
  1826. CColor cA, cB; // A = blend, B = base
  1827. bA = pA [base + i];
  1828. gA = pA [base + i + 1];
  1829. rA = pA [base + i + 2];
  1830. bB = pB [base + i];
  1831. gB = pB [base + i + 1];
  1832. rB = pB [base + i + 2];
  1833. cA.SetRGB (rA, gA, bA);
  1834. pB [base + i] = Simple_Opacity (bB, cA.GetLuminance () * 255, Opacity);
  1835. i++;
  1836. pB [base + i] = Simple_Opacity (gB, cA.GetSaturation () * 255, Opacity);
  1837. i++;
  1838. pB [base + i] = Simple_Opacity (rB, (cA.GetHue () / 360.0) * 255, Opacity);
  1839. i++;
  1840. }
  1841. } // end for each row
  1842. }
  1843. break;
  1844. /*
  1845. case 65: // testing conversion to/from RGB to HSL and back again is consistent.
  1846. {
  1847. int r, g, b;
  1848. CColor cA;
  1849. for (r = 0; r < 256; r ++)
  1850. for (g = 0; g < 256; g ++)
  1851. for (b = 0; b < 256; b ++)
  1852. {
  1853. cA.SetRGB (r, g, b);
  1854. cA.GetLuminance (); // force change to HSL
  1855. ASSERT (r == cA.GetRed ());
  1856. ASSERT (g == cA.GetGreen ());
  1857. ASSERT (b == cA.GetBlue ());
  1858. }
  1859. }
  1860. break;
  1861. */
  1862. default: return eUnknownOption;
  1863. }
  1864. // copy result back
  1865. dc.BitBlt (Left, Top, iWidth, iHeight, &B_DC, 0, 0, SRCCOPY);
  1866. SelectObject(A_DC.m_hDC, hOldAbmp);
  1867. SelectObject(B_DC.m_hDC, hOldBbmp);
  1868. DeleteObject (hbmA);
  1869. DeleteObject (hbmB);
  1870. return eOK;
  1871. } // end of CMiniWindow::BlendImage
  1872. // copies one miniwindow image as a stand-alone image
  1873. long CMiniWindow::ImageFromWindow(LPCTSTR ImageId, CMiniWindow * pSrcWindow)
  1874. {
  1875. ImageMapIterator it = m_Images.find (ImageId);
  1876. if (it != m_Images.end ())
  1877. {
  1878. delete it->second; // delete existing image
  1879. m_Images.erase (it);
  1880. }
  1881. // make new bitmap of appropriate size to hold image from other miniwindow
  1882. CBitmap * pImage = new CBitmap;
  1883. pImage->CreateCompatibleBitmap (&dc, pSrcWindow->m_iWidth, pSrcWindow->m_iHeight);
  1884. // prepare to copy it
  1885. CDC bmDC;
  1886. bmDC.CreateCompatibleDC(&dc);
  1887. CBitmap *pOldbmp = bmDC.SelectObject(pImage);
  1888. // copy into our bitmap
  1889. bmDC.BitBlt (0, 0, pSrcWindow->m_iWidth, pSrcWindow->m_iHeight, &pSrcWindow->dc, 0, 0, SRCCOPY);
  1890. // done
  1891. bmDC.SelectObject(pOldbmp);
  1892. // save in map
  1893. m_Images [ImageId] = pImage;
  1894. return eOK;
  1895. } // end of CMiniWindow::ImageFromWindow
  1896. static void HorizontalLinearGradient (const COLORREF StartColour,
  1897. const COLORREF EndColour,
  1898. unsigned char * pBuffer,
  1899. const long iWidth,
  1900. const long iHeight)
  1901. {
  1902. if (iWidth < 1)
  1903. return; // avoid divide by zero
  1904. double rdiff = GetRValue (EndColour) - GetRValue (StartColour),
  1905. gdiff = GetGValue (EndColour) - GetGValue (StartColour),
  1906. bdiff = GetBValue (EndColour) - GetBValue (StartColour);
  1907. double rval = GetRValue (StartColour),
  1908. gval = GetGValue (StartColour),
  1909. bval = GetBValue (StartColour);
  1910. double rinc = rdiff / (double) (iWidth - 1),
  1911. ginc = gdiff / (double) (iWidth - 1),
  1912. binc = bdiff / (double) (iWidth - 1);
  1913. long row, col;
  1914. uint8 r = 0, g = 0, b = 0;
  1915. int increment = BytesPerLine (iWidth, 24);
  1916. // main loop is columns
  1917. for (col = 0; col < iWidth; col++)
  1918. {
  1919. r = (uint8) rval;
  1920. g = (uint8) gval;
  1921. b = (uint8) bval;
  1922. unsigned char * p = pBuffer + col * 3;
  1923. for (row = 0; row < iHeight; row++)
  1924. {
  1925. p [0] = b;
  1926. p [1] = g;
  1927. p [2] = r;
  1928. p += increment;
  1929. } // end of each row
  1930. rval += rinc;
  1931. gval += ginc;
  1932. bval += binc;
  1933. } // end of each column
  1934. } // end of HorizontalLinearGradient
  1935. static void HorizontalLinearGradientHSL (const CColor StartColour,
  1936. const CColor EndColour,
  1937. unsigned char * pBuffer,
  1938. const long iWidth,
  1939. const long iHeight)
  1940. {
  1941. if (iWidth < 1)
  1942. return; // avoid divide by zero
  1943. double starthue = StartColour.GetHue ();
  1944. double endhue = EndColour.GetHue ();
  1945. double startlum = StartColour.GetLuminance ();
  1946. double endlum = EndColour.GetLuminance ();
  1947. double startsat = StartColour.GetSaturation ();
  1948. double endsat = EndColour.GetSaturation ();
  1949. double hinc = (endhue - starthue) / (double) (iWidth - 1),
  1950. linc = (endlum - startlum) / (double) (iWidth - 1),
  1951. sinc = (endsat - startsat) / (double) (iWidth - 1);
  1952. long row, col;
  1953. uint8 r = 0, g = 0, b = 0;
  1954. int increment = BytesPerLine (iWidth, 24);
  1955. // main loop is columns
  1956. for (col = 0; col < iWidth; col++)
  1957. {
  1958. double h = starthue;
  1959. double l = startlum;
  1960. double s = startsat;
  1961. CColor c;
  1962. c.SetHLS (h, l, s);
  1963. r = c.GetRed ();
  1964. g = c.GetGreen ();
  1965. b = c.GetBlue ();
  1966. unsigned char * p = pBuffer + col * 3;
  1967. for (row = 0; row < iHeight; row++)
  1968. {
  1969. p [0] = b;
  1970. p [1] = g;
  1971. p [2] = r;
  1972. p += increment;
  1973. } // end of each row
  1974. starthue += hinc;
  1975. startlum += linc;
  1976. startsat += sinc;
  1977. } // end of each column
  1978. } // end of HorizontalLinearGradient
  1979. static void VerticalLinearGradient (const COLORREF StartColour,
  1980. const COLORREF EndColour,
  1981. unsigned char * pBuffer,
  1982. const long iWidth,
  1983. const long iHeight)
  1984. {
  1985. if (iHeight < 1)
  1986. return; // avoid divide by zero
  1987. double rdiff = GetRValue (EndColour) - GetRValue (StartColour),
  1988. gdiff = GetGValue (EndColour) - GetGValue (StartColour),
  1989. bdiff = GetBValue (EndColour) - GetBValue (StartColour);
  1990. double rval = GetRValue (EndColour),
  1991. gval = GetGValue (EndColour),
  1992. bval = GetBValue (EndColour);
  1993. double rinc = - rdiff / (double) (iHeight - 1),
  1994. ginc = - gdiff / (double) (iHeight - 1),
  1995. binc = - bdiff / (double) (iHeight - 1);
  1996. long row, col;
  1997. uint8 r = 0, g = 0, b = 0;
  1998. int increment = BytesPerLine (iWidth, 24);
  1999. // main loop is rows
  2000. for (row = 0; row < iHeight; row++)
  2001. {
  2002. r = (uint8) rval;
  2003. g = (uint8) gval;
  2004. b = (uint8) bval;
  2005. unsigned char * p = pBuffer + increment * row;
  2006. for (col = 0; col < iWidth; col++)
  2007. {
  2008. p [0] = b;
  2009. p [1] = g;
  2010. p [2] = r;
  2011. p += 3;
  2012. } // end of each column
  2013. rval += rinc;
  2014. gval += ginc;
  2015. bval += binc;
  2016. } // end of each row
  2017. } // end of VerticalLinearGradient
  2018. static void MakeTexture (const COLORREF Multiplier,
  2019. unsigned char * pBuffer,
  2020. const long iWidth,
  2021. const long iHeight)
  2022. {
  2023. long rval = GetRValue (Multiplier),
  2024. gval = GetGValue (Multiplier),
  2025. bval = GetBValue (Multiplier);
  2026. long row, col;
  2027. long c;
  2028. int increment = BytesPerLine (iWidth, 24);
  2029. // main loop is columns
  2030. for (col = 0; col < iWidth; col++)
  2031. {
  2032. unsigned char * p = pBuffer + col * 3;
  2033. for (row = 0; row < iHeight; row++)
  2034. {
  2035. c = col ^ row;
  2036. p [0] = c * bval;
  2037. p [1] = c * gval;
  2038. p [2] = c * rval;
  2039. p += increment;
  2040. } // end of each row
  2041. } // end of each column
  2042. } // end of MakeTexture
  2043. // this is a load of old cobblers
  2044. #if 0
  2045. static void HorizontalLogGradient (const COLORREF StartColour, const double rdiff, const double gdiff, const double bdiff,
  2046. unsigned char * pBuffer,
  2047. const long iWidth,
  2048. const long iHeight,
  2049. const bool rightToLeft)
  2050. {
  2051. double rval = GetRValue (StartColour),
  2052. gval = GetGValue (StartColour),
  2053. bval = GetBValue (StartColour);
  2054. double rfactor = pow (abs (rdiff), (double) 1.0 / (double) iWidth);
  2055. double gfactor = pow (abs (gdiff), (double) 1.0 / (double) iWidth);
  2056. double bfactor = pow (abs (bdiff), (double) 1.0 / (double) iWidth);
  2057. long row, col;
  2058. uint8 r = 0, g = 0, b = 0;
  2059. double rinc = rfactor,
  2060. ginc = gfactor,
  2061. binc = bfactor;
  2062. int increment = BytesPerLine (iWidth, 24);
  2063. // main loop is columns
  2064. for (col = 0; col < iWidth; col++)
  2065. {
  2066. r = (uint8) rval;
  2067. g = (uint8) gval;
  2068. b = (uint8) bval;
  2069. unsigned char * p = pBuffer + col * 3;
  2070. for (row = 0; row < iHeight; row++)
  2071. {
  2072. p [0] = b;
  2073. p [1] = g;
  2074. p [2] = r;
  2075. p += increment;
  2076. } // end of each row
  2077. rinc *= rfactor;
  2078. ginc *= gfactor;
  2079. binc *= bfactor;
  2080. if (rightToLeft)
  2081. {
  2082. rval = GetRValue (StartColour) - rinc;
  2083. gval = GetGValue (StartColour) - ginc;
  2084. bval = GetBValue (StartColour) - binc;
  2085. }
  2086. else
  2087. {
  2088. rval = GetRValue (StartColour) + rinc;
  2089. gval = GetGValue (StartColour) + ginc;
  2090. bval = GetBValue (StartColour) + binc;
  2091. }
  2092. } // end of each column
  2093. } // end of HorizontalLogGradient
  2094. #endif // 0
  2095. long CMiniWindow::Gradient(long Left, long Top, long Right, long Bottom,
  2096. long StartColour, long EndColour,
  2097. short Mode)
  2098. {
  2099. // calculate size of desired rectangle
  2100. long iWidth = FixRight (Right) - Left;
  2101. long iHeight = FixBottom (Bottom) - Top;
  2102. if (iWidth <= 0 || iHeight <= 0) // sanity check
  2103. return eOK;
  2104. // upper layer (from image id)
  2105. CDC gDC;
  2106. gDC.CreateCompatibleDC(&dc);
  2107. CBitmap gbmp;
  2108. BITMAPINFO bmi;
  2109. ZeroMemory (&bmi, sizeof bmi);
  2110. bmi.bmiHeader.biSize = sizeof bmi;
  2111. bmi.bmiHeader.biWidth = iWidth;
  2112. bmi.bmiHeader.biHeight = iHeight;
  2113. bmi.bmiHeader.biPlanes = 1;
  2114. bmi.bmiHeader.biBitCount = 24;
  2115. bmi.bmiHeader.biCompression = BI_RGB;
  2116. bmi.bmiHeader.biSizeImage = iHeight * BytesPerLine (iWidth, 24);
  2117. unsigned char * pA = NULL;
  2118. HBITMAP hbmG = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**) &pA, NULL, 0);
  2119. HBITMAP hOldAbmp = (HBITMAP) SelectObject(gDC.m_hDC, hbmG);
  2120. switch (Mode)
  2121. {
  2122. case 1 : // horizontal (left to right)
  2123. HorizontalLinearGradient (StartColour, EndColour, pA, iWidth, iHeight);
  2124. break; // end of horizontal
  2125. case 2 : // vertical (top to bottom)
  2126. VerticalLinearGradient (StartColour, EndColour, pA, iWidth, iHeight);
  2127. break; // end of vertical
  2128. case 3 : // texture
  2129. MakeTexture (StartColour, pA, iWidth, iHeight);
  2130. break; // end of texture
  2131. // forget it!
  2132. #if 0
  2133. case 4 : // horizontal (left to right)
  2134. HorizontalLinearGradientHSL (CColor (StartColour), CColor (EndColour), pA, iWidth, iHeight);
  2135. break; // end of horizontal
  2136. case 5 : // horizontal (left to right)
  2137. HorizontalLogGradient (StartColour, rdiff, gdiff, bdiff, pA, iWidth, iHeight, false);
  2138. break; // end of horizontal
  2139. case 6 : // horizontal (right to left)
  2140. HorizontalLogGradient (EndColour, rdiff, gdiff, bdiff, pA, iWidth, iHeight, true);
  2141. break; // end of horizontal
  2142. #endif // 0
  2143. default: return eUnknownOption;
  2144. } // end of switch
  2145. // copy result back
  2146. dc.BitBlt (Left, Top, iWidth, iHeight, &gDC, 0, 0, SRCCOPY);
  2147. SelectObject(gDC.m_hDC, hOldAbmp);
  2148. DeleteObject (hbmG);
  2149. return eOK;
  2150. } // end of CMiniWindow::Gradient
  2151. static void GeneralFilter (unsigned char * inbuf, long iWidth, long iHeight,
  2152. const long iPerLine,
  2153. const double Options,
  2154. const double * iMatrix,
  2155. const double iDivisor)
  2156. {
  2157. unsigned char * pi;
  2158. long total;
  2159. long row, col, rgb;
  2160. int i;
  2161. int iLastByte = iWidth * 3;
  2162. long window [5];
  2163. if (Options != 2) // 2 = vertical only
  2164. {
  2165. // horizontal
  2166. // main loop is rows
  2167. for (row = 0; row < iHeight; row++)
  2168. {
  2169. pi = inbuf + iPerLine * row;
  2170. for (rgb = 0; rgb < 3; rgb++)
  2171. {
  2172. // preload 4 slots in window for edge case (-2 to +1)
  2173. for (col = 0; col < 4; col++)
  2174. window [col + 1] = pi [min (max (rgb + (col * 3) - 6, 0), iLastByte - 1)];
  2175. for (col = 0; col < iLastByte - 4; col += 3)
  2176. {
  2177. // we add 3 to get to the next pixel in that colour (R, G or B)
  2178. // insert right-most byte into window
  2179. window [0] = window [1];
  2180. window [1] = window [2];
  2181. window [2] = window [3];
  2182. window [3] = window [4];
  2183. window [4] = pi [min (max (col + rgb + 6, 0), iLastByte - 1)];
  2184. total = 0;
  2185. for (i = 0; i < 5; i++)
  2186. total += window [i] * iMatrix [i];
  2187. pi [col + rgb] = (uint8) (min (max (total / iDivisor, 0), 255));
  2188. } // end of each column
  2189. } // end of each colour
  2190. } // end of each row
  2191. } // horizontal wanted
  2192. if (Options != 1) // 1 = horizontal only
  2193. {
  2194. // vertical
  2195. // main loop is columns
  2196. for (col = 0; col < iLastByte; col++)
  2197. {
  2198. // preload 4 slots in window for edge case (-2 to +1)
  2199. for (row = 0; row < 4; row++)
  2200. {
  2201. long from = min (max (row - 2, 0), iHeight - 1);
  2202. pi = inbuf + col + (from * iPerLine);
  2203. window [row + 1] = *pi;
  2204. }
  2205. for (row = 0; row < iHeight; row++)
  2206. {
  2207. long from = min (max (row + 3, 0), iHeight - 1);
  2208. pi = inbuf + col + (from * iPerLine);
  2209. // insert right-most byte into window
  2210. window [0] = window [1];
  2211. window [1] = window [2];
  2212. window [2] = window [3];
  2213. window [3] = window [4];
  2214. window [4] = *pi;
  2215. total = 0;
  2216. for (i = 0; i < 5; i++)
  2217. total += window [i] * iMatrix [i];
  2218. pi = inbuf + col + (row * iPerLine);
  2219. pi [0] = (uint8) (min (max (total / iDivisor, 0), 255));
  2220. } // end of each row
  2221. } // end of each column
  2222. } // vertical wanted
  2223. } // end of Blur
  2224. static void Noise (unsigned char * inbuf, long iWidth, long iHeight, long iPerLine, double Options)
  2225. {
  2226. unsigned char * pi = inbuf;
  2227. long count = iPerLine * iHeight;
  2228. long i, c;
  2229. double threshold = Options / 100.0;
  2230. for (i = 0; i < count; i++)
  2231. {
  2232. c = *pi;
  2233. c += (128 - genrand () * 256) * threshold;
  2234. *pi++ = CLAMP (c);
  2235. }
  2236. } // end of Noise
  2237. static void MonoNoise (unsigned char * inbuf, long iWidth, long iHeight, long iPerLine, double Options)
  2238. {
  2239. long i, j, c, row;
  2240. double threshold = Options / 100.0;
  2241. for (row = 0; row < iHeight; row++)
  2242. {
  2243. unsigned char * pi = inbuf + (row * iPerLine);
  2244. for (i = 0; i < iWidth; i ++)
  2245. {
  2246. j = (128 - genrand () * 256) * threshold;
  2247. c = *pi + j;
  2248. *pi++ = CLAMP (c);
  2249. c = *pi + j;
  2250. *pi++ = CLAMP (c);
  2251. c = *pi + j;
  2252. *pi++ = CLAMP (c);
  2253. }
  2254. } // end for each row
  2255. } // end of MonoNoise
  2256. static void Brightness (unsigned char * inbuf, long iWidth, long iHeight, long iPerLine, double Options)
  2257. {
  2258. unsigned char * pi = inbuf;
  2259. long count = iPerLine * iHeight;
  2260. long i, c;
  2261. for (i = 0; i < count; i++)
  2262. {
  2263. c = *pi;
  2264. c += Options;
  2265. *pi++ = CLAMP (c);
  2266. }
  2267. } // end of Brightness
  2268. static void BrightnessMultiply (unsigned char * inbuf, long iWidth, long iHeight, long iPerLine, double Options)
  2269. {
  2270. unsigned char * pi = inbuf;
  2271. long count = iPerLine * iHeight;
  2272. long i, c;
  2273. for (i = 0; i < count; i++)
  2274. {
  2275. c = *pi;
  2276. c *= Options;
  2277. *pi++ = CLAMP (c);
  2278. }
  2279. } // end of BrightnessMultiply
  2280. static void Contrast (unsigned char * inbuf, long iWidth, long iHeight, long iPerLine, double Options)
  2281. {
  2282. unsigned char lookup [256]; // lookup table for speed
  2283. unsigned char * pi = inbuf;
  2284. long count = iPerLine * iHeight;
  2285. long i;
  2286. double c;
  2287. for (i = 0; i < 256; i++)
  2288. {
  2289. c = i - 128; // center on zero
  2290. c *= Options; // multiply by contrast
  2291. c += 128; // put back
  2292. lookup [i] = CLAMP (c);
  2293. } // end of for loop
  2294. // now convert image using lookup table
  2295. for (i = 0; i < count; i++)
  2296. *pi++ = lookup [*pi];
  2297. } // end of Contrast
  2298. static void Gamma (unsigned char * inbuf, long iWidth, long iHeight, long iPerLine, double Options)
  2299. {
  2300. unsigned char lookup [256]; // lookup table for speed
  2301. unsigned char * pi = inbuf;
  2302. long count = iPerLine * iHeight;
  2303. long i;
  2304. double c;
  2305. if (Options < 0.0)
  2306. Options = 0.0;
  2307. for (i = 0; i < 256; i++)
  2308. {
  2309. c = ( (double) i) / 255.0; // normalize it
  2310. c = pow (c, Options);
  2311. c *= 255;
  2312. lookup [i] = CLAMP (c);
  2313. } // end of for loop
  2314. // now convert image using lookup table
  2315. for (i = 0; i < count; i++)
  2316. *pi++ = lookup [*pi];
  2317. } // end of Gamma
  2318. static void ColourBrightness (unsigned char * inbuf, long iWidth, long iHeight,
  2319. long iPerLine, double Options, long iColour)
  2320. {
  2321. long i, row, c;
  2322. for (row = 0; row < iHeight; row++)
  2323. {
  2324. unsigned char * pi = inbuf + (row * iPerLine) + iColour;
  2325. for (i = 0; i < iWidth; i ++)
  2326. {
  2327. c = *pi;
  2328. c += Options;
  2329. *pi = CLAMP (c);
  2330. pi += 3;
  2331. }
  2332. } // end for each row
  2333. } // end of ColourBrightness
  2334. static void ColourBrightnessMultiply (unsigned char * inbuf, long iWidth, long iHeight,
  2335. long iPerLine, double Options, long iColour)
  2336. {
  2337. long i, row, c;
  2338. for (row = 0; row < iHeight; row++)
  2339. {
  2340. unsigned char * pi = inbuf + (row * iPerLine) + iColour;
  2341. for (i = 0; i < iWidth; i ++)
  2342. {
  2343. c = *pi;
  2344. c *= Options;
  2345. *pi = CLAMP (c);
  2346. pi += 3;
  2347. }
  2348. } // end for each row
  2349. } // end of ColourBrightnessMultiply
  2350. static void ColourContrast (unsigned char * inbuf, long iWidth, long iHeight,
  2351. long iPerLine, double Options, long iColour)
  2352. {
  2353. unsigned char lookup [256]; // lookup table for speed
  2354. long i, row;
  2355. double c;
  2356. for (i = 0; i < 256; i++)
  2357. {
  2358. c = i - 128; // center on zero
  2359. c *= Options; // multiply by contrast
  2360. c += 128; // put back
  2361. lookup [i] = CLAMP (c);
  2362. } // end of for loop
  2363. // now convert image using lookup table
  2364. for (row = 0; row < iHeight; row++)
  2365. {
  2366. unsigned char * pi = inbuf + (row * iPerLine) + iColour;
  2367. for (i = 0; i < iWidth; i ++)
  2368. {
  2369. *pi = lookup [*pi];
  2370. pi += 3;
  2371. }
  2372. } // end for each row
  2373. } // end of ColourContrast
  2374. static void ColourGamma (unsigned char * inbuf, long iWidth, long iHeight,
  2375. long iPerLine, double Options, long iColour)
  2376. {
  2377. unsigned char lookup [256]; // lookup table for speed
  2378. long i, row;
  2379. double c;
  2380. if (Options < 0.0)
  2381. Options = 0.0;
  2382. for (i = 0; i < 256; i++)
  2383. {
  2384. c = ( (double) i) / 255.0; // normalize it
  2385. c = pow (c, Options);
  2386. c *= 255;
  2387. lookup [i] = CLAMP (c);
  2388. } // end of for loop
  2389. // now convert image using lookup table
  2390. for (row = 0; row < iHeight; row++)
  2391. {
  2392. unsigned char * pi = inbuf + (row * iPerLine) + iColour;
  2393. for (i = 0; i < iWidth; i ++)
  2394. {
  2395. *pi = lookup [*pi];
  2396. pi += 3;
  2397. }
  2398. } // end for each row
  2399. } // end of ColourGamma
  2400. // see: http://en.wikipedia.org/wiki/Grayscale
  2401. static void MakeGreyscale (unsigned char * inbuf, long iWidth, long iHeight, long iPerLine, double Options, const bool bLinear)
  2402. {
  2403. long i, row;
  2404. double c;
  2405. for (row = 0; row < iHeight; row++)
  2406. {
  2407. unsigned char * pi = inbuf + (row * iPerLine);
  2408. for (i = 0; i < iWidth; i ++)
  2409. {
  2410. if (bLinear)
  2411. {
  2412. c = pi [0] + pi [1] + pi [2];
  2413. c /= 3;
  2414. }
  2415. else
  2416. c = pi [0] * 0.11 + // blue (perceptual)
  2417. pi [1] * 0.59 + // green
  2418. pi [2] * 0.30; // red
  2419. *pi++ = CLAMP (c);
  2420. *pi++ = CLAMP (c);
  2421. *pi++ = CLAMP (c);
  2422. }
  2423. } // end for each row
  2424. } // end of MakeGreyscale
  2425. static void Average (unsigned char * pBuffer,
  2426. const long iWidth,
  2427. const long iHeight)
  2428. {
  2429. long row, col;
  2430. __int64 r = 0, g = 0, b = 0, count = 0;
  2431. int increment = BytesPerLine (iWidth, 24);
  2432. // find average
  2433. // main loop is columns
  2434. for (col = 0; col < iWidth; col++)
  2435. {
  2436. unsigned char * p = pBuffer + col * 3;
  2437. for (row = 0; row < iHeight; row++)
  2438. {
  2439. b += p [0];
  2440. g += p [1];
  2441. r += p [2];
  2442. count++;
  2443. p += increment;
  2444. } // end of each row
  2445. } // end of each column
  2446. r /= count;
  2447. g /= count;
  2448. b /= count;
  2449. // apply average
  2450. // main loop is columns
  2451. for (col = 0; col < iWidth; col++)
  2452. {
  2453. unsigned char * p = pBuffer + col * 3;
  2454. for (row = 0; row < iHeight; row++)
  2455. {
  2456. p [0] = CLAMP (b);
  2457. p [1] = CLAMP (g);
  2458. p [2] = CLAMP (r);
  2459. p += increment;
  2460. } // end of each row
  2461. } // end of each column
  2462. } // end of Average
  2463. // see also: CMUSHclientDoc::FilterPixel
  2464. long CMiniWindow::Filter(long Left, long Top, long Right, long Bottom,
  2465. short Operation, double Options)
  2466. {
  2467. // constrain to what we actually have
  2468. if (Left < 0)
  2469. Left = 0;
  2470. if (Top < 0)
  2471. Top = 0;
  2472. if (Right > m_iWidth)
  2473. Right = m_iWidth;
  2474. if (Bottom > m_iHeight)
  2475. Bottom = m_iHeight;
  2476. // calculate size of desired rectangle
  2477. long iWidth = FixRight (Right) - Left;
  2478. long iHeight = FixBottom (Bottom) - Top;
  2479. if (iWidth < 1 || iHeight < 1) // sanity check
  2480. return eOK;
  2481. // upper layer (from image id)
  2482. CDC gDC;
  2483. gDC.CreateCompatibleDC(&dc);
  2484. CBitmap gbmp;
  2485. BITMAPINFO bmi;
  2486. ZeroMemory (&bmi, sizeof bmi);
  2487. bmi.bmiHeader.biSize = sizeof bmi;
  2488. bmi.bmiHeader.biWidth = iWidth;
  2489. bmi.bmiHeader.biHeight = iHeight;
  2490. bmi.bmiHeader.biPlanes = 1;
  2491. bmi.bmiHeader.biBitCount = 24;
  2492. bmi.bmiHeader.biCompression = BI_RGB;
  2493. bmi.bmiHeader.biSizeImage = iHeight * BytesPerLine (iWidth, 24);
  2494. unsigned char * pA = NULL;
  2495. HBITMAP hbmG = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**) &pA, NULL, 0);
  2496. HBITMAP hOldAbmp = (HBITMAP) SelectObject(gDC.m_hDC, hbmG);
  2497. // copy base image from miniwindow to bitmap
  2498. gDC.BitBlt (0, 0, iWidth, iHeight, &dc, Left, Top, SRCCOPY);
  2499. // manipulate image here
  2500. switch (Operation)
  2501. {
  2502. case 1: Noise (pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options); break;
  2503. case 2: MonoNoise(pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options); break;
  2504. case 3: // blur
  2505. {
  2506. double iMatrix [5] = { 1, 1, 1, 1, 1 };
  2507. GeneralFilter (pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options, iMatrix, 5);
  2508. }
  2509. break;
  2510. case 4: // sharpen
  2511. {
  2512. double iMatrix [5] = { -1, -1, 7, -1, -1 };
  2513. GeneralFilter (pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options, iMatrix, 3);
  2514. }
  2515. break;
  2516. case 5: // edge detect
  2517. {
  2518. double iMatrix [5] = { 0, 2.5, -6, 2.5, 0 };
  2519. GeneralFilter (pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options, iMatrix, 1);
  2520. }
  2521. break;
  2522. case 6: // emboss
  2523. {
  2524. double iMatrix [5] = { 1, 2, 1, -1, -2 };
  2525. GeneralFilter (pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options, iMatrix, 1);
  2526. }
  2527. break;
  2528. case 7: Brightness (pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options); break; // additive brightness
  2529. case 8: Contrast (pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options); break;
  2530. case 9: Gamma (pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options); break;
  2531. case 10: ColourBrightness (pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options, 2); break; // red
  2532. case 11: ColourContrast (pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options, 2); break; // red
  2533. case 12: ColourGamma (pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options, 2); break; // red
  2534. case 13: ColourBrightness (pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options, 1); break; // green
  2535. case 14: ColourContrast (pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options, 1); break; // green
  2536. case 15: ColourGamma (pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options, 1); break; // green
  2537. case 16: ColourBrightness (pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options, 0); break; // blue
  2538. case 17: ColourContrast (pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options, 0); break; // blue
  2539. case 18: ColourGamma (pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options, 0); break; // blue
  2540. case 19: MakeGreyscale (pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options, true); break; // linear
  2541. case 20: MakeGreyscale (pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options, false); break; // perceptual
  2542. case 21: BrightnessMultiply(pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options); break; // multiplicative brightness
  2543. case 22: ColourBrightnessMultiply(pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options, 2); break; // red
  2544. case 23: ColourBrightnessMultiply(pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options, 1); break; // green
  2545. case 24: ColourBrightnessMultiply(pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options, 0); break; // blue
  2546. case 25: // lesser blur
  2547. {
  2548. double iMatrix [5] = { 0, 1, 1, 1, 0 };
  2549. GeneralFilter (pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options, iMatrix, 3);
  2550. }
  2551. break;
  2552. case 26: // minor blur
  2553. {
  2554. double iMatrix [5] = { 0, 0.5, 1, 0.5, 0 };
  2555. GeneralFilter (pA, iWidth, iHeight, BytesPerLine (iWidth, 24), Options, iMatrix, 2);
  2556. }
  2557. break;
  2558. case 27: Average (pA, iWidth, iHeight); break;
  2559. default: return eUnknownOption;
  2560. }
  2561. // copy result back
  2562. dc.BitBlt (Left, Top, iWidth, iHeight, &gDC, 0, 0, SRCCOPY);
  2563. SelectObject(gDC.m_hDC, hOldAbmp);
  2564. DeleteObject (hbmG);
  2565. return eOK;
  2566. } // end of CMiniWindow::Filter
  2567. long CMiniWindow::SetPixel(long x, long y, long Colour)
  2568. {
  2569. dc.SetPixelV(x, y, Colour);
  2570. return eOK;
  2571. } // end of CMiniWindow::SetPixel
  2572. long CMiniWindow::GetPixel(long x, long y)
  2573. {
  2574. return dc.GetPixel(x, y);
  2575. } // end of CMiniWindow::GetPixel
  2576. long CMiniWindow::MergeImageAlpha(LPCTSTR ImageId, LPCTSTR MaskId,
  2577. long Left, long Top, long Right, long Bottom,
  2578. short Mode, double Opacity,
  2579. long SrcLeft, long SrcTop, long SrcRight, long SrcBottom)
  2580. {
  2581. // constrain to what we actually have
  2582. if (Left < 0)
  2583. Left = 0;
  2584. if (Top < 0)
  2585. Top = 0;
  2586. if (Right > m_iWidth)
  2587. Right = m_iWidth;
  2588. if (Bottom > m_iHeight)
  2589. Bottom = m_iHeight;
  2590. if (Opacity < 0.0 || Opacity > 1.0)
  2591. return eBadParameter;
  2592. // image to be merged
  2593. ImageMapIterator it = m_Images.find (ImageId);
  2594. if (it == m_Images.end ())
  2595. return eImageNotInstalled;
  2596. CBitmap * bitmap = it->second;
  2597. // mask image
  2598. it = m_Images.find (MaskId);
  2599. if (it == m_Images.end ())
  2600. return eImageNotInstalled;
  2601. CBitmap * mask_bitmap = it->second;
  2602. BITMAP bi;
  2603. bitmap->GetBitmap(&bi);
  2604. // calculate size of desired rectangle
  2605. long iWidth = FixRight (Right) - Left;
  2606. long iHeight = FixBottom (Bottom) - Top;
  2607. // constrain to what we actually have
  2608. if (SrcLeft < 0)
  2609. SrcLeft = 0;
  2610. if (SrcTop < 0)
  2611. SrcTop = 0;
  2612. if (SrcRight > bi.bmWidth )
  2613. SrcRight = bi.bmWidth ;
  2614. if (SrcBottom > bi.bmHeight)
  2615. SrcBottom = bi.bmHeight;
  2616. // adjust so that -1 means 1 from right
  2617. if (SrcRight <= 0)
  2618. SrcRight = bi.bmWidth + SrcRight;
  2619. if (SrcBottom <= 0)
  2620. SrcBottom = bi.bmHeight + SrcBottom;
  2621. // width and height are the smaller of the two rectangles
  2622. iWidth = min (iWidth, SrcRight - SrcLeft);
  2623. iHeight = min (iHeight, SrcBottom - SrcTop);
  2624. if (iWidth <= 0 || iHeight <= 0) // sanity check
  2625. return eOK;
  2626. mask_bitmap->GetBitmap(&bi);
  2627. // adjust down in case we don't use whole image
  2628. SrcRight = SrcLeft + iWidth;
  2629. SrcBottom = SrcTop + iHeight;
  2630. // mask must at least be as big as the image we are merging
  2631. if (bi.bmWidth < SrcRight)
  2632. return eBadParameter;
  2633. if (bi.bmHeight < SrcBottom)
  2634. return eBadParameter;
  2635. // merge layer (from image id)
  2636. CDC A_DC;
  2637. A_DC.CreateCompatibleDC(&dc);
  2638. BITMAPINFO bmi;
  2639. ZeroMemory (&bmi, sizeof bmi);
  2640. bmi.bmiHeader.biSize = sizeof bmi;
  2641. bmi.bmiHeader.biWidth = iWidth;
  2642. bmi.bmiHeader.biHeight = iHeight;
  2643. bmi.bmiHeader.biPlanes = 1;
  2644. bmi.bmiHeader.biBitCount = 24;
  2645. bmi.bmiHeader.biCompression = BI_RGB;
  2646. bmi.bmiHeader.biSizeImage = iHeight * BytesPerLine (iWidth, 24);
  2647. unsigned char * pA = NULL;
  2648. HBITMAP hbmA = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**) &pA, NULL, 0);
  2649. HBITMAP hOldAbmp = (HBITMAP) SelectObject(A_DC.m_hDC, hbmA);
  2650. CDC bmDC; // for loading bitmaps into
  2651. bmDC.CreateCompatibleDC(&dc);
  2652. //copy part from image to upper layer
  2653. CBitmap *pOldbmp = bmDC.SelectObject(bitmap);
  2654. A_DC.BitBlt (0, 0, iWidth, iHeight, &bmDC, SrcLeft, SrcTop, SRCCOPY);
  2655. bmDC.SelectObject(pOldbmp);
  2656. // base image (from miniwindow)
  2657. CDC B_DC;
  2658. B_DC.CreateCompatibleDC(&dc);
  2659. CBitmap B_bmp;
  2660. unsigned char * pB = NULL;
  2661. HBITMAP hbmB = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**) &pB, NULL, 0);
  2662. HBITMAP hOldBbmp = (HBITMAP) SelectObject(B_DC.m_hDC, hbmB);
  2663. // copy base image from miniwindow to bitmap
  2664. B_DC.BitBlt (0, 0, iWidth, iHeight, &dc, Left, Top, SRCCOPY);
  2665. // mask image
  2666. CDC M_DC;
  2667. M_DC.CreateCompatibleDC(&dc);
  2668. CBitmap M_bmp;
  2669. unsigned char * pM = NULL;
  2670. HBITMAP hbmM = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**) &pM, NULL, 0);
  2671. HBITMAP hOldMbmp = (HBITMAP) SelectObject(M_DC.m_hDC, hbmM);
  2672. // copy mask image from image to bitmap
  2673. pOldbmp = bmDC.SelectObject(mask_bitmap);
  2674. M_DC.BitBlt (0, 0, iWidth, iHeight, &bmDC, SrcLeft, SrcTop, SRCCOPY);
  2675. bmDC.SelectObject(pOldbmp);
  2676. long count = bmi.bmiHeader.biSizeImage;
  2677. long perline = BytesPerLine (iWidth, 24);
  2678. long i;
  2679. // do the merge
  2680. #define Blend_Mask(A,B,M) ((uint8) ((A * M + B * (255 - M)) / 255) )
  2681. switch (Mode)
  2682. {
  2683. case 0: // normal
  2684. if (Opacity < 1.0)
  2685. {
  2686. for (i = 0; i < count; i++)
  2687. {
  2688. pB [i] = Simple_Opacity (pB [i], Blend_Mask (pA [i], pB [i], pM [i]), Opacity);
  2689. }
  2690. }
  2691. else
  2692. {
  2693. for (i = 0; i < count; i ++)
  2694. pB [i] = Blend_Mask (pA [i], pB [i], pM [i]);
  2695. }
  2696. break; // end of mode 0
  2697. case 1: // transparent on pixel at 0,0
  2698. {
  2699. COLORREF opaque = ::GetPixel (A_DC, 0, 0);
  2700. long row;
  2701. for (row = 0; row < iHeight; row++)
  2702. {
  2703. long base = row * perline;
  2704. unsigned char rA, gA, bA, rB, gB, bB;
  2705. if (Opacity < 1.0)
  2706. for (i = 0; i < perline - 2; )
  2707. {
  2708. bA = pA [base + i];
  2709. gA = pA [base + i + 1];
  2710. rA = pA [base + i + 2];
  2711. bB = pB [base + i];
  2712. gB = pB [base + i + 1];
  2713. rB = pB [base + i + 2];
  2714. // if this pixel is the opaque one, take the base pixel instead
  2715. if (RGB (rA, gA, bA) == opaque)
  2716. {
  2717. rA = rB;
  2718. gA = gB;
  2719. bA = bB;
  2720. }
  2721. pB [base + i] = Simple_Opacity (bB, Blend_Mask (bA, bB, pM [base + i]), Opacity);
  2722. i++;
  2723. pB [base + i] = Simple_Opacity (gB, Blend_Mask (gA, gB, pM [base + i]), Opacity);
  2724. i++;
  2725. pB [base + i] = Simple_Opacity (rB, Blend_Mask (rA, rB, pM [base + i]), Opacity);
  2726. i++;
  2727. }
  2728. else
  2729. for (i = 0; i < perline - 2;)
  2730. {
  2731. bA = pA [base + i];
  2732. gA = pA [base + i + 1];
  2733. rA = pA [base + i + 2];
  2734. bB = pB [base + i];
  2735. gB = pB [base + i + 1];
  2736. rB = pB [base + i + 2];
  2737. // if this pixel is the opaque one, take the base pixel instead
  2738. if (RGB (rA, gA, bA) == opaque)
  2739. {
  2740. rA = rB;
  2741. gA = gB;
  2742. bA = bB;
  2743. }
  2744. pB [base + i] = Blend_Mask (bA, bB, pM [base + i]);
  2745. i++;
  2746. pB [base + i] = Blend_Mask (gA, gB, pM [base + i]);
  2747. i++;
  2748. pB [base + i] = Blend_Mask (rA, rB, pM [base + i]);
  2749. i++;
  2750. } // end Opacity == 1
  2751. } // end for loop
  2752. }
  2753. break; // end of mode 1
  2754. default: return eUnknownOption;
  2755. } // end of switch on Mode
  2756. // copy result back
  2757. dc.BitBlt (Left, Top, iWidth, iHeight, &B_DC, 0, 0, SRCCOPY);
  2758. SelectObject(A_DC.m_hDC, hOldAbmp);
  2759. SelectObject(B_DC.m_hDC, hOldBbmp);
  2760. SelectObject(M_DC.m_hDC, hOldMbmp);
  2761. DeleteObject (hbmA);
  2762. DeleteObject (hbmB);
  2763. DeleteObject (hbmM);
  2764. return eOK;
  2765. } // end of CMiniWindow::MergeImageAlpha
  2766. static CString strMXP_menu_item [MXP_MENU_COUNT];
  2767. CMenu* GrabAMenu (void)
  2768. {
  2769. CMenu menu;
  2770. VERIFY(menu.LoadMenu(IDR_MXP_MENU));
  2771. CMenu* pPopup = menu.GetSubMenu(0);
  2772. if (pPopup == NULL)
  2773. return NULL;
  2774. menu.Detach (); // CMenu is temporary on stack
  2775. pPopup->DeleteMenu (0, MF_BYPOSITION); // get rid of dummy item
  2776. return pPopup;
  2777. } // end of GrabAMenu
  2778. CString CMiniWindow::Menu(long Left, long Top, LPCTSTR Items, CMUSHView* pView)
  2779. {
  2780. CString strResult;
  2781. // can't menu if not visible
  2782. if (!m_bShow || m_bTemporarilyHide)
  2783. return strResult;
  2784. // can't if outside window
  2785. if (Left < 0 || Left > m_iWidth ||
  2786. Top < 0 || Top > m_iHeight)
  2787. return strResult;
  2788. // make relative to miniwindow
  2789. Left += m_rect.left;
  2790. Top += m_rect.top;
  2791. vector<string> v;
  2792. StringToVector (Items, v, "|");
  2793. int iCount = v.size ();
  2794. // must have at least one item
  2795. if (iCount < 1)
  2796. return strResult;
  2797. #if 0 // AARRRRRRRRGGGGGGGGGGGHHHHHHHHHHHHHHH!!!!!!!!!!!!!!!!!!!
  2798. // if we are doing this in response to a mouse-down, we have to cancel the
  2799. // mouse-down event now, or things get confused
  2800. if (!pView->m_sPreviousMiniWindow.empty ())
  2801. {
  2802. CMUSHclientDoc* pDoc = pView->GetDocument();
  2803. MiniWindowMapIterator it = pDoc->m_MiniWindows.find (pView->m_sPreviousMiniWindow);
  2804. if (it != pDoc->m_MiniWindows.end ())
  2805. {
  2806. CMiniWindow * old_mw = it->second;
  2807. // cancel previous move-down hotspot
  2808. if (!old_mw->m_sMouseDownHotspot.empty ()) // HotspotId was used
  2809. {
  2810. // lookup that HotspotId
  2811. HotspotMapIterator it = old_mw->m_Hotspots.find (old_mw->m_sMouseDownHotspot);
  2812. // call CancelMouseDown for that hotspot, if it exists
  2813. if (it != old_mw->m_Hotspots.end ())
  2814. {
  2815. m_last_mouseup = m_last_mousemove;
  2816. pView->Send_Mouse_Event_To_Plugin (old_mw->m_sCallbackPlugin,
  2817. it->second->m_sCancelMouseDown,
  2818. old_mw->m_sMouseDownHotspot);
  2819. }
  2820. old_mw->m_sMouseDownHotspot.erase (); // no mouse-down right now
  2821. } // we had previous hotspot
  2822. } // previous window still exists
  2823. pView->m_sPreviousMiniWindow.erase (); // no longer have a previous window
  2824. ReleaseCapture(); // Release the mouse capture established at
  2825. // the beginning of the mouse click.
  2826. } // released mouse in different window
  2827. #endif // AARRRRRRRRGGGGGGGGGGGHHHHHHHHHHHHHHH!!!!!!!!!!!!!!!!!!!
  2828. CPoint menupoint (Left, Top);
  2829. list<CMenu*> vPopup; // nested menus
  2830. CMenu* pPopup = GrabAMenu (); // top level
  2831. if (pPopup == NULL)
  2832. return strResult; // doesn't exist, strange
  2833. vPopup.push_back (pPopup);
  2834. CWnd* pWndPopupOwner = (CWnd *) pView;
  2835. while (pWndPopupOwner->GetStyle() & WS_CHILD)
  2836. pWndPopupOwner = pWndPopupOwner->GetParent();
  2837. ClientToScreen(pView->m_hWnd, &menupoint);
  2838. int j = 0;
  2839. for (vector<string>::const_iterator i = v.begin (); i != v.end (); i++)
  2840. {
  2841. CString strItem = i->c_str ();
  2842. if (*i == "-" || *i == "")
  2843. vPopup.back ()->AppendMenu (MF_SEPARATOR, 0, "");
  2844. else if (strItem.Left (1) == ">") // nested menu
  2845. {
  2846. CMenu* pNestedMenu = GrabAMenu (); // nested menu
  2847. if (pNestedMenu)
  2848. {
  2849. vPopup.back ()->AppendMenu (MF_POPUP | MF_ENABLED, (UINT ) pNestedMenu->m_hMenu, strItem.Mid (1));
  2850. vPopup.push_back (pNestedMenu); // add to menu stack
  2851. }
  2852. }
  2853. else if (strItem.Left (1) == "<") // un-nest menu
  2854. {
  2855. if (vPopup.size () > 1)
  2856. vPopup.pop_back (); // drop last item
  2857. }
  2858. else if (strItem.Left (1) == "^")
  2859. vPopup.back ()->AppendMenu (MF_STRING | MF_GRAYED, 0, strItem.Mid (1));
  2860. else
  2861. {
  2862. strMXP_menu_item [j] = strItem;
  2863. vPopup.back ()->AppendMenu (MF_STRING | MF_ENABLED, MXP_FIRST_MENU + j, strItem);
  2864. j++;
  2865. if (j >= MXP_MENU_COUNT)
  2866. break;
  2867. }
  2868. }
  2869. // without this line the auto-enable always set "no items" to active
  2870. Frame.m_bAutoMenuEnable = FALSE;
  2871. int iResult = vPopup.front ()->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD,
  2872. menupoint.x,
  2873. menupoint.y,
  2874. pWndPopupOwner);
  2875. // put things back how they were
  2876. Frame.m_bAutoMenuEnable = TRUE;
  2877. if (iResult > 0)
  2878. strResult = strMXP_menu_item [iResult - MXP_FIRST_MENU];
  2879. vPopup.front ()->DestroyMenu (); // clean up
  2880. return strResult; // will be empty for errors or cancelled
  2881. } // end of CMiniWindow::Menu
  2882. long CMiniWindow::DragHandler(CMUSHclientDoc * pDoc, LPCTSTR HotspotId, string sPluginID, LPCTSTR MoveCallback, LPCTSTR ReleaseCallback, long Flags)
  2883. {
  2884. if (strlen (MoveCallback) > 0 && CheckLabel (MoveCallback, true))
  2885. return eInvalidObjectLabel;
  2886. if (strlen (ReleaseCallback) > 0 && CheckLabel (ReleaseCallback, true))
  2887. return eInvalidObjectLabel;
  2888. // can't switch plugins here :)
  2889. if (!m_sCallbackPlugin.empty () && m_sCallbackPlugin != sPluginID)
  2890. return eHotspotPluginChanged;
  2891. HotspotMapIterator it = m_Hotspots.find (HotspotId);
  2892. if (it == m_Hotspots.end ())
  2893. return eHotspotNotInstalled; // no such hotspot
  2894. CHotspot * pHotspot = it->second;
  2895. pHotspot->m_sMoveCallback = MoveCallback;
  2896. pHotspot->m_sReleaseCallback = ReleaseCallback;
  2897. pHotspot->m_DragFlags = Flags;
  2898. // if not in a plugin, look in main world for hotspot callbacks, and remember the dispatch ID
  2899. if (sPluginID.empty ())
  2900. {
  2901. CString strErrorMessage;
  2902. pHotspot->m_dispid_MoveCallback = pDoc->GetProcedureDispid (MoveCallback, "mouse move", "", strErrorMessage);
  2903. pHotspot->m_dispid_ReleaseCallback = pDoc->GetProcedureDispid (ReleaseCallback, "mouse release", "", strErrorMessage);
  2904. }
  2905. return eOK;
  2906. } // end of CMiniWindow::DragHandler
  2907. // changes hotspot tooltip text
  2908. long CMiniWindow::HotspotTooltip(LPCTSTR HotspotId,
  2909. LPCTSTR TooltipText)
  2910. {
  2911. HotspotMapIterator it = m_Hotspots.find (HotspotId);
  2912. if (it == m_Hotspots.end ())
  2913. return eHotspotNotInstalled;
  2914. CHotspot * pHotspot = it->second;
  2915. pHotspot->m_sTooltipText = TooltipText;
  2916. return eOK;
  2917. } // end of CMiniWindow::HotspotTooltip
  2918. long CMiniWindow::DrawImageAlpha(LPCTSTR ImageId,
  2919. long Left, long Top, long Right, long Bottom,
  2920. double Opacity,
  2921. long SrcLeft, long SrcTop)
  2922. {
  2923. // constrain to what we actually have
  2924. if (Left < 0)
  2925. Left = 0;
  2926. if (Top < 0)
  2927. Top = 0;
  2928. if (Right > m_iWidth)
  2929. Right = m_iWidth;
  2930. if (Bottom > m_iHeight)
  2931. Bottom = m_iHeight;
  2932. if (Opacity < 0.0 || Opacity > 1.0)
  2933. return eBadParameter;
  2934. // image to be merged
  2935. ImageMapIterator it = m_Images.find (ImageId);
  2936. if (it == m_Images.end ())
  2937. return eImageNotInstalled;
  2938. CBitmap * bitmap = it->second;
  2939. BITMAP bi;
  2940. bitmap->GetBitmap(&bi);
  2941. // can't do it unless we have alpha channel
  2942. if (bi.bmBitsPixel != 32)
  2943. return eImageNotInstalled;
  2944. // calculate size of desired rectangle
  2945. long iWidth = FixRight (Right) - Left;
  2946. long iHeight = FixBottom (Bottom) - Top;
  2947. // constrain to what we actually have
  2948. if (SrcLeft < 0)
  2949. SrcLeft = 0;
  2950. if (SrcTop < 0)
  2951. SrcTop = 0;
  2952. if (iWidth <= 0 || iHeight <= 0) // sanity check
  2953. return eOK;
  2954. // merge layer (from image id)
  2955. CDC A_DC;
  2956. A_DC.CreateCompatibleDC(&dc);
  2957. BITMAPINFO bmi;
  2958. ZeroMemory (&bmi, sizeof bmi);
  2959. bmi.bmiHeader.biSize = sizeof bmi;
  2960. bmi.bmiHeader.biWidth = iWidth;
  2961. bmi.bmiHeader.biHeight = iHeight;
  2962. bmi.bmiHeader.biPlanes = 1;
  2963. bmi.bmiHeader.biBitCount = 32;
  2964. bmi.bmiHeader.biCompression = BI_RGB;
  2965. bmi.bmiHeader.biSizeImage = iHeight * BytesPerLine (iWidth, 32);
  2966. unsigned char * pA = NULL;
  2967. HBITMAP hbmA = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**) &pA, NULL, 0);
  2968. HBITMAP hOldAbmp = (HBITMAP) SelectObject(A_DC.m_hDC, hbmA);
  2969. CDC bmDC; // for loading bitmaps into
  2970. bmDC.CreateCompatibleDC(&dc);
  2971. //copy part from image to upper layer
  2972. CBitmap *pOldbmp = bmDC.SelectObject(bitmap);
  2973. A_DC.BitBlt (0, 0, iWidth, iHeight, &bmDC, SrcLeft, SrcTop, SRCCOPY);
  2974. bmDC.SelectObject(pOldbmp);
  2975. // base image (from miniwindow)
  2976. CDC B_DC;
  2977. B_DC.CreateCompatibleDC(&dc);
  2978. CBitmap B_bmp;
  2979. unsigned char * pB = NULL;
  2980. HBITMAP hbmB = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**) &pB, NULL, 0);
  2981. HBITMAP hOldBbmp = (HBITMAP) SelectObject(B_DC.m_hDC, hbmB);
  2982. // copy base image from miniwindow to bitmap
  2983. B_DC.BitBlt (0, 0, iWidth, iHeight, &dc, Left, Top, SRCCOPY);
  2984. long count = bmi.bmiHeader.biSizeImage;
  2985. long i;
  2986. // do the merge
  2987. #define Blend_Mask(A,B,M) ((uint8) ((A * M + B * (255 - M)) / 255) )
  2988. if (Opacity < 1.0)
  2989. {
  2990. for (i = 0; i < count; i += 4)
  2991. {
  2992. pB [i] = Simple_Opacity (pB [i], Blend_Mask (pA [i], pB [i], pA [i + 3]), Opacity);
  2993. pB [i + 1] = Simple_Opacity (pB [i + 1], Blend_Mask (pA [i + 1], pB [i + 1], pA [i + 3]), Opacity);
  2994. pB [i + 2] = Simple_Opacity (pB [i + 2], Blend_Mask (pA [i + 2], pB [i + 2], pA [i + 3]), Opacity);
  2995. }
  2996. }
  2997. else
  2998. {
  2999. for (i = 0; i < count; i += 4)
  3000. {
  3001. pB [i] = Blend_Mask (pA [i], pB [i], pA [i + 3]);
  3002. pB [i + 1] = Blend_Mask (pA [i + 1], pB [i + 1], pA [i + 3]);
  3003. pB [i + 2] = Blend_Mask (pA [i + 2], pB [i + 2], pA [i + 3]);
  3004. }
  3005. }
  3006. // copy result back
  3007. dc.BitBlt (Left, Top, iWidth, iHeight, &B_DC, 0, 0, SRCCOPY);
  3008. SelectObject(A_DC.m_hDC, hOldAbmp);
  3009. SelectObject(B_DC.m_hDC, hOldBbmp);
  3010. DeleteObject (hbmA);
  3011. DeleteObject (hbmB);
  3012. return eOK;
  3013. } // end of CMiniWindow::DrawImageAlpha
  3014. long CMiniWindow::GetImageAlpha(LPCTSTR ImageId,
  3015. long Left, long Top, long Right, long Bottom,
  3016. long SrcLeft, long SrcTop)
  3017. {
  3018. // constrain to what we actually have
  3019. if (Left < 0)
  3020. Left = 0;
  3021. if (Top < 0)
  3022. Top = 0;
  3023. if (Right > m_iWidth)
  3024. Right = m_iWidth;
  3025. if (Bottom > m_iHeight)
  3026. Bottom = m_iHeight;
  3027. // image to be merged
  3028. ImageMapIterator it = m_Images.find (ImageId);
  3029. if (it == m_Images.end ())
  3030. return eImageNotInstalled;
  3031. CBitmap * bitmap = it->second;
  3032. BITMAP bi;
  3033. bitmap->GetBitmap(&bi);
  3034. // can't do it unless we have alpha channel
  3035. if (bi.bmBitsPixel != 32)
  3036. return eImageNotInstalled;
  3037. // calculate size of desired rectangle
  3038. long iWidth = FixRight (Right) - Left;
  3039. long iHeight = FixBottom (Bottom) - Top;
  3040. // constrain to what we actually have
  3041. if (SrcLeft < 0)
  3042. SrcLeft = 0;
  3043. if (SrcTop < 0)
  3044. SrcTop = 0;
  3045. if (iWidth <= 0 || iHeight <= 0) // sanity check
  3046. return eOK;
  3047. // merge layer (from image id)
  3048. CDC A_DC;
  3049. A_DC.CreateCompatibleDC(&dc);
  3050. BITMAPINFO bmi;
  3051. ZeroMemory (&bmi, sizeof bmi);
  3052. bmi.bmiHeader.biSize = sizeof bmi;
  3053. bmi.bmiHeader.biWidth = iWidth;
  3054. bmi.bmiHeader.biHeight = iHeight;
  3055. bmi.bmiHeader.biPlanes = 1;
  3056. bmi.bmiHeader.biBitCount = 32;
  3057. bmi.bmiHeader.biCompression = BI_RGB;
  3058. bmi.bmiHeader.biSizeImage = iHeight * BytesPerLine (iWidth, 32);
  3059. unsigned char * pA = NULL;
  3060. HBITMAP hbmA = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**) &pA, NULL, 0);
  3061. HBITMAP hOldAbmp = (HBITMAP) SelectObject(A_DC.m_hDC, hbmA);
  3062. CDC bmDC; // for loading bitmaps into
  3063. bmDC.CreateCompatibleDC(&dc);
  3064. //copy part from image to upper layer
  3065. CBitmap *pOldbmp = bmDC.SelectObject(bitmap);
  3066. A_DC.BitBlt (0, 0, iWidth, iHeight, &bmDC, SrcLeft, SrcTop, SRCCOPY);
  3067. bmDC.SelectObject(pOldbmp);
  3068. // base image (from miniwindow)
  3069. CDC B_DC;
  3070. B_DC.CreateCompatibleDC(&dc);
  3071. CBitmap B_bmp;
  3072. unsigned char * pB = NULL;
  3073. HBITMAP hbmB = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**) &pB, NULL, 0);
  3074. HBITMAP hOldBbmp = (HBITMAP) SelectObject(B_DC.m_hDC, hbmB);
  3075. long count = bmi.bmiHeader.biSizeImage;
  3076. long i;
  3077. // copy alpha channel into window
  3078. for (i = 0; i < count; i += 4)
  3079. {
  3080. pB [i] = pA [i + 3];
  3081. pB [i + 1] = pA [i + 3];
  3082. pB [i + 2] = pA [i + 3];
  3083. }
  3084. // copy result back
  3085. dc.BitBlt (Left, Top, iWidth, iHeight, &B_DC, 0, 0, SRCCOPY);
  3086. SelectObject(A_DC.m_hDC, hOldAbmp);
  3087. SelectObject(B_DC.m_hDC, hOldBbmp);
  3088. DeleteObject (hbmA);
  3089. DeleteObject (hbmB);
  3090. return eOK;
  3091. } // end of CMiniWindow::GetImageAlpha
  3092. long CMiniWindow::ScrollwheelHandler(CMUSHclientDoc * pDoc, LPCTSTR HotspotId, string sPluginID, LPCTSTR MoveCallback)
  3093. {
  3094. if (strlen (MoveCallback) > 0 && CheckLabel (MoveCallback, true))
  3095. return eInvalidObjectLabel;
  3096. // can't switch plugins here :)
  3097. if (!m_sCallbackPlugin.empty () && m_sCallbackPlugin != sPluginID)
  3098. return eHotspotPluginChanged;
  3099. HotspotMapIterator it = m_Hotspots.find (HotspotId);
  3100. if (it == m_Hotspots.end ())
  3101. return eHotspotNotInstalled; // no such hotspot
  3102. CHotspot * pHotspot = it->second;
  3103. pHotspot->m_sScrollwheelCallback = MoveCallback;
  3104. // if not in a plugin, look in main world for hotspot callbacks, and remember the dispatch ID
  3105. if (sPluginID.empty ())
  3106. {
  3107. CString strErrorMessage;
  3108. pHotspot->m_dispid_ScrollwheelCallback = pDoc->GetProcedureDispid (MoveCallback, "scroll wheel", "", strErrorMessage);
  3109. }
  3110. return eOK;
  3111. } // end of CMiniWindow::ScrollwheelHandler
  3112. // resize a window
  3113. long CMiniWindow::Resize(long Width, long Height, long BackgroundColour)
  3114. {
  3115. // no change to size? wow, that was easy ...
  3116. if (Width == m_iWidth && Height == m_iHeight)
  3117. return eOK;
  3118. // remember new width and height
  3119. m_iWidth = Width ;
  3120. m_iHeight = Height;
  3121. CDC bmDC; // for loading bitmaps into
  3122. bmDC.CreateCompatibleDC(&dc);
  3123. // select original bitmap out of device context
  3124. dc.SelectObject(m_oldBitmap);
  3125. // save old bitmap for copying from
  3126. CBitmap * previousWindowBitmap = m_Bitmap;
  3127. // select into new device context
  3128. CBitmap * pOldbmp = bmDC.SelectObject (previousWindowBitmap);
  3129. // make new bitmap for different size
  3130. m_Bitmap = new CBitmap;
  3131. // CreateBitmap with zero-dimensions creates a monochrome bitmap, so force to be at least 1x1
  3132. m_Bitmap->CreateBitmap (MAX (m_iWidth, 1), MAX (m_iHeight, 1), 1, GetDeviceCaps(dc, BITSPIXEL), NULL);
  3133. m_oldBitmap = dc.SelectObject (m_Bitmap);
  3134. dc.SetWindowOrg(0, 0);
  3135. // fill with requested border colour
  3136. dc.FillSolidRect (0, 0, m_iWidth, m_iHeight, BackgroundColour);
  3137. // copy old contents back
  3138. dc.BitBlt (0, 0, m_iWidth, m_iHeight, &bmDC, 0, 0, SRCCOPY);
  3139. bmDC.SelectObject(pOldbmp);
  3140. // done with previous bitmap from this miniwindow
  3141. previousWindowBitmap->DeleteObject ();
  3142. delete previousWindowBitmap;
  3143. return eOK;
  3144. } // end of CMiniWindow::Resize
  3145. // move a hotspot (maybe the window was resized)
  3146. long CMiniWindow::MoveHotspot(LPCTSTR HotspotId,
  3147. long Left, long Top, long Right, long Bottom)
  3148. {
  3149. HotspotMapIterator it = m_Hotspots.find (HotspotId);
  3150. if (it == m_Hotspots.end ())
  3151. return eHotspotNotInstalled;
  3152. it->second->m_rect = CRect (Left, Top, FixRight (Right), FixBottom (Bottom));
  3153. return eOK;
  3154. } // end of CMiniWindow::MoveHotspot
  3155. long CMiniWindow::TransformImage(LPCTSTR ImageId, float Left, float Top, short Mode, float Mxx, float Mxy, float Myx, float Myy)
  3156. {
  3157. ImageMapIterator it = m_Images.find (ImageId);
  3158. if (it == m_Images.end ())
  3159. return eImageNotInstalled;
  3160. CBitmap * bitmap = it->second;
  3161. dc.SetBkMode (TRANSPARENT);
  3162. BITMAP bi;
  3163. bitmap->GetBitmap(&bi);
  3164. CDC bmDC;
  3165. bmDC.CreateCompatibleDC(&dc);
  3166. CBitmap *pOldbmp = bmDC.SelectObject(bitmap);
  3167. long iWidth = bi.bmWidth;
  3168. long iHeight = bi.bmHeight;
  3169. if (iWidth <= 0 || iHeight <= 0) // Sanity Claus
  3170. {
  3171. bmDC.SelectObject(pOldbmp);
  3172. return eOK;
  3173. }
  3174. // need advanced mode to do SetWorldTransform successfully
  3175. if (SetGraphicsMode (dc.m_hDC, GM_ADVANCED) == 0)
  3176. {
  3177. bmDC.SelectObject (pOldbmp);
  3178. return eBadParameter;
  3179. }
  3180. // x' = x * Mxx + y * Mxy + Left
  3181. // y' = x * Myx + y * Myy + Top
  3182. XFORM xform;
  3183. xform.eM11 = Mxx; // cosine
  3184. xform.eM21 = Mxy; // sine
  3185. xform.eM12 = Myx; // - sine
  3186. xform.eM22 = Myy; // cosine
  3187. xform.eDx = Left;
  3188. xform.eDy = Top;
  3189. if (SetWorldTransform (dc.m_hDC, &xform) == 0)
  3190. {
  3191. bmDC.SelectObject(pOldbmp);
  3192. return eBadParameter;
  3193. }
  3194. // gives smoother rotations, especially with transparency
  3195. dc.SetStretchBltMode (HALFTONE);
  3196. SetBrushOrgEx(dc.m_hDC, 0, 0, NULL); // as recommended after SetStretchBltMode
  3197. switch (Mode)
  3198. {
  3199. case 1: dc.BitBlt (0, 0, iWidth, iHeight, &bmDC, 0, 0, SRCCOPY);
  3200. break; // straight copy
  3201. case 3: // transparency, nom nom nom!
  3202. {
  3203. COLORREF crOldBack = dc.SetBkColor (RGB (255, 255, 255)); // white
  3204. COLORREF crOldText = dc.SetTextColor (RGB (0, 0, 0)); // black
  3205. CDC dcTrans; // transparency mask
  3206. // Create a memory dc for the mask
  3207. dcTrans.CreateCompatibleDC(&dc);
  3208. // Create the mask bitmap
  3209. CBitmap bitmapTrans;
  3210. bitmapTrans.CreateBitmap(m_iWidth, m_iHeight, 1, 1, NULL);
  3211. // Select the mask bitmap into the appropriate dc
  3212. CBitmap* pOldBitmapTrans = dcTrans.SelectObject(&bitmapTrans);
  3213. // Our transparent pixel will be at 0,0 (top left corner) of original image
  3214. COLORREF crOldBackground = bmDC.SetBkColor (::GetPixel (bmDC, 0, 0));
  3215. // Build mask based on transparent colour at location 0, 0
  3216. dcTrans.BitBlt (0, 0, iWidth, iHeight, &bmDC, 0, 0, SRCCOPY);
  3217. // Do the work
  3218. dc.BitBlt (0, 0, iWidth, iHeight, &bmDC, 0, 0, SRCINVERT);
  3219. dc.BitBlt (0, 0, iWidth, iHeight, &dcTrans, 0, 0, SRCAND);
  3220. dc.BitBlt (0, 0, iWidth, iHeight, &bmDC, 0, 0, SRCINVERT);
  3221. // Restore settings
  3222. dcTrans.SelectObject(pOldBitmapTrans);
  3223. dc.SetBkColor(crOldBack);
  3224. dc.SetTextColor(crOldText);
  3225. bmDC.SetBkColor(crOldBackground);
  3226. }
  3227. break;
  3228. default:
  3229. bmDC.SelectObject(pOldbmp);
  3230. ModifyWorldTransform(dc.m_hDC, &xform, MWT_IDENTITY);
  3231. SetGraphicsMode (dc.m_hDC, GM_COMPATIBLE);
  3232. return eBadParameter;
  3233. } // end of switch
  3234. bmDC.SelectObject(pOldbmp);
  3235. // reset to identity transformation
  3236. ModifyWorldTransform(dc.m_hDC, &xform, MWT_IDENTITY);
  3237. SetGraphicsMode (dc.m_hDC, GM_COMPATIBLE);
  3238. return eOK;
  3239. } // end of CMiniWindow::TransformImage