tag:blogger.com,1999:blog-368783302024-02-08T07:01:05.887-08:00WhorldDevelopment notes for the <a href="http://whorld.org">Whorld</a> geometric visualizer/VJ software/Freeframe plugin.Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.comBlogger78125tag:blogger.com,1999:blog-36878330.post-38826911027905378032009-08-15T14:20:00.000-07:002010-01-06T14:20:38.424-08:00Curve Anglesome benchmarks, all in microseconds, 100 rings, running average of 100 samples:<br /><br />574 previous version: no curve angle support<br />765 curve angle support, but curve angle2 calc commented out<br />767 curve angle support, but curve angle2 calc skipped by zero test<br /><br />same test but with 20 sides instead of 5:<br />1.843 previous version: no curve angle support<br />2.547 curve angle support, but curve angle2 calc commented out<br />2.564 curve angle support, but curve angle2 calc skipped by zero test<br /><br />same test on bad box:<br />297 previous version: no curve angle support<br />378 curve angle support, but curve angle2 calc skipped by zero test<br /><br />Either way best case is curve calc now takes approx. 33% longer. However the good news is the entire point calc is only about 20% of the draw function time *in line mode*.Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com1tag:blogger.com,1999:blog-36878330.post-30493855118951255452009-07-14T14:18:00.000-07:002010-01-06T14:19:59.163-08:00Whorld ChoirAccording to my calculations, the plasmagon patch would take almost 6 million years to return to its starting position, and that's not counting the pinwheel global oscillator. Yikes! I guess I can see why there's no such thing as LCM for real numbers but it bothers me somehow.<br /><br /><pre><br />0.03 33.33333333<br />0.07 14.28571429<br />0.08 12.5<br />0.09 11.11111111<br />0.003 333.3333333<br />0.004 250<br />0.005 200<br />0.006 166.6666667<br /> 1.83715E+14 seconds<br /> 5825579.068 years<br /><br /></pre>Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-49094927883240977842008-04-05T22:28:00.001-07:002008-04-05T22:28:35.152-07:00Whorld/GL: only the beginningIt seems that Whorld's drawing code can't be directly ported to OpenGL, because OpenGL doesn't even handle concave polygons, never mind self-overlapping polygons or shapes composed of Bezier curves. The good news is, it might be possible to use a combination of GDI and OpenGL. GDI provides a function called FlattenPath, which turns a path containing Bezier curves into a (very large) set of line segments. This flattened path could then be passed to the glu tessellation functions, which would turn the path into a set of simple polygons that could be rendered directly in OpenGL. It all sounds a bit Rube Goldberg, but it might be worth it to achieve transparency and the many other effects available in OpenGL. The GDI FlattenPath function seems to be very fast, at least compared to the actual rendering done by StrokeAndFillPath.Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-41459372569010839162008-02-06T22:16:00.000-08:002008-04-05T22:17:02.421-07:00compensating frame rateIf you change the frame rate, and want your patches to look the same, you must compensate the following variables: Ring Growth, and Color Speed. If you double the frame rate, halve Ring Growth and Color Speed. Yes, the app should take care of this for you, but for the moment it doesn't.<br /><br />Patches that depend on a particular relationship to the frame rate (e.g. the Seed of Life patch) will require further tweaking.<br /><br />Note that changing the frame rate will change the relative speed of the cascading delete, and there's currently no way to compensate for it. Sorry!Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-32227568677745659302008-01-26T22:15:00.000-08:002008-04-05T22:16:34.348-07:00bezier benchmarks (categorized)frames = 500<br />playlist: new curve alg bench.whl<br />patch: default<br />Master Offsets:<br />Star Factor = 1<br />Even Curve = .2<br />Odd Curve = .2<br />draw mode = line (or fill/outline)<br />NOTE: app should be MAXIMIZED at 1024 x 768<br /><br />Dell / W2K<br />draw mode: lines only<br />back buffer: auto (video memory)<br />OnTimer = 0.054 (0.29%)<br />Draw = 18.992 (99.71%)<br /> math = 0.600 (3.15%)<br /> GDI = 18.393 (96.57%)<br />total = 19.047<br />secs@frame = 0.38 (26.25 FPS)<br />2nd pass: similar<br /><br />Bad Box / XP<br />draw mode: lines only<br />back buffer: auto (video memory)<br />OnTimer = 0.008 (0.16%)<br />Draw = 4.854 (99.84%)<br /> math = 0.263 (5.41%)<br /> GDI = 4.590 (94.42%)<br />total = 4.862<br />secs@frame = 0.010 (102.84 FPS)<br /><br />Bad Box / XP<br />draw mode: fill/outline<br />back buffer: auto (video memory)<br />OnTimer = 0.008 (0.05%)<br />Draw = 16.735 (99.95%)<br /> math = 0.270 (1.61%)<br /> GDI = 16.466 (98.34%)<br />total = 16.744<br />secs@frame = 0.033 (29.86 FPS)<br />2nd pass: similar<br />not too good!<br /><br />Bad Box / XP<br />draw mode: fill/outline<br />back buffer: system memory<br />OnTimer = 0.016 (0.09%)<br />Draw = 17.916 (99.91%)<br /> math = 0.276 (1.54%)<br /> GDI = 17.639 (98.37%)<br />total = 17.931<br />secs@frame = 0.036 (27.88 FPS)<br />2nd pass: similar<br />system memory is not helping<br /><br />Z Dell / XP<br />FPS: 25<br />draw mode: fill/outline<br />back buffer: auto (video memory)<br />OnTimer = 0.014 (0.06%)<br />Draw = 21.519 (99.94%)<br />math = 0.145 (0.67%)<br />GDI = 21.374 (99.26%)<br />total = 21.533<br />secs@frame = 0.043 (23.22 FPS)<br />7 FPS *SLOWER* than Bad Box with back buffer in video memory? just terrible<br /><br />Z Dell / XP<br />FPS: 25<br />draw mode: fill/outline<br />back buffer: system memory<br />OnTimer = 0.017 (0.12%)<br />Draw = 14.446 (99.98%)<br />math = 0.146 (1.01%)<br />GDI = 14.300 (98.88%)<br />total = 14.463<br />secs@frame = 0.029 (34.57 FPS)<br />brand-new 2.66 GHz Dell is maximum 5 FPS faster than 3-year old Bad Box?<br />system memory *is* helping in this case, WTF?Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-59536534655611168072008-01-25T22:12:00.000-08:002008-04-05T22:15:19.509-07:00benchmarks for improved curve generationIn previous versions, curves were unstable (i.e. they would jitter) when star factor was negative. This occurred because the curve points were being computed from integer vertices. The new version computes the curve points from real vertices. Also, the curves points are now generated at the same time as the vertices, in a single loop, instead of in a second pass. This is more efficient, and eliminates the need for a second point array.<br /><br />Comparing 1.6.06 and 1.7.03<br /><br />Only the math portion of Draw is compared.<br /><br />playlist: new curve alg bench.whl<br />patch: default<br />Master Offsets:<br />Star Factor = 1<br />Even Curve = .2<br />Odd Curve = .2<br /><br />Benchmark includes code between<br /> while (NextPos != NULL) {<br />and<br /> rp.Delete = !RingVisible;<br />plus MakeCurves in 1.6.06<br /><br />1000 frames<br /><br />pass 1.6.06 1.7.03<br />---- ------- -------<br />#1 .001157 .000998<br />#2 .001161 .000995 (14% faster)<br /><br />In summary, the Draw math takes less time in 1.7.03, despite having added some major new features (global parameters, curve shear). Presumably the speedup is due to a combination of better-optimized code and reduced memory usage. The global parameters aren't free, but their cost is minimal: 1.7.03 drops to around .000930 if m_GlobRing is removed from Draw.<br /><br />Total time for Draw in 1.7.03: .025 in line mode, off the chart in fill mode<br /><br />Same exact tests, but on the bad box:<br /><br />pass 1.6.06 1.7.03<br />---- ------- -------<br />#1 .000654 .000468<br />#2 .000653 .000468 (28% faster)<br /><br />Total time for Draw: .009 in line mode, .022 in fill modeChris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-1162284908212831162006-06-19T00:54:00.000-07:002006-10-31T02:59:47.480-08:00swarmin AddRing:<br /><pre><br />int swcnt; // number of vertices in swarm polygon<br />int swidx; // index of swarm polygon's current vertex<br />int swrad; // radius of swarm polygon, in pixels<br /><br />double theta = (PI * 2) * (double(swidx) / swcnt); // can be better optimized<br />Ring.Shift.x += sin(theta) * swrad;<br />Ring.Shift.y += cos(theta) * swrad;<br />swidx++;<br />swidx %= swcnt; // can be better optimized<br /></pre><br />NOTE that this feature requires the skew curve fix (see above), otherwise curves will be horribly distorted.Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-1162284853795347982006-06-19T00:53:00.000-07:002006-10-31T01:31:40.033-08:00skew curve fixSkew distorts curved rings; to avoid this, MakeCurves must use the skewed origin.<br /><br />wrong:<br />iorg = CPoint(round(org.x), round(org.y));<br /><br />correct:<br />iorg = CPoint(round(xshift), round(yshift));Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-1162284827965387132006-06-05T00:53:00.000-07:002006-10-31T01:38:55.946-08:00maximize list control within playlist dialog<pre><br />void CPlaylistDlg::OnHideControls() <br />{<br /> m_HideControls ^= 1;<br /> CWnd *wp = GetWindow(GW_CHILD);<br /> while (wp != NULL) {<br /> if (wp != &m_List)<br /> wp->ShowWindow(m_HideControls ? SW_HIDE : SW_SHOW);<br /> wp = wp->GetNextWindow();<br /> }<br /> PostMessage(WM_SIZE);<br />}<br /><br />void CPlaylistDlg::OnSize(UINT nType, int cx, int cy) <br />{<br /> CToolDlg::OnSize(nType, cx, cy);<br /> if (m_HideControls) {<br /> CRect r;<br /> GetClientRect(r);<br /> m_List.MoveWindow(r);<br /> } else<br /> m_Resize.OnSize();<br />}<br /><br />void CPlaylistDlg::OnShowWindow(BOOL bShow, UINT nStatus) <br />{<br /> CToolDlg::OnShowWindow(bShow, nStatus);<br /> if (bShow && !m_HideControls)<br /> m_Resize.OnSize();<br />}<br /></pre>Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-1162284796275959652006-06-02T00:52:00.000-07:002006-10-31T01:39:10.070-08:00MIDI support for video functionsAssuming MIDI ranges equal 5 (the default):<br /><pre><br />Video Select<br /><br />num MIDI Video<br />pad Values Clip<br />0 0..12 0<br />1 13..25 1<br />2 26..38 2<br />3 39..51 3<br />4 52..63 4<br />5 64..76 5<br />6 77..89 6<br />7 90..101 7<br />8 102..114 8<br />9 115..126 9<br />. 127 None<br /></pre><br />Note that a value of 127 disables video. If this is undesirable, set Video Select's MIDI range to 4.99 instead of 5.<br /><pre><br />Video Blending <br /><br />num MIDI <br />pad Values Blending description ROP code GDI name<br />0 0..12 ~Src & Dst AND inverted source with destination DSna <br />1 13..25 ~Src | Dst OR inverted source with destination DSno MERGEPAINT<br />2 26..38 Src & ~Dst AND source with inverted destination SDna SRCERASE<br />3 39..51 Src & ~Dst OR source with inverted destination SDno <br />4 52..63 Src & Dst AND source with destination DSa SRCAND<br />5 64..76 Src | Dst OR source with destination DSo SRCPAINT<br />6 77..89 Src ^ Dst XOR source with destination DSx SRCINVERT<br />7 90..101 ~(Src & Dst) AND source with destination, invert result DSan <br />8 102..114 ~(Src | Dst) OR source with destination, invert result DSon NOTSRCERASE<br />9 115..127 ~(Src ^ Dst) XOR source with destination, invert result DSxn <br /><br />Video Cycle Length<br /><br />num MIDI Cycle<br />pad Values Length<br />1 0..12 1<br />2 13..25 2<br />3 26..38 3<br />4 39..51 4<br />5 52..63 5<br />6 64..76 6<br />7 77..89 7<br />8 90..101 8<br />9 102..114 9<br />0 115..127 10<br /></pre><br />Note that numpad zero sets the cycle length to "all" which is effectively 10.Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-1162284637479644672006-05-16T00:49:00.000-07:002006-10-31T01:39:27.826-08:00pre-fetching video<pre><br />bool CPlaylistDlg::PreFetchVideo()<br />{<br /> CAviToBmp vid;<br /> for (int i = 0; i < BANKS; i++) {<br /> int count = m_Bank[i].GetSize();<br /> for (int j = 0; j < count; j++) {<br /> LPCSTR Path = m_Bank[i][j].m_Path;<br /> if (IsVideo(Path)) {<br /> if (!vid.Open(Path))<br /> return(FALSE);<br /> }<br /> }<br /> }<br /> return(TRUE);<br />}<br /></pre><br />This takes approx. 10 seconds for all 340 clips. We can't pre-open the clips because AVIStreamGetFrameOpen won't open more than 75 AVI files simultaneously (it fails with no error message). Not sure why just opening and closing the clips helps. Something to do with disk caching? Not sure how *much* it helps either.<br /><br />The 10 second delay can be avoided by doing the pre-fetch in a background thread. Note the CoInitialize! AviFileOpen fails without it. This gives me a bad feeling that VfW isn't thread-safe...<br /><pre><br />UINT CPlaylistDlg::PreFetchVideoThread(LPVOID pParam)<br />{<br /> CoInitialize(0);<br /> CPlaylistDlg *pd = (CPlaylistDlg *)pParam;<br /> if (!pd->PreFetchVideo())<br /> AfxMessageBox("Can't pre-fetch video");<br /> CoUninitialize();<br /> return(0);<br />}<br /><br />in CPlaylistDlg::Cache:<br />AfxBeginThread(PreFetchVideoThread, this, <br /> THREAD_PRIORITY_BELOW_NORMAL, 0, 0, 0);<br /></pre><br />The 75 clip limit is apparently NOT due to XVID, it occurs with Cinepack too.<br /><br />Test to determine how much of Open delay is due to AVIStreamGetFrameOpen. Result for 100 Opens:<br /><pre><br /> total avg min max<br />Open 4.915 .049 .007 .087<br />GetFr. 0.790 .008 .007 .009<br /></pre><br />A second run gave very similar results:<br /><pre><br />Open 3.119 .031 .007 .087<br />GetFr. 0.741 .007 .007 .009<br /></pre><br />Result: AVIStreamGetFrameOpen is less than 15% of the average, but more importantly, it has a static cost of ~8 milliseconds. This makes a reasonable case for trying the partial pre-open strategy (pre-open all clips but without doing AVIStreamGetFrameOpen, and then do AVIStreamGetFrameOpen when the clip is selected).Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-1162284541222673022006-05-10T00:48:00.001-07:002006-10-31T01:41:55.370-08:00exporting a looped movieTried exporting a bidirectional movie (plays forward and then plays in reverse). It's smoother than just letting the video jump back to the beginning but there's still a visible transition when the rings change direction.<br /><pre><br />int PassFrames = m_RangeLast - m_RangeFirst + 1;<br />int TotalFrames = (PassFrames - 1) * 2;<br />pd.Create();<br />int j = m_RangeLast;<br />pd.SetRange(0, TotalFrames);<br />for (int i = 0; i < TotalFrames; i++) {<br /> if (i >= PassFrames)<br /> sm.Seek(--j);<br /> sm.Read();<br /> HBITMAP bm = View->MakeDIB(&OutFrameSize, &m_InFrameSize, GetScaleToFit());<br /> bool retc = bta.AddFrame(bm);<br /> DeleteObject(bm);<br /> if (!m_DisplayOutput) // prevent view from painting<br /> View->ValidateRect(NULL); // must revalidate before SetPos<br /> pd.SetPos(i);<br /> if (pd.Canceled())<br /> return(IDCANCEL);<br />}<br /></pre>Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-1162284507641542652006-05-10T00:48:00.000-07:002006-10-31T01:39:42.646-08:00LissajousTried it. It's too predictable and hugs the edge of the frame too much. Not so good! The random motion is better.Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-1162284486069431892006-05-09T00:47:00.000-07:002006-10-31T01:42:08.006-08:00video mirroring quadrant selection<pre><br />CSize FrmSz(m_Size);<br />CSize SrcSz(vp->GetFrameSize());<br />CPoint SrcPt;<br />if (m_st.Mirror) {<br /> FrmSz.cx = (FrmSz.cx + 1) >> 1;<br /> FrmSz.cy = (FrmSz.cy + 1) >> 1;<br /> switch (Quadrant) {<br /> case 0: // upper left<br /> SrcPt = CPoint(0, SrcSz.cy - 1);<br /> SrcSz.cx >>= 1;<br /> SrcSz.cy >>= 1;<br /> SrcSz.cy = -SrcSz.cy;<br /> break;<br /> case 1: // upper right<br /> SrcPt = CPoint(SrcSz.cx - 1, SrcSz.cy - 1);<br /> SrcSz.cx >>= 1;<br /> SrcSz.cy >>= 1;<br /> SrcSz.cx = -SrcSz.cx;<br /> SrcSz.cy = -SrcSz.cy;<br /> break;<br /> case 2: // lower left<br /> SrcPt = CPoint(0, 0);<br /> SrcSz.cx >>= 1;<br /> SrcSz.cy >>= 1;<br /> break;<br /> case 3: // lower right<br /> SrcPt = CPoint(SrcSz.cx - 1, 0);<br /> SrcSz.cx >>= 1;<br /> SrcSz.cy >>= 1;<br /> SrcSz.cx = -SrcSz.cx;<br /> break;<br /> }<br />} else {<br /> SrcPt = CPoint(0, SrcSz.cy - 1);<br /> SrcSz.cy = -SrcSz.cy;<br />}<br />HDC sdc;<br />vp->GetDC(&sdc);<br />StretchBlt(dc, 0, 0, FrmSz.cx, FrmSz.cy, sdc,<br /> SrcPt.x, SrcPt.y, SrcSz.cx, SrcSz.cy, m_VideoList.GetROP());<br />vp->ReleaseDC(sdc);<br /></pre>Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-1162284449075962232006-04-24T00:46:00.000-07:002006-10-31T01:42:21.026-08:00more optimizationsIn Draw:<br />replaced CRect::OffsetRect in Bounds calc with inline<br />replaced CRect::PtInPect in inner loop with inline (BIG difference)<br />only calculate iorg if making Curves<br /><br />benchmarks:<br />hot rod, 1024 x 768, maximized (but not full screen)<br />default patch, fill & outline, speed = 20 "curve fill bench.whl"<br />total Draw time for 1000 frames at 25 FPS, in seconds<br />average ring count = 193<br /><pre><br />v1.1.01 v1.4.04<br />---------------<br />12.643 12.831<br />12.630 12.641<br />12.629 12.639<br />12.629 12.830<br />12.638 12.651<br />12.633 12.638<br />12.637 12.833<br />12.636 12.645<br />12.630 12.840<br /> 12.643<br /> 12.633<br /> 12.671<br /> 12.644<br /> 12.639<br /><br />AVG AVG<br />12.634 12.698<br /></pre><br />1.4.04 has a noticeably larger deviation: most of the samples cluster around 12.64 but every fourth sample or so clusters around 12.83. No idea why! Generally the results are encouraging however. If we go by the averages, 1.4.04 is 64 microseconds slower per frame, which translates to an extra 1.6 milliseconds per second at 25 FPS. If we go by the worst case, 1.4.04 is 210 microseconds slower per frame, i.e. an extra 5.25 milliseconds per second (half a percent). Neither difference is likely to be significant.<br /><pre><br />Same exact test but without fill (still on hot rod):<br />v1.1.01 v1.4.04<br />---------------<br />0.428 0.413<br />0.428 0.413<br /></pre><br />For non-fill case, 1.4.04 is FASTER! Awesome. Presumably inlining PtInRect in the innermost loop made the big difference, let's see.<br /><pre><br />1.4.04 no fill, using CRect::PtInRect:<br /> 0.447<br /> 0.448<br /></pre><br />Yup, CRect::PtInRect was bad stuff. Not sure whether it was the function call or inefficiency within PtInRect itself, or maybe both.<br /><br />An interesting question: why is the difference worse with fill/outline? Almost all of the added code gets executed regardless of draw mode. The only exceptions are the Convex test to decide rp.Color vs. PrevColor, and the (Curve || PrevCurve) test. Surely these can't account for 64..210 microseconds per frame?Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-1162284370578173982006-04-24T00:45:00.001-07:002006-10-31T01:43:04.106-08:00per-ring curve decision benchmarksBottom line: The per-ring version (1.4.03) is very slightly faster than 1.4.02. The data shows a consistent improvement of between 5 and 10 microseconds per frame. This is the opposite of the expected result. Perhaps moving the curvature test into the main loop allowed the compiler to better optimize the initial pass (for trail)? It could also be a change in cache behavior.<br /><pre><br />#include "benchmark.h"<br />float sum;<br />int cnt;<br />void CWhorldView::Draw(HDC dc)<br />{<br />CBenchmark b;<br />.<br />.<br />.<br />sum += b.Elapsed();<br />cnt++;<br />if (cnt == 1000) {<br /> CString s;<br /> s.Format("%d %f %f\n", cnt, sum, sum / cnt);<br /> AfxMessageBox(s);<br />}<br /><br />total time (sum) in seconds for 1000 frames<br /><br />default patch<br />1.4.02 1.4.03<br />--------------<br />1.448 1.436<br />1.448 1.436<br />1.442 1.435<br />1.442 1.439<br />1.446 1.440<br /><br />default patch, speed and canvas scale at max<br />1.4.02 1.4.03<br />--------------<br />5.400 5.390<br />5.396 5.390<br /></pre>Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-1162284329781502112006-04-24T00:45:00.000-07:002006-10-31T01:42:34.073-08:00help changes for 1.4add Odd Curve and Even Curve to parameters (done)<br />move Canvas Scale and Hue Loop Length from Options/General to Master (done)<br />change ReadFromPatch to Patch Mode and expand as needed (done)<br />update keyboard accelerators (done)Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-1162284257040913892006-04-16T00:43:00.000-07:002006-10-31T01:42:50.020-08:00using multimedia timer instead of windows timerWe can't use a multimedia timer all the time (as was suggested on the MFC forum), because it significantly increases CPU useage, e.g. 75% vs. 33%. Windows task-switching overhead is the most likely culprit, especially since using a custom timer thread instead of a multimedia timer produces identical behavior.<br /><br />We could use a multimedia timer only during non-client modal states, but there's still a visible glitch, due to the phase difference between the windows timer and the multimedia timer. The difference varies from 0 to 1 timer periods, and I can't see any obvious way to avoid it. It's better than doing nothing, but it may have other side effects, so the SendMessage technique shown above may still be the best shot.Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-1162284305960302362006-04-14T00:44:00.000-07:002006-10-31T01:43:18.393-08:00corrupt Mirror, Origin, Drawmode in patchesThe first cases appeared on 10/08/2005. Many of the "frosty" patches had it, and the corrupt data propagated from them to other patches, via hybridization. All patches were fixed today. I can't replicate the behavior nor can I find any obvious cause in the current code.Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-1162284285248990982006-04-11T00:44:00.000-07:002006-10-31T01:43:34.736-08:00closing aux frame displays file save dialogIn CMainFrame::DetachView, must remove aux view from document.<br /><pre><br /> GetDoc()->RemoveView(m_AuxView); // remove aux view from our document<br /></pre>Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-1162284231273939542006-04-11T00:43:00.000-07:002006-10-31T01:43:50.800-08:00prevent non-client clicks from pausing appThe following works, provided the "Show window contents while dragging" system property is unchecked. The only side effects are a) left-clicking on the menu bar moves the cursor to the center of the menu bar (strange, but not really a problem), and b) close happens on button down instead of button up.<br /><pre><br />void CPersistDlg::OnNcLButtonDown(UINT nHitTest, CPoint point)<br />{<br /> switch (nHitTest) {<br /> case HTCLOSE:<br /> SendMessage(WM_SYSCOMMAND, SC_CLOSE, 0);<br /> break;<br /> case HTCAPTION:<br /> SendMessage(WM_SYSCOMMAND, SC_MOVE, 0);<br /> break;<br /> default:<br /> CDialog::OnNcLButtonDown(nHitTest, point);<br /> break;<br /> }<br />}<br /><br />void CPersistDlg::OnNcRButtonDown(UINT nHitTest, CPoint point)<br />{<br /> switch (nHitTest) {<br /> case HTCAPTION:<br /> case HTSYSMENU:<br /> SendMessage(WM_CONTEXTMENU, (LONG)m_hWnd, MAKELONG(point.x, point.y));<br /> break;<br /> default:<br /> CDialog::OnNcRButtonDown(nHitTest, point);<br /> break;<br /> }<br />}<br /></pre>Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-1162284186588544292006-04-05T00:41:00.000-07:002006-10-31T01:44:06.983-08:00benchmarks for drawing AVI frames<pre><br />test video: "Movie_051112214725 comp.avi"<br />Release mode<br />Master Rings = 0<br />Window size: 632 x 459<br /># samples: 1000 (40 seconds)<br />averages (times in seconds)<br /><br />Dell / Windows 2000<br />---------------------<br />using BitBlt, SRCCOPY<br />total 0.032182 <- i.e. avg duration of DrawAviFrame<br />get frame 0.013667 (42.5%)<br />create bitmap 0.009647 (30.0%)<br />blit 0.008469 (26.3%)<br />misc 0.000400 (1.2%)<br /><br />using StretchBlt, SRCCOPY<br />total 0.047449 <- exceeds timer period! 47% worse than BitBlt<br />get frame 0.013653 (28.8%)<br />create bitmap 0.009758 (20.6%)<br />blit 0.023656 (49.9%)<br />misc 0.000383 (0.8%)<br /><br />using StretchBlt, SRCINVERT<br />total 0.059013 <- 148% of timer period! 25% worse than SRCCOPY<br />get frame 0.013646 (23.1%)<br />create bitmap 0.010457 (17.7%)<br />blit 0.034508 (58.5%)<br />misc 0.000402 (0.7%)<br /><br />Hotrod / XP<br />---------------------<br />using BitBlt, SRCCOPY<br />total 0.004478 <- i.e. avg duration of DrawAviFrame<br />get frame 0.002263 (50.5%)<br />create bitmap 0.001238 (27.6%)<br />blit 0.000881 (19.7%)<br />misc 0.000096 (2.1%)<br /><br />using StretchBlt, SRCCOPY<br />total 0.006912 <- 54% worse than BitBlt<br />get frame 0.002257 (32.7%)<br />create bitmap 0.001240 (17.9%)<br />blit 0.003314 (48.0%)<br />misc 0.000100 (1.4%)<br /><br />using StretchBlt, SRCINVERT<br />total 0.008200 <- 20% of timer period, 19% worse than SRCCOPY<br />get frame 0.002267 (27.6%)<br />create bitmap 0.001239 (15.1%)<br />blit 0.004592 (56.0%)<br />misc 0.000102 (1.2%)<br /><br />Conclusion:<br />Hotrod is 7.2 times faster, but StretchBlt mode is bad stuff.<br /><br />#include "benchmark.h"<br />double blitsum;<br />double createsum;<br />double getfrmsum;<br />double totsum;<br />int samps;<br />...<br />totsum += b.Elapsed();<br />samps++;<br />if (samps == 1000) {<br /> CString s;<br /> double total = totsum / samps;<br /> double getfrm = getfrmsum / samps;<br /> double create = createsum / samps;<br /> double blit = blitsum / samps;<br /> double misc = total - (getfrm + create + blit);<br /> s.Format("total\t%f\ngetfrm\t%f (%.1f%%)\ncreate\t%f (%.1f%%)\nblit\t%f (%.1f%%)\nmisc\t%f (%.1f%%)", <br /> total, <br /> getfrm, getfrm / total * 100, <br /> create, create / total * 100, <br /> blit, blit / total * 100, <br /> misc, misc / total * 100);<br /> AfxMessageBox(s);<br />}<br /></pre>Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-1162284089976746362006-03-31T00:40:00.000-08:002006-10-31T01:44:24.283-08:00effect of ROPs on background<pre><br />reverse polish notation! read right to left!<br /><br />P Selected pen<br />D Destination bitmap<br />a Bitwise AND<br />n Bitwise NOT (inverse)<br />o Bitwise OR<br />x Bitwise exclusive OR (XOR)<br /><br />1 R2_BLACK 0 makes everything black<br />2 R2_NOTMERGEPEN DPon black outlining leaves bg unchanged, else pen is inverted and translucent (correct when bg is black, else darker)<br />3 R2_MASKNOTPEN DPna black outlining leaves bg unchanged, else pen is inverted and translucent (correct when bg is white, else darker)<br />4 R2_NOTCOPYPEN Pn overwrites bg, inverted drawing color<br />5 R2_MASKPENNOT PDna white outlining leaves bg unchanged, else pen is translucent (correct when bg is black, else darker)<br />6 R2_NOT Dn reverses bg: outlining or overlap shows bg unchanged (it's inverted twice)<br />7 R2_XORPEN DPx the coolest<br />8 R2_NOTMASKPEN DPan white outlining leaves bg unchanged, else pen is inverted and translucent (correct when bg is black, else lighter)<br />9 R2_MASKPEN DPa white outlining leaves bg unchanged, else pen is translucent (correct when bg is white, else darker)<br />10 R2_NOTXORPEN DPxn similar to xor<br />11 R2_NOP D drawing is invisible<br />12 R2_MERGENOTPEN DPno drawing in white leaves bg unchanged, else pen is inverted and translucent (opaque when bg is black)<br />13 R2_COPYPEN P straight copy<br />14 R2_MERGEPENNOT PDno black outlining leaves bg unchanged, else pen is translucent (correct when bg is white)<br />15 R2_MERGEPEN DPo black outlining leaves bg unchanged, else pen is translucent (correct when bg is black)<br />16 R2_WHITE 1 makes everything white<br /></pre>Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-1162284053733529372006-03-25T00:40:00.000-08:002006-10-31T01:44:43.093-08:00more zoom/origin hellThe really weird thing is, it looks much better if the origin DOESN'T move in mirror mode!!! So it's actually not broken at the moment, except for<br /><br />1) the move and return thing, and that's gone now that the normalized origin isn't zoomed<br />2) the problem with trail (outer rings move when they shouldn't)<br />3) make DIB must still use center zoom, otherwise scale to fit will screw up mirrored snapshotsChris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0tag:blogger.com,1999:blog-36878330.post-1162284014008243042006-03-23T00:39:00.000-08:002006-10-31T01:45:04.296-08:00zoom/origin hellUnmirrored zooming is supposed to be origin-centered, i.e. you zoom into or out of the ring origin. This isn't allowed in mirror mode because it would change the image. In mirror mode, you zoom into or out of the *mirror origin* (i.e. the center of the screen), and let the ring origin move as needed so that the image stays the same.<br /><br />origin behavior with zooming, version 1.1:<br /><pre><br />mirror drag behavior<br />no no stationary: correct<br />yes no moves: correct<br />no yes stationary: correct<br />yes yes stationary (or with damping, moves and then returns): BOGUS<br /></pre><br />origin behavior with zooming, version 1.3 (trail):<br /><pre><br />mirror drag behavior<br />no no moves: BOGUS<br />yes no moves: correct<br />no yes stationary, but with damping, moves and then returns: BOGUS<br />yes yes stationary (or with damping, moves and then returns): BOGUS<br /></pre><br />Note that random origin may have the same problems as drag origin; it's hard to tell.<br /><br />Trail complicates things further, because if the origin moves and returns, the trail pattern is lost.<br /><br />Really there are two separate problems:<br /><br />1) The damping feature of the origin motion prevents the ring origin from moving during zoom, or causes it to slip back. As a result, zooming in mirror mode changes the mirror imagery. This behavior always occurred in mirror mode, but somehow it went unnoticed; adding trail made it more noticeable, by causing it to affect unmirrored mode also.<br /><br />The likely cause is that When you zoom in mirrored mode, the view's normalized origin changes, but the main frame's target origin (also in normalized space) DOESN'T change, so the view eventually slips back to the target origin. This can be solved by saving the normalized origin before zooming the view, and then correcting the target origin by the difference between the new normalized origin and the saved one.<br /><br />2) Adding trail broke zooming into the ring origin. It could be argued that this was a bad idea anyway and that we should always zoom into the mirror origin, in which case this bug is actually a feature. However, this argument doesn't really hold up, because zooming into the ring origin is typically more interesting than zooming into whatever happens to be in the center of the screen.<br /><br />This problem could possibly be solved by a temporary coordinate space change, so that the zoom scaling is done in ring-origin space instead of mirror-origin space. You could calculate the difference between the master ring origin and the center of the screen, and then for each ring, subtract the difference from its origin, multiply by zoom, and add the difference back on.Chris Kordahttp://www.blogger.com/profile/01929043998111578392noreply@blogger.com0