Process Hacker
graph.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * graph control
4  *
5  * Copyright (C) 2010-2011 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #define _PH_GRAPH_PRIVATE
24 #include <phgui.h>
25 #include <graph.h>
26 
27 #define COLORREF_TO_BITS(Color) (_byteswap_ulong(Color) >> 8)
28 
29 typedef struct _PHP_GRAPH_CONTEXT
30 {
31  HWND Handle;
32  ULONG Style;
33  ULONG_PTR Id;
34  PH_GRAPH_DRAW_INFO DrawInfo;
35  PH_GRAPH_OPTIONS Options;
36  BOOLEAN NeedsUpdate;
37  BOOLEAN NeedsDraw;
38 
39  HDC BufferedContext;
40  HBITMAP BufferedOldBitmap;
41  HBITMAP BufferedBitmap;
42  PVOID BufferedBits;
43  RECT BufferedContextRect;
44 
45  HDC FadeOutContext;
46  HBITMAP FadeOutOldBitmap;
47  HBITMAP FadeOutBitmap;
48  PVOID FadeOutBits;
49  RECT FadeOutContextRect;
50 
51  HWND TooltipHandle;
52  WNDPROC TooltipOldWndProc;
53  POINT LastCursorLocation;
55 
56 LRESULT CALLBACK PhpGraphWndProc(
57  _In_ HWND hwnd,
58  _In_ UINT uMsg,
59  _In_ WPARAM wParam,
60  _In_ LPARAM lParam
61  );
62 
63 RECT PhNormalGraphTextMargin = { 5, 5, 5, 5 };
64 RECT PhNormalGraphTextPadding = { 3, 3, 3, 3 };
65 
67  VOID
68  )
69 {
70  WNDCLASSEX c = { sizeof(c) };
71 
72  c.style = CS_GLOBALCLASS | CS_DBLCLKS;
73  c.lpfnWndProc = PhpGraphWndProc;
74  c.cbClsExtra = 0;
75  c.cbWndExtra = sizeof(PVOID);
76  c.hInstance = PhLibImageBase;
77  c.hIcon = NULL;
78  c.hCursor = LoadCursor(NULL, IDC_ARROW);
79  c.hbrBackground = NULL;
80  c.lpszMenuName = NULL;
81  c.lpszClassName = PH_GRAPH_CLASSNAME;
82  c.hIconSm = NULL;
83 
84  if (!RegisterClassEx(&c))
85  return FALSE;
86 
87  return TRUE;
88 }
89 
100  _In_ HDC hdc,
101  _In_ PPH_GRAPH_DRAW_INFO DrawInfo
102  )
103 {
104  ULONG width;
105  ULONG height;
106  ULONG height1;
107  ULONG flags;
108  LONG step;
109  RECT rect;
110  POINT points[4];
111  HPEN nullPen;
112  HPEN dcPen;
113  HBRUSH dcBrush;
114  PPH_LIST lineList1 = NULL;
115  PPH_LIST lineList2 = NULL;
116 
117  width = DrawInfo->Width;
118  height = DrawInfo->Height;
119  height1 = DrawInfo->Height - 1;
120  flags = DrawInfo->Flags;
121  step = DrawInfo->Step;
122 
123  nullPen = GetStockObject(NULL_PEN);
124  dcPen = GetStockObject(DC_PEN);
125  dcBrush = GetStockObject(DC_BRUSH);
126 
127  // Fill in the background.
128  rect.left = 0;
129  rect.top = 0;
130  rect.right = width;
131  rect.bottom = height;
132  SetDCBrushColor(hdc, DrawInfo->BackColor);
133  FillRect(hdc, &rect, dcBrush);
134 
135  // Draw the data (this section only fills in the background).
136 
137  if (DrawInfo->LineData1 && DrawInfo->LineDataCount >= 2)
138  {
139  LONG x = width - step;
140  ULONG index = 0;
141  BOOLEAN willBreak = FALSE;
142 
143  SelectObject(hdc, dcBrush);
144  SelectObject(hdc, nullPen);
145 
146  lineList1 = PhCreateList(DrawInfo->LineDataCount);
147 
148  if (DrawInfo->LineData2)
149  lineList2 = PhCreateList(DrawInfo->LineDataCount);
150 
151  while (index < DrawInfo->LineDataCount - 1)
152  {
153  FLOAT f0; // 0..1
154  FLOAT f1;
155  ULONG h0; // height..0
156  ULONG h1;
157 
158  if (x < 0 || index == DrawInfo->LineDataCount - 2)
159  willBreak = TRUE;
160 
161  // Draw line 1.
162 
163  f0 = DrawInfo->LineData1[index];
164  f1 = DrawInfo->LineData1[index + 1];
165  h0 = (ULONG)((1 - f0) * height);
166  h1 = (ULONG)((1 - f1) * height);
167 
168  // top-left, top-right, bottom-right, bottom-left
169  points[0].x = x;
170  points[0].y = h1;
171  points[1].x = x + step;
172  points[1].y = h0;
173  points[2].x = x + step;
174  points[2].y = height;
175  points[3].x = x;
176  points[3].y = height;
177 
178  // Fill in the area below the line.
179  SetDCBrushColor(hdc, DrawInfo->LineBackColor1);
180  Polygon(hdc, points, 4);
181 
182  // Add the line height values to the list for drawing later.
183  if (h0 > height1) h0 = height1;
184  if (h1 > height1) h1 = height1;
185  PhAddItemList(lineList1, (PVOID)h0);
186  if (willBreak) PhAddItemList(lineList1, (PVOID)h1);
187 
188  // Draw line 2 (either stacked or overlayed).
189  if (DrawInfo->LineData2 && (flags & PH_GRAPH_USE_LINE_2))
190  {
191  if (flags & PH_GRAPH_OVERLAY_LINE_2)
192  {
193  f0 = DrawInfo->LineData2[index];
194  f1 = DrawInfo->LineData2[index + 1];
195  }
196  else
197  {
198  f0 += DrawInfo->LineData2[index];
199  f1 += DrawInfo->LineData2[index + 1];
200 
201  if (f0 > 1) f0 = 1;
202  if (f1 > 1) f1 = 1;
203  }
204 
205  h0 = (ULONG)((1 - f0) * height);
206  h1 = (ULONG)((1 - f1) * height);
207 
208  if (flags & PH_GRAPH_OVERLAY_LINE_2)
209  {
210  points[0].y = h1;
211  points[1].y = h0;
212  }
213  else
214  {
215  // Fix the bottom points so we don't fill in the line 1 area.
216  points[2].y = points[1].y; // p2.y = h0(line1)
217  points[3].y = points[0].y; // p3.y = h1(line1)
218  points[0].y = h1;
219  points[1].y = h0;
220  }
221 
222  SetDCBrushColor(hdc, DrawInfo->LineBackColor2);
223  Polygon(hdc, points, 4);
224 
225  if (h0 > height1) h0 = height1;
226  if (h1 > height1) h1 = height1;
227  PhAddItemList(lineList2, (PVOID)h0);
228  if (willBreak) PhAddItemList(lineList2, (PVOID)h1);
229  }
230 
231  if (x < 0)
232  break;
233 
234  x -= step;
235  index++;
236  }
237  }
238 
239  // Draw the grid.
240  if (flags & PH_GRAPH_USE_GRID)
241  {
242  ULONG x = width / DrawInfo->GridWidth;
243  ULONG y = height / DrawInfo->GridHeight;
244  ULONG i;
245  ULONG pos;
246  ULONG gridStart;
247 
248  SetDCPenColor(hdc, DrawInfo->GridColor);
249  SelectObject(hdc, dcPen);
250 
251  // Draw the vertical lines.
252 
253  points[0].y = 0;
254  points[1].y = height;
255  gridStart = (DrawInfo->GridStart * DrawInfo->Step) % DrawInfo->GridWidth;
256 
257  for (i = 0; i <= x; i++)
258  {
259  pos = width - (i * DrawInfo->GridWidth + gridStart) - 1;
260  points[0].x = pos;
261  points[1].x = pos;
262  Polyline(hdc, points, 2);
263  }
264 
265  // Draw the horizontal lines.
266 
267  points[0].x = 0;
268  points[1].x = width;
269 
270  for (i = 0; i <= y; i++)
271  {
272  pos = i * DrawInfo->GridHeight - 1;
273  points[0].y = pos;
274  points[1].y = pos;
275  Polyline(hdc, points, 2);
276  }
277  }
278 
279  // Draw the data (this section draws the lines).
280  // This is done to get a clearer graph.
281  if (lineList1)
282  {
283  LONG x = width - step;
284  ULONG index;
285  ULONG previousHeight1;
286  ULONG previousHeight2;
287 
288  previousHeight1 = (ULONG)lineList1->Items[0];
289  index = 1;
290 
291  if (lineList2)
292  previousHeight2 = (ULONG)lineList2->Items[0];
293 
294  while (index < lineList1->Count)
295  {
296  points[0].x = x;
297  points[1].x = x + step;
298 
299  // Draw line 2 first so it doesn't draw over line 1.
300  if (lineList2)
301  {
302  points[0].y = (ULONG)lineList2->Items[index];
303  points[1].y = previousHeight2;
304 
305  SelectObject(hdc, dcPen);
306  SetDCPenColor(hdc, DrawInfo->LineColor2);
307  Polyline(hdc, points, 2);
308 
309  previousHeight2 = points[0].y;
310 
311  points[0].y = (ULONG)lineList1->Items[index];
312  points[1].y = previousHeight1;
313 
314  SelectObject(hdc, dcPen);
315  SetDCPenColor(hdc, DrawInfo->LineColor1);
316  Polyline(hdc, points, 2);
317 
318  previousHeight1 = points[0].y;
319  }
320  else
321  {
322  points[0].x = x;
323  points[0].y = (ULONG)lineList1->Items[index];
324  points[1].x = x + step;
325  points[1].y = previousHeight1;
326 
327  SelectObject(hdc, dcPen);
328  SetDCPenColor(hdc, DrawInfo->LineColor1);
329  Polyline(hdc, points, 2);
330 
331  previousHeight1 = points[0].y;
332  }
333 
334  if (x < 0)
335  break;
336 
337  x -= step;
338  index++;
339  }
340 
341  PhDereferenceObject(lineList1);
342  if (lineList2) PhDereferenceObject(lineList2);
343  }
344 
345  // Draw the text.
346  if (DrawInfo->Text.Buffer)
347  {
348  // Fill in the text box.
349  SetDCBrushColor(hdc, DrawInfo->TextBoxColor);
350  FillRect(hdc, &DrawInfo->TextBoxRect, GetStockObject(DC_BRUSH));
351 
352  // Draw the text.
353  SetTextColor(hdc, DrawInfo->TextColor);
354  SetBkMode(hdc, TRANSPARENT);
355  DrawText(hdc, DrawInfo->Text.Buffer, (ULONG)DrawInfo->Text.Length / 2, &DrawInfo->TextRect, DT_NOCLIP);
356  }
357 }
358 
359 FORCEINLINE VOID PhpGetGraphPoint(
360  _In_ PPH_GRAPH_DRAW_INFO DrawInfo,
361  _In_ ULONG Index,
362  _Out_ PULONG H1,
363  _Out_ PULONG H2
364  )
365 {
366  if (Index < DrawInfo->LineDataCount)
367  {
368  FLOAT f1;
369  FLOAT f2;
370 
371  f1 = DrawInfo->LineData1[Index];
372 
373  if (f1 < 0)
374  f1 = 0;
375  if (f1 > 1)
376  f1 = 1;
377 
378  *H1 = (ULONG)(f1 * (DrawInfo->Height - 1));
379 
380  if (DrawInfo->Flags & PH_GRAPH_USE_LINE_2)
381  {
382  f2 = f1 + DrawInfo->LineData2[Index];
383 
384  if (f2 < 0)
385  f2 = 0;
386  if (f2 > 1)
387  f2 = 1;
388 
389  *H2 = (ULONG)(f2 * (DrawInfo->Height - 1));
390  }
391  }
392  else
393  {
394  *H1 = 0;
395  *H2 = 0;
396  }
397 }
398 
414  _In_ HDC hdc,
415  _In_ PVOID Bits,
416  _In_ PPH_GRAPH_DRAW_INFO DrawInfo
417  )
418 {
419  PULONG bits;
420  LONG width;
421  LONG height;
422  LONG numberOfPixels;
423  ULONG flags;
424  LONG i;
425  LONG x;
426 
427  BOOLEAN intermediate; // whether we are currently between two data positions
428  ULONG dataIndex; // the data index of the current position
429  ULONG h1_i; // the line 1 height value to the left of the current position
430  ULONG h1_o; // the line 1 height value at the current position
431  ULONG h2_i; // the line 1 + line 2 height value to the left of the current position
432  ULONG h2_o; // the line 1 + line 2 height value at the current position
433  ULONG h1; // current pixel
434  ULONG h1_left; // current pixel
435  ULONG h2; // current pixel
436  ULONG h2_left; // current pixel
437 
438  LONG mid;
439  LONG h1_low1;
440  LONG h1_high1;
441  LONG h1_low2;
442  LONG h1_high2;
443  LONG h2_low1;
444  LONG h2_high1;
445  LONG h2_low2;
446  LONG h2_high2;
447  LONG old_low2;
448  LONG old_high2;
449 
450  ULONG lineColor1;
451  ULONG lineBackColor1;
452  ULONG lineColor2;
453  ULONG lineBackColor2;
454  ULONG gridYCounter;
455  ULONG gridXIncrement;
456  ULONG gridColor;
457 
458  bits = Bits;
459  width = DrawInfo->Width;
460  height = DrawInfo->Height;
461  numberOfPixels = width * height;
462  flags = DrawInfo->Flags;
463  lineColor1 = COLORREF_TO_BITS(DrawInfo->LineColor1);
464  lineBackColor1 = COLORREF_TO_BITS(DrawInfo->LineBackColor1);
465  lineColor2 = COLORREF_TO_BITS(DrawInfo->LineColor2);
466  lineBackColor2 = COLORREF_TO_BITS(DrawInfo->LineBackColor2);
467 
468  if (DrawInfo->BackColor == 0)
469  {
470  memset(bits, 0, numberOfPixels * 4);
471  }
472  else
473  {
474  PhFillMemoryUlong(bits, COLORREF_TO_BITS(DrawInfo->BackColor), numberOfPixels);
475  }
476 
477  x = width - 1;
478  intermediate = FALSE;
479  dataIndex = 0;
480  h1_low2 = MAXLONG;
481  h1_high2 = 0;
482  h2_low2 = MAXLONG;
483  h2_high2 = 0;
484 
485  PhpGetGraphPoint(DrawInfo, 0, &h1_i, &h2_i);
486 
487  if (flags & PH_GRAPH_USE_GRID)
488  {
489  gridYCounter = DrawInfo->GridWidth - (DrawInfo->GridStart * DrawInfo->Step) % DrawInfo->GridWidth - 1;
490  gridXIncrement = width * DrawInfo->GridHeight;
491  gridColor = COLORREF_TO_BITS(DrawInfo->GridColor);
492  }
493 
494  while (x >= 0)
495  {
496  // Calculate the height of the graph at this point.
497 
498  if (!intermediate)
499  {
500  h1_o = h1_i;
501  h2_o = h2_i;
502 
503  // Pull in new data.
504  dataIndex++;
505  PhpGetGraphPoint(DrawInfo, dataIndex, &h1_i, &h2_i);
506 
507  h1 = h1_o;
508  h1_left = (h1_i + h1_o) / 2;
509  h2 = h2_o;
510  h2_left = (h2_i + h2_o) / 2;
511  }
512  else
513  {
514  h1 = h1_left;
515  h1_left = h1_i;
516  h2 = h2_left;
517  h2_left = h2_i;
518  }
519 
520  // The graph is drawn right-to-left. There is one iteration of the loop per horizontal pixel.
521  // There is a fixed step value of 2, so every other iteration is a mid-point (intermediate)
522  // iteration with a height value of (left + right) / 2. In order to rasterize the outline,
523  // effectively in each iteration half of the line is drawn at the current column and the other
524  // half is drawn in the column to the left.
525 
526  // Rasterize the data outline.
527  // h?_low2 to h?_high2 is the vertical line to the left of the current pixel.
528  // h?_low1 to h?_high1 is the vertical line at the current pixel.
529  // We merge (union) the old h?_low2 to h?_high2 line with the current line in each iteration.
530  //
531  // For example:
532  //
533  // X represents a data point. M represents the mid-point between two data points ("intermediate").
534  // X, M and x are all part of the outline. # represents the background filled in during
535  // the current loop iteration.
536  //
537  // slope > 0: slope < 0:
538  //
539  // X < high1 X < high2 (of next loop iteration)
540  // x x
541  // x < low1 x < low2 (of next loop iteration)
542  // x# < high2 x < high1 (of next loop iteration)
543  // M# < low2 M < low1 (of next loop iteration)
544  // x# < high1 (of next loop iteration) x < high2
545  // x# < low1 (of next loop iteration) x < low2
546  // x # < high2 (of next loop iteration) x < high1
547  // x # x
548  // X # < low2 (of next loop iteration) X < low1
549  // # #
550  // ^ ^
551  // ^| current pixel ^| current pixel
552  // | |
553  // | left of current pixel | left of current pixel
554  //
555  // In both examples above, the line low2-high2 will be merged with the line low1-high1 of the next
556  // iteration.
557 
558  mid = ((h1_left + h1) / 2) * width;
559  old_low2 = h1_low2;
560  old_high2 = h1_high2;
561 
562  if (h1_left < h1) // slope > 0
563  {
564  h1_low2 = h1_left * width;
565  h1_high2 = mid;
566  h1_low1 = mid + width;
567  h1_high1 = h1 * width;
568  }
569  else // slope < 0
570  {
571  h1_high2 = h1_left * width;
572  h1_low2 = mid + width;
573  h1_high1 = mid;
574  h1_low1 = h1 * width;
575  }
576 
577  // Merge the lines.
578  if (h1_low1 > old_low2)
579  h1_low1 = old_low2;
580  if (h1_high1 < old_high2)
581  h1_high1 = old_high2;
582 
583  // Fix up values for the current horizontal offset.
584  h1_low1 += x;
585  h1_high1 += x;
586 
587  if (flags & PH_GRAPH_USE_LINE_2)
588  {
589  mid = ((h2_left + h2) / 2) * width;
590  old_low2 = h2_low2;
591  old_high2 = h2_high2;
592 
593  if (h2_left < h2) // slope > 0
594  {
595  h2_low2 = h2_left * width;
596  h2_high2 = mid;
597  h2_low1 = mid + width;
598  h2_high1 = h2 * width;
599  }
600  else // slope < 0
601  {
602  h2_high2 = h2_left * width;
603  h2_low2 = mid + width;
604  h2_high1 = mid;
605  h2_low1 = h2 * width;
606  }
607 
608  // Merge the lines.
609  if (h2_low1 > old_low2)
610  h2_low1 = old_low2;
611  if (h2_high1 < old_high2)
612  h2_high1 = old_high2;
613 
614  // Fix up values for the current horizontal offset.
615  h2_low1 += x;
616  h2_high1 += x;
617  }
618 
619  // Fill in the background.
620 
621  if (flags & PH_GRAPH_USE_LINE_2)
622  {
623  for (i = h1_high1 + width; i < h2_low1; i += width)
624  {
625  bits[i] = lineBackColor2;
626  }
627  }
628 
629  for (i = x; i < h1_low1; i += width)
630  {
631  bits[i] = lineBackColor1;
632  }
633 
634  // Draw the grid.
635 
636  if (flags & PH_GRAPH_USE_GRID)
637  {
638  // Draw the vertical grid line.
639  if (gridYCounter == 0)
640  {
641  for (i = x; i < numberOfPixels; i += width)
642  {
643  bits[i] = gridColor;
644  }
645  }
646 
647  gridYCounter++;
648 
649  if (gridYCounter == DrawInfo->GridWidth)
650  gridYCounter = 0;
651 
652  // Draw the horizontal grid line.
653  for (i = x + gridXIncrement; i < numberOfPixels; i += gridXIncrement)
654  {
655  bits[i] = gridColor;
656  }
657  }
658 
659  // Draw the outline (line 1 is allowed to paint over line 2).
660 
661  if (flags & PH_GRAPH_USE_LINE_2)
662  {
663  for (i = h2_low1; i <= h2_high1; i += width) // exclude pixel in the middle
664  {
665  bits[i] = lineColor2;
666  }
667  }
668 
669  for (i = h1_low1; i <= h1_high1; i += width)
670  {
671  bits[i] = lineColor1;
672  }
673 
674  intermediate = !intermediate;
675  x--;
676  }
677 
678  if (DrawInfo->Text.Buffer)
679  {
680  // Fill in the text box.
681  SetDCBrushColor(hdc, DrawInfo->TextBoxColor);
682  FillRect(hdc, &DrawInfo->TextBoxRect, GetStockObject(DC_BRUSH));
683 
684  // Draw the text.
685  SetTextColor(hdc, DrawInfo->TextColor);
686  SetBkMode(hdc, TRANSPARENT);
687  DrawText(hdc, DrawInfo->Text.Buffer, (ULONG)DrawInfo->Text.Length / 2, &DrawInfo->TextRect, DT_NOCLIP);
688  }
689 }
690 
704  _In_ HDC hdc,
705  _Inout_ PPH_GRAPH_DRAW_INFO DrawInfo,
706  _In_ PPH_STRINGREF Text,
707  _In_ PRECT Margin,
708  _In_ PRECT Padding,
709  _In_ ULONG Align
710  )
711 {
712  SIZE textSize;
713  PH_RECTANGLE boxRectangle;
714  PH_RECTANGLE textRectangle;
715 
716  DrawInfo->Text = *Text;
717  GetTextExtentPoint32(hdc, Text->Buffer, (ULONG)Text->Length / 2, &textSize);
718 
719  // Calculate the box rectangle.
720 
721  boxRectangle.Width = textSize.cx + Padding->left + Padding->right;
722  boxRectangle.Height = textSize.cy + Padding->top + Padding->bottom;
723 
724  if (Align & PH_ALIGN_LEFT)
725  boxRectangle.Left = Margin->left;
726  else if (Align & PH_ALIGN_RIGHT)
727  boxRectangle.Left = DrawInfo->Width - boxRectangle.Width - Margin->right;
728  else
729  boxRectangle.Left = (DrawInfo->Width - boxRectangle.Width) / 2;
730 
731  if (Align & PH_ALIGN_TOP)
732  boxRectangle.Top = Margin->top;
733  else if (Align & PH_ALIGN_BOTTOM)
734  boxRectangle.Top = DrawInfo->Height - boxRectangle.Height - Margin->bottom;
735  else
736  boxRectangle.Top = (DrawInfo->Height - boxRectangle.Height) / 2;
737 
738  // Calculate the text rectangle.
739 
740  textRectangle.Left = boxRectangle.Left + Padding->left;
741  textRectangle.Top = boxRectangle.Top + Padding->top;
742  textRectangle.Width = textSize.cx;
743  textRectangle.Height = textSize.cy;
744 
745  // Save the rectangles.
746  DrawInfo->TextRect = PhRectangleToRect(textRectangle);
747  DrawInfo->TextBoxRect = PhRectangleToRect(boxRectangle);
748 }
749 
751  _Out_ PPHP_GRAPH_CONTEXT *Context
752  )
753 {
754  PPHP_GRAPH_CONTEXT context;
755 
756  context = PhAllocate(sizeof(PHP_GRAPH_CONTEXT));
757  memset(context, 0, sizeof(PHP_GRAPH_CONTEXT));
758 
759  context->DrawInfo.Width = 3;
760  context->DrawInfo.Height = 3;
761  context->DrawInfo.Flags = PH_GRAPH_USE_GRID;
762  context->DrawInfo.Step = 2;
763  context->DrawInfo.BackColor = RGB(0xef, 0xef, 0xef);
764  context->DrawInfo.LineDataCount = 0;
765  context->DrawInfo.LineData1 = NULL;
766  context->DrawInfo.LineData2 = NULL;
767  context->DrawInfo.LineColor1 = RGB(0x00, 0xff, 0x00);
768  context->DrawInfo.LineColor2 = RGB(0xff, 0x00, 0x00);
769  context->DrawInfo.LineBackColor1 = RGB(0x00, 0x77, 0x00);
770  context->DrawInfo.LineBackColor2 = RGB(0x77, 0x00, 0x00);
771  context->DrawInfo.GridColor = RGB(0xc7, 0xc7, 0xc7);
772  context->DrawInfo.GridWidth = 20;
773  context->DrawInfo.GridHeight = 40;
774  context->DrawInfo.GridStart = 0;
775  context->DrawInfo.TextColor = RGB(0x00, 0xff, 0x00);
776  context->DrawInfo.TextBoxColor = RGB(0x00, 0x22, 0x00);
777 
778  context->Options.FadeOutBackColor = RGB(0xef, 0xef, 0xef);
779  context->Options.FadeOutWidth = 100;
780 
781  *Context = context;
782 }
783 
785  _Inout_ _Post_invalid_ PPHP_GRAPH_CONTEXT Context
786  )
787 {
788  PhFree(Context);
789 }
790 
791 static PWSTR PhpMakeGraphTooltipContextAtom(
792  VOID
793  )
794 {
795  PH_DEFINE_MAKE_ATOM(L"PhLib_GraphTooltipContext");
796 }
797 
798 static VOID PhpDeleteBufferedContext(
799  _In_ PPHP_GRAPH_CONTEXT Context
800  )
801 {
802  if (Context->BufferedContext)
803  {
804  // The original bitmap must be selected back into the context, otherwise
805  // the bitmap can't be deleted.
806  SelectObject(Context->BufferedContext, Context->BufferedOldBitmap);
807  DeleteObject(Context->BufferedBitmap);
808  DeleteDC(Context->BufferedContext);
809 
810  Context->BufferedContext = NULL;
811  Context->BufferedBitmap = NULL;
812  Context->BufferedBits = NULL;
813  }
814 }
815 
816 static VOID PhpCreateBufferedContext(
817  _In_ PPHP_GRAPH_CONTEXT Context
818  )
819 {
820  HDC hdc;
821  BITMAPINFOHEADER header;
822 
823  PhpDeleteBufferedContext(Context);
824 
825  GetClientRect(Context->Handle, &Context->BufferedContextRect);
826 
827  hdc = GetDC(Context->Handle);
828  Context->BufferedContext = CreateCompatibleDC(hdc);
829 
830  memset(&header, 0, sizeof(BITMAPINFOHEADER));
831  header.biSize = sizeof(BITMAPINFOHEADER);
832  header.biWidth = Context->BufferedContextRect.right;
833  header.biHeight = Context->BufferedContextRect.bottom;
834  header.biPlanes = 1;
835  header.biBitCount = 32;
836 
837  Context->BufferedBitmap = CreateDIBSection(hdc, (BITMAPINFO *)&header, DIB_RGB_COLORS, &Context->BufferedBits, NULL, 0);
838 
839  ReleaseDC(Context->Handle, hdc);
840  Context->BufferedOldBitmap = SelectObject(Context->BufferedContext, Context->BufferedBitmap);
841 }
842 
843 static VOID PhpDeleteFadeOutContext(
844  _In_ PPHP_GRAPH_CONTEXT Context
845  )
846 {
847  if (Context->FadeOutContext)
848  {
849  SelectObject(Context->FadeOutContext, Context->FadeOutOldBitmap);
850  DeleteObject(Context->FadeOutBitmap);
851  DeleteDC(Context->FadeOutContext);
852 
853  Context->FadeOutContext = NULL;
854  Context->FadeOutBitmap = NULL;
855  Context->FadeOutBits = NULL;
856  }
857 }
858 
859 static VOID PhpCreateFadeOutContext(
860  _In_ PPHP_GRAPH_CONTEXT Context
861  )
862 {
863  HDC hdc;
864  BITMAPINFOHEADER header;
865  ULONG i;
866  ULONG j;
867  ULONG height;
868  COLORREF backColor;
869  ULONG fadeOutWidth;
870  FLOAT fadeOutWidthSquared;
871  ULONG currentAlpha;
872  ULONG currentColor;
873 
874  PhpDeleteFadeOutContext(Context);
875 
876  GetClientRect(Context->Handle, &Context->FadeOutContextRect);
877  Context->FadeOutContextRect.right = Context->Options.FadeOutWidth;
878 
879  hdc = GetDC(Context->Handle);
880  Context->FadeOutContext = CreateCompatibleDC(hdc);
881 
882  memset(&header, 0, sizeof(BITMAPINFOHEADER));
883  header.biSize = sizeof(BITMAPINFOHEADER);
884  header.biWidth = Context->FadeOutContextRect.right;
885  header.biHeight = Context->FadeOutContextRect.bottom;
886  header.biPlanes = 1;
887  header.biBitCount = 32;
888 
889  Context->FadeOutBitmap = CreateDIBSection(hdc, (BITMAPINFO *)&header, DIB_RGB_COLORS, &Context->FadeOutBits, NULL, 0);
890 
891  ReleaseDC(Context->Handle, hdc);
892  Context->FadeOutOldBitmap = SelectObject(Context->FadeOutContext, Context->FadeOutBitmap);
893 
894  if (!Context->FadeOutBits)
895  return;
896 
897  height = Context->FadeOutContextRect.bottom;
898  backColor = Context->Options.FadeOutBackColor;
899  fadeOutWidth = Context->Options.FadeOutWidth;
900  fadeOutWidthSquared = (FLOAT)fadeOutWidth * fadeOutWidth;
901 
902  for (i = 0; i < fadeOutWidth; i++)
903  {
904  currentAlpha = 255 - (ULONG)((FLOAT)(i * i) / fadeOutWidthSquared * 255);
905  currentColor =
906  ((backColor & 0xff) * currentAlpha / 255) +
907  ((((backColor >> 8) & 0xff) * currentAlpha / 255) << 8) +
908  ((((backColor >> 16) & 0xff) * currentAlpha / 255) << 16) +
909  (currentAlpha << 24);
910 
911  for (j = i; j < height * fadeOutWidth; j += fadeOutWidth)
912  {
913  ((PULONG)Context->FadeOutBits)[j] = currentColor;
914  }
915  }
916 }
917 
919  _In_ HWND hwnd,
920  _In_ PPHP_GRAPH_CONTEXT Context
921  )
922 {
923  PH_GRAPH_GETDRAWINFO getDrawInfo;
924 
925  Context->DrawInfo.Width = Context->BufferedContextRect.right;
926  Context->DrawInfo.Height = Context->BufferedContextRect.bottom;
927 
928  getDrawInfo.Header.hwndFrom = hwnd;
929  getDrawInfo.Header.idFrom = Context->Id;
930  getDrawInfo.Header.code = GCN_GETDRAWINFO;
931  getDrawInfo.DrawInfo = &Context->DrawInfo;
932 
933  SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)&getDrawInfo);
934 }
935 
937  _In_ HWND hwnd,
938  _In_ PPHP_GRAPH_CONTEXT Context
939  )
940 {
941  if (Context->BufferedBits)
942  PhDrawGraphDirect(Context->BufferedContext, Context->BufferedBits, &Context->DrawInfo);
943 
944  if (Context->Style & GC_STYLE_FADEOUT)
945  {
946  BLENDFUNCTION blendFunction;
947 
948  if (!Context->FadeOutContext)
949  PhpCreateFadeOutContext(Context);
950 
951  blendFunction.BlendOp = AC_SRC_OVER;
952  blendFunction.BlendFlags = 0;
953  blendFunction.SourceConstantAlpha = 255;
954  blendFunction.AlphaFormat = AC_SRC_ALPHA;
955  GdiAlphaBlend(
956  Context->BufferedContext,
957  0,
958  0,
959  Context->Options.FadeOutWidth,
960  Context->FadeOutContextRect.bottom,
961  Context->FadeOutContext,
962  0,
963  0,
964  Context->Options.FadeOutWidth,
965  Context->FadeOutContextRect.bottom,
966  blendFunction
967  );
968  }
969 
970  if (Context->Style & GC_STYLE_DRAW_PANEL)
971  {
972  PH_GRAPH_DRAWPANEL drawPanel;
973 
974  drawPanel.Header.hwndFrom = hwnd;
975  drawPanel.Header.idFrom = Context->Id;
976  drawPanel.Header.code = GCN_DRAWPANEL;
977  drawPanel.hdc = Context->BufferedContext;
978  drawPanel.Rect = Context->BufferedContextRect;
979 
980  SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)&drawPanel);
981  }
982 }
983 
984 LRESULT CALLBACK PhpGraphWndProc(
985  _In_ HWND hwnd,
986  _In_ UINT uMsg,
987  _In_ WPARAM wParam,
988  _In_ LPARAM lParam
989  )
990 {
991  PPHP_GRAPH_CONTEXT context;
992 
993  context = (PPHP_GRAPH_CONTEXT)GetWindowLongPtr(hwnd, 0);
994 
995  if (uMsg == WM_CREATE)
996  {
997  PhpCreateGraphContext(&context);
998  SetWindowLongPtr(hwnd, 0, (LONG_PTR)context);
999  }
1000 
1001  if (!context)
1002  return DefWindowProc(hwnd, uMsg, wParam, lParam);
1003 
1004  switch (uMsg)
1005  {
1006  case WM_MOUSEMOVE:
1007  case WM_LBUTTONDOWN:
1008  case WM_LBUTTONUP:
1009  case WM_RBUTTONDOWN:
1010  case WM_RBUTTONUP:
1011  case WM_MBUTTONDOWN:
1012  case WM_MBUTTONUP:
1013  {
1014  if (context->TooltipHandle)
1015  {
1016  MSG message;
1017 
1018  message.hwnd = hwnd;
1019  message.message = uMsg;
1020  message.wParam = wParam;
1021  message.lParam = lParam;
1022  SendMessage(context->TooltipHandle, TTM_RELAYEVENT, 0, (LPARAM)&message);
1023  }
1024  }
1025  break;
1026  }
1027 
1028  switch (uMsg)
1029  {
1030  case WM_CREATE:
1031  {
1032  CREATESTRUCT *createStruct = (CREATESTRUCT *)lParam;
1033 
1034  context->Handle = hwnd;
1035  context->Style = createStruct->style;
1036  context->Id = (ULONG_PTR)createStruct->hMenu;
1037  }
1038  break;
1039  case WM_DESTROY:
1040  {
1041  if (context->TooltipHandle)
1042  DestroyWindow(context->TooltipHandle);
1043 
1044  PhpDeleteFadeOutContext(context);
1045  PhpDeleteBufferedContext(context);
1046  PhpFreeGraphContext(context);
1047  SetWindowLongPtr(hwnd, 0, (LONG_PTR)NULL);
1048  }
1049  break;
1050  case WM_STYLECHANGED:
1051  {
1052  STYLESTRUCT *styleStruct = (STYLESTRUCT *)lParam;
1053 
1054  if (wParam == GWL_STYLE)
1055  {
1056  context->Style = styleStruct->styleNew;
1057  context->NeedsDraw = TRUE;
1058  }
1059  }
1060  break;
1061  case WM_SIZE:
1062  {
1063  // Force a re-create of the buffered context.
1064  PhpCreateBufferedContext(context);
1065  PhpDeleteFadeOutContext(context);
1066 
1067  PhpUpdateDrawInfo(hwnd, context);
1068  context->NeedsDraw = TRUE;
1069  InvalidateRect(hwnd, NULL, FALSE);
1070 
1071  if (context->TooltipHandle)
1072  {
1073  TOOLINFO toolInfo;
1074 
1075  memset(&toolInfo, 0, sizeof(TOOLINFO));
1076  toolInfo.cbSize = sizeof(TOOLINFO);
1077  toolInfo.hwnd = hwnd;
1078  toolInfo.uId = 1;
1079  GetClientRect(hwnd, &toolInfo.rect);
1080  SendMessage(context->TooltipHandle, TTM_NEWTOOLRECT, 0, (LPARAM)&toolInfo);
1081  }
1082  }
1083  break;
1084  case WM_PAINT:
1085  {
1086  PAINTSTRUCT paintStruct;
1087  HDC hdc;
1088 
1089  if (hdc = BeginPaint(hwnd, &paintStruct))
1090  {
1091  if (!context->BufferedContext)
1092  PhpCreateBufferedContext(context);
1093 
1094  if (context->NeedsUpdate)
1095  {
1096  PhpUpdateDrawInfo(hwnd, context);
1097  context->NeedsUpdate = FALSE;
1098  }
1099 
1100  if (context->NeedsDraw)
1101  {
1102  PhpDrawGraphControl(hwnd, context);
1103  context->NeedsDraw = FALSE;
1104  }
1105 
1106  BitBlt(
1107  hdc,
1108  paintStruct.rcPaint.left,
1109  paintStruct.rcPaint.top,
1110  paintStruct.rcPaint.right - paintStruct.rcPaint.left,
1111  paintStruct.rcPaint.bottom - paintStruct.rcPaint.top,
1112  context->BufferedContext,
1113  paintStruct.rcPaint.left,
1114  paintStruct.rcPaint.top,
1115  SRCCOPY
1116  );
1117 
1118  EndPaint(hwnd, &paintStruct);
1119  }
1120  }
1121  return 0;
1122  case WM_ERASEBKGND:
1123  return 1;
1124  case WM_NCPAINT:
1125  {
1126  HRGN updateRegion;
1127 
1128  updateRegion = (HRGN)wParam;
1129 
1130  if (updateRegion == (HRGN)1) // HRGN_FULL
1131  updateRegion = NULL;
1132 
1133  // Themed border
1134  if (context->Style & WS_BORDER)
1135  {
1136  HDC hdc;
1137  ULONG flags;
1138  RECT rect;
1139 
1140  // Note the use of undocumented flags below. GetDCEx doesn't work without these.
1141 
1142  flags = DCX_WINDOW | DCX_LOCKWINDOWUPDATE | 0x10000;
1143 
1144  if (updateRegion)
1145  flags |= DCX_INTERSECTRGN | 0x40000;
1146 
1147  if (hdc = GetDCEx(hwnd, updateRegion, flags))
1148  {
1149  GetClientRect(hwnd, &rect);
1150  rect.right += 2;
1151  rect.bottom += 2;
1152  SetDCBrushColor(hdc, RGB(0x8f, 0x8f, 0x8f));
1153  FrameRect(hdc, &rect, GetStockObject(DC_BRUSH));
1154 
1155  ReleaseDC(hwnd, hdc);
1156  return 0;
1157  }
1158  }
1159  }
1160  break;
1161  case WM_NOTIFY:
1162  {
1163  LPNMHDR header = (LPNMHDR)lParam;
1164 
1165  if (header->hwndFrom == context->TooltipHandle)
1166  {
1167  switch (header->code)
1168  {
1169  case TTN_GETDISPINFO:
1170  {
1171  LPNMTTDISPINFO dispInfo = (LPNMTTDISPINFO)header;
1172  POINT point;
1173  RECT clientRect;
1174  PH_GRAPH_GETTOOLTIPTEXT getTooltipText;
1175 
1176  GetCursorPos(&point);
1177  ScreenToClient(hwnd, &point);
1178  GetClientRect(hwnd, &clientRect);
1179 
1180  getTooltipText.Header.hwndFrom = hwnd;
1181  getTooltipText.Header.idFrom = context->Id;
1182  getTooltipText.Header.code = GCN_GETTOOLTIPTEXT;
1183  getTooltipText.Index = (clientRect.right - point.x - 1) / context->DrawInfo.Step;
1184  getTooltipText.TotalCount = context->DrawInfo.LineDataCount;
1185  getTooltipText.Text.Buffer = NULL;
1186  getTooltipText.Text.Length = 0;
1187 
1188  SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)&getTooltipText);
1189 
1190  if (getTooltipText.Text.Buffer)
1191  {
1192  dispInfo->lpszText = getTooltipText.Text.Buffer;
1193  }
1194  }
1195  break;
1196  }
1197  }
1198  }
1199  break;
1200  case WM_SETCURSOR:
1201  {
1202  if (context->Options.DefaultCursor)
1203  {
1204  SetCursor(context->Options.DefaultCursor);
1205  return TRUE;
1206  }
1207  }
1208  break;
1209  case WM_MOUSEMOVE:
1210  {
1211  if (context->TooltipHandle)
1212  {
1213  POINT point;
1214 
1215  GetCursorPos(&point);
1216  ScreenToClient(hwnd, &point);
1217 
1218  if (context->LastCursorLocation.x != point.x || context->LastCursorLocation.y != point.y)
1219  {
1220  SendMessage(context->TooltipHandle, TTM_UPDATE, 0, 0);
1221  context->LastCursorLocation = point;
1222  }
1223  }
1224  }
1225  break;
1226  case WM_LBUTTONDOWN:
1227  case WM_LBUTTONUP:
1228  case WM_LBUTTONDBLCLK:
1229  case WM_RBUTTONDOWN:
1230  case WM_RBUTTONUP:
1231  case WM_RBUTTONDBLCLK:
1232  {
1233  PH_GRAPH_MOUSEEVENT mouseEvent;
1234  RECT clientRect;
1235 
1236  GetClientRect(hwnd, &clientRect);
1237 
1238  mouseEvent.Header.hwndFrom = hwnd;
1239  mouseEvent.Header.idFrom = context->Id;
1240  mouseEvent.Header.code = GCN_MOUSEEVENT;
1241  mouseEvent.Message = uMsg;
1242  mouseEvent.Keys = (ULONG)wParam;
1243  mouseEvent.Point.x = LOWORD(lParam);
1244  mouseEvent.Point.y = HIWORD(lParam);
1245 
1246  mouseEvent.Index = (clientRect.right - mouseEvent.Point.x - 1) / context->DrawInfo.Step;
1247  mouseEvent.TotalCount = context->DrawInfo.LineDataCount;
1248 
1249  SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)&mouseEvent);
1250  }
1251  break;
1252  case GCM_GETDRAWINFO:
1253  {
1254  PPH_GRAPH_DRAW_INFO drawInfo = (PPH_GRAPH_DRAW_INFO)lParam;
1255 
1256  memcpy(drawInfo, &context->DrawInfo, sizeof(PH_GRAPH_DRAW_INFO));
1257  }
1258  return TRUE;
1259  case GCM_SETDRAWINFO:
1260  {
1261  PPH_GRAPH_DRAW_INFO drawInfo = (PPH_GRAPH_DRAW_INFO)lParam;
1262  ULONG width;
1263  ULONG height;
1264 
1265  width = context->DrawInfo.Width;
1266  height = context->DrawInfo.Height;
1267  memcpy(&context->DrawInfo, drawInfo, sizeof(PH_GRAPH_DRAW_INFO));
1268  context->DrawInfo.Width = width;
1269  context->DrawInfo.Height = height;
1270  }
1271  return TRUE;
1272  case GCM_DRAW:
1273  {
1274  PhpUpdateDrawInfo(hwnd, context);
1275  context->NeedsDraw = TRUE;
1276  }
1277  return TRUE;
1278  case GCM_MOVEGRID:
1279  {
1280  LONG increment = (LONG)wParam;
1281 
1282  context->DrawInfo.GridStart += increment;
1283  }
1284  return TRUE;
1286  return (LRESULT)context->BufferedContext;
1287  case GCM_SETTOOLTIP:
1288  {
1289  if (wParam)
1290  {
1291  TOOLINFO toolInfo = { sizeof(toolInfo) };
1292 
1293  context->TooltipHandle = CreateWindow(
1294  TOOLTIPS_CLASS,
1295  NULL,
1296  WS_POPUP | WS_EX_TRANSPARENT | TTS_NOPREFIX,
1297  CW_USEDEFAULT,
1298  CW_USEDEFAULT,
1299  CW_USEDEFAULT,
1300  CW_USEDEFAULT,
1301  NULL,
1302  NULL,
1304  NULL
1305  );
1306 
1307  SetWindowPos(context->TooltipHandle, HWND_TOPMOST, 0, 0, 0, 0,
1308  SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
1309 
1310  toolInfo.uFlags = 0;
1311  toolInfo.hwnd = hwnd;
1312  toolInfo.uId = 1;
1313  toolInfo.lpszText = LPSTR_TEXTCALLBACK;
1314  GetClientRect(hwnd, &toolInfo.rect);
1315  SendMessage(context->TooltipHandle, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);
1316 
1317  SendMessage(context->TooltipHandle, TTM_SETDELAYTIME, TTDT_INITIAL, 0);
1318  SendMessage(context->TooltipHandle, TTM_SETDELAYTIME, TTDT_AUTOPOP, MAXSHORT);
1319  // Allow newlines (-1 doesn't work)
1320  SendMessage(context->TooltipHandle, TTM_SETMAXTIPWIDTH, 0, MAXSHORT);
1321  }
1322  else
1323  {
1324  DestroyWindow(context->TooltipHandle);
1325  context->TooltipHandle = NULL;
1326  }
1327  }
1328  return TRUE;
1329  case GCM_UPDATETOOLTIP:
1330  {
1331  if (!context->TooltipHandle)
1332  return FALSE;
1333 
1334  SendMessage(context->TooltipHandle, TTM_UPDATE, 0, 0);
1335  }
1336  return TRUE;
1337  case GCM_GETOPTIONS:
1338  memcpy((PPH_GRAPH_OPTIONS)lParam, &context->Options, sizeof(PH_GRAPH_OPTIONS));
1339  return TRUE;
1340  case GCM_SETOPTIONS:
1341  memcpy(&context->Options, (PPH_GRAPH_OPTIONS)lParam, sizeof(PH_GRAPH_OPTIONS));
1342  PhpDeleteFadeOutContext(context);
1343  return TRUE;
1344  }
1345 
1346  return DefWindowProc(hwnd, uMsg, wParam, lParam);
1347 }
1348 
1355  _Out_ PPH_GRAPH_BUFFERS Buffers
1356  )
1357 {
1358  Buffers->AllocatedCount = 0;
1359  Buffers->Data1 = NULL;
1360  Buffers->Data2 = NULL;
1361  Buffers->Valid = FALSE;
1362 }
1363 
1370  _Inout_ PPH_GRAPH_BUFFERS Buffers
1371  )
1372 {
1373  if (Buffers->Data1) PhFree(Buffers->Data1);
1374  if (Buffers->Data2) PhFree(Buffers->Data2);
1375 }
1376 
1387  _Inout_ PPH_GRAPH_BUFFERS Buffers,
1388  _Inout_ PPH_GRAPH_DRAW_INFO DrawInfo,
1389  _In_ ULONG DataCount
1390  )
1391 {
1392  DrawInfo->LineDataCount = min(DataCount, PH_GRAPH_DATA_COUNT(DrawInfo->Width, DrawInfo->Step));
1393 
1394  // Do we need to allocate or re-allocate the data buffers?
1395  if (Buffers->AllocatedCount < DrawInfo->LineDataCount)
1396  {
1397  if (Buffers->Data1)
1398  PhFree(Buffers->Data1);
1399  if ((DrawInfo->Flags & PH_GRAPH_USE_LINE_2) && Buffers->Data2)
1400  PhFree(Buffers->Data2);
1401 
1402  Buffers->AllocatedCount *= 2;
1403 
1404  if (Buffers->AllocatedCount < DrawInfo->LineDataCount)
1405  Buffers->AllocatedCount = DrawInfo->LineDataCount;
1406 
1407  Buffers->Data1 = PhAllocate(Buffers->AllocatedCount * sizeof(FLOAT));
1408 
1409  if (DrawInfo->Flags & PH_GRAPH_USE_LINE_2)
1410  {
1411  Buffers->Data2 = PhAllocate(Buffers->AllocatedCount * sizeof(FLOAT));
1412  }
1413 
1414  Buffers->Valid = FALSE;
1415  }
1416 
1417  DrawInfo->LineData1 = Buffers->Data1;
1418  DrawInfo->LineData2 = Buffers->Data2;
1419 }
1420 
1422  _Out_ PPH_GRAPH_STATE State
1423  )
1424 {
1425  PhInitializeGraphBuffers(&State->Buffers);
1426  State->Text = NULL;
1427  State->TooltipText = NULL;
1428  State->TooltipIndex = -1;
1429 }
1430 
1432  _Inout_ PPH_GRAPH_STATE State
1433  )
1434 {
1435  PhDeleteGraphBuffers(&State->Buffers);
1436  if (State->Text) PhDereferenceObject(State->Text);
1437  if (State->TooltipText) PhDereferenceObject(State->TooltipText);
1438 }
1439 
1441  _Inout_ PPH_GRAPH_STATE State,
1442  _In_ PPH_GRAPH_GETDRAWINFO GetDrawInfo,
1443  _In_ ULONG DataCount
1444  )
1445 {
1446  PhGetDrawInfoGraphBuffers(&State->Buffers, GetDrawInfo->DrawInfo, DataCount);
1447 }