ROOT  6.06/09
Reference Guide
X11Buffer.mm
Go to the documentation of this file.
1 // @(#)root/graf2d:$Id$
2 // Author: Timur Pocheptsov 29/02/2012
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2012, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 //#define NDEBUG
13 
14 #include <stdexcept>
15 #include <cstring>
16 #include <cassert>
17 #include <memory>
18 
19 #include <Cocoa/Cocoa.h>
20 
21 #include "ROOTOpenGLView.h"
22 #include "CocoaPrivate.h"
23 #include "QuartzWindow.h"
24 #include "QuartzPixmap.h"
25 #include "QuartzUtils.h"
26 #include "X11Drawable.h"
27 #include "X11Buffer.h"
28 #include "TGWindow.h"
29 #include "TGClient.h"
30 #include "TGCocoa.h"
31 
32 namespace ROOT {
33 namespace MacOSX {
34 namespace X11 {
35 
36 //______________________________________________________________________________
38  : fID(wid),
39  fGC(gc)
40 {
41 }
42 
43 //______________________________________________________________________________
45  : fID(wid),
46  fGC()
47 {
48 }
49 
50 //______________________________________________________________________________
52 {
53 }
54 
55 //______________________________________________________________________________
56 void Command::Execute(CGContextRef /*ctx*/)const
57 {
58 }
59 
60 //______________________________________________________________________________
62 {
63  return wid == fID;
64 }
65 
66 //______________________________________________________________________________
68 {
69  return false;
70 }
71 
72 //______________________________________________________________________________
73 DrawLine::DrawLine(Drawable_t wid, const GCValues_t &gc, const Point &p1, const Point &p2)
74  : Command(wid, gc),
75  fP1(p1),
76  fP2(p2)
77 {
78 }
79 
80 //______________________________________________________________________________
81 void DrawLine::Execute()const
82 {
83  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
84  "Execute, gVirtualX is either null or not of TGCocoa type");
85  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
86  vx->DrawLineAux(fID, fGC, fP1.fX, fP1.fY, fP2.fX, fP2.fY);
87 }
88 
89 //______________________________________________________________________________
90 DrawSegments::DrawSegments(Drawable_t wid, const GCValues_t &gc,
91  const Segment_t *segments, Int_t nSegments)
92  : Command(wid, gc)
93 {
94  assert(segments != 0 && "DrawSegments, segments parameter is null");
95  assert(nSegments > 0 && "DrawSegments, nSegments <= 0");
96 
97  fSegments.assign(segments, segments + nSegments);
98 }
99 
100 //______________________________________________________________________________
101 void DrawSegments::Execute()const
102 {
103  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
104  "Execute, gVirtualX is either null or not of TGCocoa type");
105  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
106  vx->DrawSegmentsAux(fID, fGC, &fSegments[0], (Int_t)fSegments.size());
107 }
108 
109 //______________________________________________________________________________
110 ClearArea::ClearArea(Window_t wid, const Rectangle_t &area)
111  : Command(wid),
112  fArea(area)
113 {
114 }
115 
116 //______________________________________________________________________________
117 void ClearArea::Execute()const
118 {
119  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
120  "Execute, gVirtualX is either null or not of TGCocoa type");
121  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
122  vx->ClearAreaAux(fID, fArea.fX, fArea.fY, fArea.fWidth, fArea.fHeight);
123 }
124 
125 //______________________________________________________________________________
126 CopyArea::CopyArea(Drawable_t src, Drawable_t dst, const GCValues_t &gc,
127  const Rectangle_t &area, const Point &dstPoint)
128  : Command(dst, gc),
129  fSrc(src),
130  fArea(area),
131  fDstPoint(dstPoint)
132 {
133 }
134 
135 //______________________________________________________________________________
136 bool CopyArea::HasOperand(Drawable_t drawable)const
137 {
138  return fID == drawable || fSrc == drawable || fGC.fClipMask == drawable;
139 }
140 
141 //______________________________________________________________________________
142 void CopyArea::Execute()const
143 {
144  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
145  "Execute, gVirtualX is either null or not of TGCocoa type");
146 
147  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
148  vx->CopyAreaAux(fSrc, fID, fGC, fArea.fX, fArea.fY, fArea.fWidth,
149  fArea.fHeight, fDstPoint.fX, fDstPoint.fY);
150 }
151 
152 //______________________________________________________________________________
153 DrawString::DrawString(Drawable_t wid, const GCValues_t &gc, const Point &point,
154  const std::string &text)
155  : Command(wid, gc),
156  fPoint(point),
157  fText(text)
158 {
159 }
160 
161 //______________________________________________________________________________
162 void DrawString::Execute()const
163 {
164  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
165  "Execute, gVirtualX is either null or not of TGCocoa type");
166 
167  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
168  vx->DrawStringAux(fID, fGC, fPoint.fX, fPoint.fY, fText.c_str(), fText.length());
169 }
170 
171 //______________________________________________________________________________
172 FillRectangle::FillRectangle(Drawable_t wid, const GCValues_t &gc,
173  const Rectangle_t &rectangle)
174  : Command(wid, gc),
175  fRectangle(rectangle)
176 {
177 }
178 
179 //______________________________________________________________________________
180 void FillRectangle::Execute()const
181 {
182  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
183  "Execute, gVirtualX is either null or not of TGCocoa type");
184 
185  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
186  vx->FillRectangleAux(fID, fGC, fRectangle.fX, fRectangle.fY,
187  fRectangle.fWidth, fRectangle.fHeight);
188 }
189 
190 //______________________________________________________________________________
191 FillPolygon::FillPolygon(Drawable_t wid, const GCValues_t &gc,
192  const Point_t *points, Int_t nPoints)
193  : Command(wid, gc)
194 {
195  assert(points != 0 && "FillPolygon, points parameter is null");
196  assert(nPoints > 0 && "FillPolygon, nPoints <= 0");
197 
198  fPolygon.assign(points, points + nPoints);
199 }
200 
201 //______________________________________________________________________________
202 void FillPolygon::Execute()const
203 {
204  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
205  "Execute, gVirtualX is either null or not of TGCocoa type");
206 
207  ((TGCocoa *)gVirtualX)->FillPolygonAux(fID, fGC, &fPolygon[0], (Int_t)fPolygon.size());
208 }
209 
210 //______________________________________________________________________________
211 DrawRectangle::DrawRectangle(Drawable_t wid, const GCValues_t &gc,
212  const Rectangle_t &rectangle)
213  : Command(wid, gc),
214  fRectangle(rectangle)
215 {
216 }
217 
218 //______________________________________________________________________________
219 void DrawRectangle::Execute()const
220 {
221  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
222  "Execute, gVirtualX is either null or not of TGCocoa type");
223 
224  TGCocoa * const vx = static_cast<TGCocoa *>(gVirtualX);
225  vx->DrawRectangleAux(fID, fGC, fRectangle.fX, fRectangle.fY,
226  fRectangle.fWidth, fRectangle.fHeight);
227 }
228 
229 //______________________________________________________________________________
230 UpdateWindow::UpdateWindow(QuartzView *view)
231  : Command(view.fID),
232  fView(view)
233 {
234  assert(view != nil && "UpdateWindow, view parameter is nil");//view.fID will be also 0.
235 }
236 
237 //______________________________________________________________________________
238 void UpdateWindow::Execute()const
239 {
240  assert(fView.fContext != 0 && "Execute, view.fContext is null");
241 
242  if (QuartzPixmap *pixmap = fView.fBackBuffer)
243  [fView copy : pixmap area : Rectangle(0, 0, pixmap.fWidth, pixmap.fHeight)
244  withMask : nil clipOrigin : Point() toPoint : Point()];
245 }
246 
247 //______________________________________________________________________________
248 DeletePixmap::DeletePixmap(Pixmap_t pixmap)
249  : Command(pixmap, GCValues_t())
250 {
251 }
252 
253 //______________________________________________________________________________
254 void DeletePixmap::Execute()const
255 {
256  assert(dynamic_cast<TGCocoa *>(gVirtualX) != 0 &&
257  "Execute, gVirtualX is either null or not of TGCocoa type");
258 
259  ((TGCocoa *)gVirtualX)->DeletePixmapAux(fID);
260 }
261 
262 //______________________________________________________________________________
263 DrawBoxXor::DrawBoxXor(Window_t windowID, const Point &p1, const Point &p2)
264  : Command(windowID, GCValues_t()),
265  fP1(p1),
266  fP2(p2)
267 {
268  if (fP1.fX > fP2.fX)
269  std::swap(fP1.fX, fP2.fX);
270  if (fP1.fY > fP2.fY)
271  std::swap(fP1.fY, fP2.fY);
272 }
273 
274 //______________________________________________________________________________
275 void DrawBoxXor::Execute()const
276 {
277  //Noop.
278 }
279 
280 //______________________________________________________________________________
281 void DrawBoxXor::Execute(CGContextRef ctx)const
282 {
283  //
284  assert(ctx != 0 && "Execute, ctx parameter is null");
285 
286  CGContextSetRGBStrokeColor(ctx, 0., 0., 0., 1.);
287  CGContextSetLineWidth(ctx, 1.);
288 
289  CGContextStrokeRect(ctx, CGRectMake(fP1.fX, fP1.fY, fP2.fX - fP1.fX, fP2.fY - fP1.fY));
290 }
291 
292 //______________________________________________________________________________
293 DrawLineXor::DrawLineXor(Window_t windowID, const Point &p1, const Point &p2)
294  : Command(windowID, GCValues_t()),
295  fP1(p1),
296  fP2(p2)
297 {
298 }
299 
300 //______________________________________________________________________________
301 void DrawLineXor::Execute()const
302 {
303  //Noop.
304 }
305 
306 //______________________________________________________________________________
307 void DrawLineXor::Execute(CGContextRef ctx)const
308 {
309  //
310  assert(ctx != 0 && "Execute, ctx parameter is null");
311 
312  CGContextSetRGBStrokeColor(ctx, 0., 0., 0., 1.);
313  CGContextSetLineWidth(ctx, 1.);
314 
315  CGContextBeginPath(ctx);
316  CGContextMoveToPoint(ctx, fP1.fX, fP1.fY);
317  CGContextAddLineToPoint(ctx, fP2.fX, fP2.fY);
318  CGContextStrokePath(ctx);
319 }
320 
321 //______________________________________________________________________________
322 CommandBuffer::CommandBuffer()
323 {
324 }
325 
326 //______________________________________________________________________________
327 CommandBuffer::~CommandBuffer()
328 {
329  ClearCommands();
330  ClearXOROperations();
331 }
332 
333 //______________________________________________________________________________
334 void CommandBuffer::AddDrawLine(Drawable_t wid, const GCValues_t &gc, Int_t x1,
335  Int_t y1, Int_t x2, Int_t y2)
336 {
337  try {
338  //if this throws, I do not care.
339  std::auto_ptr<DrawLine> cmd(new DrawLine(wid, gc, Point(x1, y1), Point(x2, y2)));
340  fCommands.push_back(cmd.get());//this can throw.
341  cmd.release();
342  } catch (const std::exception &) {
343  throw;
344  }
345 }
346 
347 //______________________________________________________________________________
348 void CommandBuffer::AddDrawSegments(Drawable_t wid, const GCValues_t &gc,
349  const Segment_t *segments, Int_t nSegments)
350 {
351  assert(segments != 0 && "AddDrawSegments, segments parameter is null");
352  assert(nSegments > 0 && "AddDrawSegments, nSegments <= 0");
353 
354  try {
355  std::auto_ptr<DrawSegments> cmd(new DrawSegments(wid, gc, segments, nSegments));
356  fCommands.push_back(cmd.get());
357  cmd.release();
358  } catch (const std::exception &) {
359  throw;
360  }
361 }
362 
363 //______________________________________________________________________________
364 void CommandBuffer::AddClearArea(Window_t wid, Int_t x, Int_t y, UInt_t w, UInt_t h)
365 {
366  try {
367  Rectangle_t r = {};//To be replaced with X11::Rectangle.
368  r.fX = x;
369  r.fY = y;
370  r.fWidth = (UShort_t)w;
371  r.fHeight = (UShort_t)h;
372  std::auto_ptr<ClearArea> cmd(new ClearArea(wid, r));//Can throw, nothing leaks.
373  fCommands.push_back(cmd.get());//this can throw.
374  cmd.release();
375  } catch (const std::exception &) {
376  throw;
377  }
378 }
379 
380 //______________________________________________________________________________
381 void CommandBuffer::AddCopyArea(Drawable_t src, Drawable_t dst, const GCValues_t &gc,
382  Int_t srcX, Int_t srcY, UInt_t width, UInt_t height,
383  Int_t dstX, Int_t dstY)
384 {
385  try {
386  Rectangle_t area = {};
387  area.fX = srcX;
388  area.fY = srcY;
389  area.fWidth = (UShort_t)width;
390  area.fHeight = (UShort_t)height;
391  //Can throw, nothing leaks.
392  std::auto_ptr<CopyArea> cmd(new CopyArea(src, dst, gc, area, Point(dstX, dstY)));
393  fCommands.push_back(cmd.get());//this can throw.
394  cmd.release();
395  } catch (const std::exception &) {
396  throw;
397  }
398 }
399 
400 //______________________________________________________________________________
401 void CommandBuffer::AddDrawString(Drawable_t wid, const GCValues_t &gc, Int_t x, Int_t y,
402  const char *text, Int_t len)
403 {
404  try {
405  if (len < 0)//Negative length can come from caller.
406  len = std::strlen(text);
407  const std::string substr(text, len);//Can throw.
408  std::auto_ptr<DrawString> cmd(new DrawString(wid, gc, Point(x, y), substr));//Can throw.
409  fCommands.push_back(cmd.get());//can throw.
410  cmd.release();
411  } catch (const std::exception &) {
412  throw;
413  }
414 }
415 
416 //______________________________________________________________________________
417 void CommandBuffer::AddFillRectangle(Drawable_t wid, const GCValues_t &gc,
418  Int_t x, Int_t y, UInt_t w, UInt_t h)
419 {
420  try {
421  Rectangle_t r = {};
422  r.fX = x;
423  r.fY = y;
424  r.fWidth = (UShort_t)w;
425  r.fHeight = (UShort_t)h;
426  std::auto_ptr<FillRectangle> cmd(new FillRectangle(wid, gc, r));
427  fCommands.push_back(cmd.get());
428  cmd.release();
429  } catch (const std::exception &) {
430  throw;
431  }
432 }
433 
434 //______________________________________________________________________________
435 void CommandBuffer::AddDrawRectangle(Drawable_t wid, const GCValues_t &gc,
436  Int_t x, Int_t y, UInt_t w, UInt_t h)
437 {
438  try {
439  Rectangle_t r = {};
440  r.fX = x;
441  r.fY = y;
442  r.fWidth = (UShort_t)w;
443  r.fHeight = (UShort_t)h;
444  std::auto_ptr<DrawRectangle> cmd(new DrawRectangle(wid, gc, r));
445  fCommands.push_back(cmd.get());
446  cmd.release();
447  } catch (const std::exception &) {
448  throw;
449  }
450 }
451 
452 //______________________________________________________________________________
453 void CommandBuffer::AddFillPolygon(Drawable_t wid, const GCValues_t &gc,
454  const Point_t *polygon, Int_t nPoints)
455 {
456  assert(polygon != 0 && "AddFillPolygon, polygon parameter is null");
457  assert(nPoints > 0 && "AddFillPolygon, nPoints <= 0");
458 
459  try {
460  std::auto_ptr<FillPolygon> cmd(new FillPolygon(wid, gc, polygon, nPoints));
461  fCommands.push_back(cmd.get());
462  cmd.release();
463  } catch (const std::exception &) {
464  throw;
465  }
466 }
467 
468 //______________________________________________________________________________
469 void CommandBuffer::AddUpdateWindow(QuartzView *view)
470 {
471  assert(view != nil && "AddUpdateWindow, view parameter is nil");
472 
473  try {
474  std::auto_ptr<UpdateWindow> cmd(new UpdateWindow(view));
475  fCommands.push_back(cmd.get());
476  cmd.release();
477  } catch (const std::exception &) {
478  throw;
479  }
480 }
481 
482 //______________________________________________________________________________
483 void CommandBuffer::AddDeletePixmap(Pixmap_t pixmapID)
484 {
485  try {
486  std::auto_ptr<DeletePixmap> cmd(new DeletePixmap(pixmapID));
487  fCommands.push_back(cmd.get());
488  cmd.release();
489  } catch (const std::exception &) {
490  throw;
491  }
492 }
493 
494 //______________________________________________________________________________
495 void CommandBuffer::AddDrawBoxXor(Window_t windowID, Int_t x1, Int_t y1, Int_t x2, Int_t y2)
496 {
497  try {
498  std::auto_ptr<DrawBoxXor> cmd(new DrawBoxXor(windowID, Point(x1, y1), Point(x2, y2)));
499  fXorOps.push_back(cmd.get());
500  cmd.release();
501  } catch (const std::exception &) {
502  throw;
503  }
504 }
505 
506 //______________________________________________________________________________
507 void CommandBuffer::AddDrawLineXor(Window_t windowID, Int_t x1, Int_t y1, Int_t x2, Int_t y2)
508 {
509  try {
510  std::auto_ptr<DrawLineXor> cmd(new DrawLineXor(windowID, Point(x1, y1), Point(x2, y2)));
511  fXorOps.push_back(cmd.get());
512  cmd.release();
513  } catch (const std::exception &) {
514  throw;
515  }
516 }
517 
518 //______________________________________________________________________________
519 void CommandBuffer::Flush(Details::CocoaPrivate *impl)
520 {
521  assert(impl != 0 && "Flush, impl parameter is null");
522 
523  //Basic es-guarantee: state is unknown, but valid, no
524  //resource leaks, no locked focus.
525 
526  //All magic is here.
527  CGContextRef prevContext = 0;
528  CGContextRef currContext = 0;
529  QuartzView *prevView = nil;
530 
531  for (size_type i = 0, e = fCommands.size(); i < e; ++i) {
532  const Command *cmd = fCommands[i];
533  if (!cmd)//Command was deleted by RemoveOperation/RemoveGraphicsOperation.
534  continue;
535 
536  NSObject<X11Drawable> *drawable = impl->GetDrawable(cmd->fID);
537  if (drawable.fIsPixmap) {
538  cmd->Execute();//Can throw, ok.
539  continue;
540  }
541 
542  QuartzView *view = (QuartzView *)impl->GetWindow(cmd->fID).fContentView;
543 
544  if (prevView != view)
545  ClipOverlaps(view);//Can throw, ok.
546 
547  prevView = view;
548 
549  try {
550  if ([view lockFocusIfCanDraw]) {
551  NSGraphicsContext *nsContext = [NSGraphicsContext currentContext];
552  assert(nsContext != nil && "Flush, currentContext is nil");
553  currContext = (CGContextRef)[nsContext graphicsPort];
554  assert(currContext != 0 && "Flush, graphicsPort is null");//remove this assert?
555 
556  view.fContext = currContext;
557  if (prevContext && prevContext != currContext)
558  CGContextFlush(prevContext);
559  prevContext = currContext;
560 
561  const Quartz::CGStateGuard ctxGuard(currContext);
562 
563  //Clip regions first.
564  if (fClippedRegion.size())
565  CGContextClipToRects(currContext, &fClippedRegion[0], fClippedRegion.size());
566 
567  //Now add also shape combine mask.
569  ClipToShapeMask(view, currContext);
570 
571  cmd->Execute();//This can throw, we should restore as much as we can here.
572 
573  if (view.fBackBuffer) {
574  //Very "special" window.
575  const Rectangle copyArea(0, 0, view.fBackBuffer.fWidth, view.fBackBuffer.fHeight);
576  [view copy : view.fBackBuffer area : copyArea
577  withMask : nil clipOrigin : Point() toPoint : Point()];
578  }
579 
580  [view unlockFocus];
581 
582  view.fContext = 0;
583  }
584  } catch (const std::exception &) {
585  //Focus was locked, roll-back:
586  [view unlockFocus];
587  //View's context was modified, roll-back:
588  view.fContext = 0;
589  //Re-throw, something really bad happened (std::bad_alloc).
590  throw;
591  }
592  }
593 
594  if (currContext)
595  CGContextFlush(currContext);
596 
597  ClearCommands();
598 }
599 
600 //______________________________________________________________________________
601 void CommandBuffer::FlushXOROps(Details::CocoaPrivate *impl)
602 {
603  assert(impl != 0 && "FlushXOROps, impl parameter is null");
604 
605  if (!fXorOps.size())
606  return;
607 
608  //I assume here, that all XOR ops in one iteration (one Update call) must
609  //be for the same window (if not, there is no normal way to implement this at all).
610  //TODO: verify and check this condition.
611 
612  NSObject<X11Drawable> *drawable = impl->GetDrawable(fXorOps[0]->fID);
613 
614  assert([drawable isKindOfClass : [QuartzView class]] &&
615  "FlushXOROps, drawable must be of type QuartzView");
616 
617  QuartzView *view = (QuartzView *)drawable;
618 
619  if ([view lockFocusIfCanDraw]) {
620  NSGraphicsContext *nsContext = [NSGraphicsContext currentContext];
621  assert(nsContext != nil && "FlushXOROps, currentContext is nil");
622  CGContextRef currContext = (CGContextRef)[nsContext graphicsPort];
623  assert(currContext != 0 && "FlushXOROps, graphicsPort is null");//remove this assert?
624 
625  const Quartz::CGStateGuard ctxGuard(currContext);//ctx guard.
626 
627  CGContextSetAllowsAntialiasing(currContext, false);
628 
629  view.fContext = currContext;
630 
631  if (view.fBackBuffer) {//back buffer has canvas' contents.
632  //Very "special" window.
633  const Rectangle copyArea(0, 0, view.fBackBuffer.fWidth, view.fBackBuffer.fHeight);
634  [view copy : view.fBackBuffer area : copyArea
635  withMask : nil clipOrigin : Point() toPoint : Point()];
636  }
637 
638  //Now, do "XOR" drawings.
639  for (size_type i = 0, e = fXorOps.size(); i < e; ++i) {
640  if (fXorOps[i]) {
641  fXorOps[i]->Execute(currContext);
642  }
643  }
644 
645  [view unlockFocus];
646  view.fContext = 0;
647 
648  CGContextFlush(currContext);
649 
650  CGContextSetAllowsAntialiasing(currContext, true);
651  }
652 
653  ClearXOROperations();
654 }
655 
656 //______________________________________________________________________________
657 void CommandBuffer::RemoveOperationsForDrawable(Drawable_t drawable)
658 {
659  for (size_type i = 0; i < fCommands.size(); ++i) {
660  if (fCommands[i] && fCommands[i]->HasOperand(drawable)) {
661  delete fCommands[i];
662  fCommands[i] = 0;
663  }
664  }
665 
666  for (size_type i = 0; i < fXorOps.size(); ++i) {
667  if (fXorOps[i] && fXorOps[i]->HasOperand(drawable)) {
668  delete fXorOps[i];
669  fXorOps[i] = 0;
670  }
671  }
672 }
673 
674 //______________________________________________________________________________
675 void CommandBuffer::RemoveGraphicsOperationsForWindow(Window_t wid)
676 {
677  for (size_type i = 0; i < fCommands.size(); ++i) {
678  if (fCommands[i] && fCommands[i]->HasOperand(wid) &&
679  fCommands[i]->IsGraphicsCommand())
680  {
681  delete fCommands[i];
682  fCommands[i] = 0;
683  }
684  }
685 }
686 
687 //______________________________________________________________________________
688 void CommandBuffer::RemoveXORGraphicsOperationsForWindow(Window_t wid)
689 {
690  for (size_type i = 0; i < fCommands.size(); ++i) {
691  if (fXorOps[i] && fXorOps[i]->HasOperand(wid)) {
692  delete fXorOps[i];
693  fXorOps[i] = 0;
694  }
695  }
696 }
697 
698 //______________________________________________________________________________
699 void CommandBuffer::ClearCommands()
700 {
701  for (size_type i = 0, e = fCommands.size(); i < e; ++i)
702  delete fCommands[i];
703 
704  fCommands.clear();
705 }
706 
707 //______________________________________________________________________________
708 void CommandBuffer::ClearXOROperations()
709 {
710  for (size_type i = 0, e = fXorOps.size(); i < e; ++i)
711  delete fXorOps[i];
712 
713  fXorOps.clear();
714 }
715 
716 //Clipping machinery.
717 
718 namespace {
719 
720 //________________________________________________________________________________________
721 bool RectsOverlap(const NSRect &r1, const NSRect &r2)
722 {
723  if (r2.origin.x >= r1.origin.x + r1.size.width)
724  return false;
725  if (r2.origin.x + r2.size.width <= r1.origin.x)
726  return false;
727  if (r2.origin.y >= r1.origin.y + r1.size.height)
728  return false;
729  if (r2.origin.y + r2.size.height <= r1.origin.y)
730  return false;
731 
732  return true;
733 }
734 
735 }
736 
737 //______________________________________________________________________________
738 void CommandBuffer::ClipOverlaps(QuartzView *view)
739 {
740  //QuartzViews do not have backing store.
741  //But ROOT calls gClient->NeedRedraw ignoring
742  //children or overlapping siblings. This leads
743  //to obvious problems, for example, parent
744  //erasing every child inside while repainting itself.
745  //To fix this and emulate window with backing store
746  //without real backing store, I'm calculating the
747  //area of a view this is visible and not overlapped.
748 
749  //Who can overlap our view?
750  //1. Its own siblings and, probably, siblings of its ancestors.
751  //2. Children views.
752 
753  assert(view != nil && "ClipOverlaps, view parameter is nil");
754 
755  typedef std::vector<QuartzView *>::reverse_iterator reverse_iterator;
756  typedef std::vector<CGRect>::iterator rect_iterator;
757 
758  fRectsToClip.clear();
759  fClippedRegion.clear();
760 
761  //Check siblings and ancestors' siblings:
762 
763  //1. Remember the whole branch starting from our view
764  //up to a top-level window.
765  fViewBranch.clear();
766  for (QuartzView *v = view; v; v = v.fParentView)
767  fViewBranch.push_back(v);
768 
769  //We do not need content view, since it does not have any siblings.
770  if (fViewBranch.size())
771  fViewBranch.pop_back();
772 
773  //For every fViewBranch[i] in our branch, we're looking for overlapping siblings.
774  //Calculations are in view.fParentView's coordinate system.
775 
776  WidgetRect clipRect;
777  NSRect frame1 = {};
778 
779  const NSRect frame2 = view.frame;
780 
781  for (reverse_iterator it = fViewBranch.rbegin(), eIt = fViewBranch.rend(); it != eIt; ++it) {
782  QuartzView *ancestorView = *it;//This is either one of ancestors, or a view itself.
783  bool doCheck = false;
784  for (QuartzView *sibling in [ancestorView.fParentView subviews]) {
785  if (ancestorView == sibling) {
786  //View has its children in an array, and for every subviews[i] in this array,
787  //only views with index > i can overlap subviews[i].
788  doCheck = true;//all views after this must be checked.
789  continue;
790  } else if (!doCheck || sibling.fMapState != kIsViewable) {
791  continue;
792  }
793 
794  frame1 = sibling.frame;
795 
796  if (!frame1.size.width || !frame1.size.height)
797  continue;
798 
799  frame1.origin = [sibling.fParentView convertPoint : frame1.origin
800  toView : view.fParentView];
801 
802  //Check if two rects intersect.
803  if (RectsOverlap(frame2, frame1)) {
804  //Substruct frame1 from our view's rect.
805  clipRect.fX1 = frame1.origin.x;
806  clipRect.fX2 = clipRect.fX1 + frame1.size.width;
807  clipRect.fY1 = frame1.origin.y;
808  clipRect.fY2 = clipRect.fY1 + frame1.size.height;
809  fRectsToClip.push_back(clipRect);
810  }
811  }
812  }
813 
814  //Substruct children.
815 
816  for (QuartzView *child in [view subviews]) {
817  if (child.fMapState != kIsViewable)
818  continue;
819 
820  frame1 = child.frame;
821 
822  if (!frame1.size.width || !frame1.size.height)
823  continue;
824 
825  if (view.fParentView)//view can also be a content view.
826  frame1.origin = [view convertPoint : frame1.origin toView : view.fParentView];
827 
828  if (RectsOverlap(frame2, frame1)) {
829  clipRect.fX1 = frame1.origin.x;
830  clipRect.fX2 = clipRect.fX1 + frame1.size.width;
831  clipRect.fY1 = frame1.origin.y;
832  clipRect.fY2 = clipRect.fY1 + frame1.size.height;
833  fRectsToClip.push_back(clipRect);
834  }
835  }
836 
837  if (fRectsToClip.size()) {
838  //Now, if we have any rectanges to substruct them from our view's frame,
839  //we are building a set of rectangles, which represents visible part of view.
840 
841  WidgetRect rect(frame2.origin.x, frame2.origin.y, frame2.origin.x + frame2.size.width,
842  frame2.origin.y + frame2.size.height);
843 
844  BuildClipRegion(rect);
845 
846  if (view.fParentView) {
847  //To able to use this set of rectangles with CGContextClipToRects,
848  //convert them (if needed) into view's own coordinate system.
849  rect_iterator recIt = fClippedRegion.begin(), eIt = fClippedRegion.end();
850  for (; recIt != eIt; ++recIt) {
851  if (!recIt->size.width && !recIt->size.height) {
852  //This is a special 'empty' rectangle, which means our view is completely hidden.
853  assert(fClippedRegion.size() == 1 && "ClipOverlaps, internal logic error");
854  break;
855  }
856  recIt->origin = NSPointToCGPoint([view.fParentView convertPoint :
857  NSPointFromCGPoint(recIt->origin) toView : view]);
858  }
859  }
860  }
861 }
862 
863 namespace {
864 
865 typedef std::vector<int>::iterator int_iterator;
866 
867 //_____________________________________________________________________________________________________
868 int_iterator BinarySearchLeft(int_iterator first, int_iterator last, int value)
869 {
870  if (first == last)
871  return last;
872 
873  const int_iterator it = std::lower_bound(first, last, value);
874  assert(it != last && (it == first || *it == value) && "internal logic error");
875 
876  //If value < *first, return last (not found).
877  return it == first && *it != value ? last : it;
878 }
879 
880 //_____________________________________________________________________________________________________
881 int_iterator BinarySearchRight(int_iterator first, int_iterator last, int value)
882 {
883  if (first == last)
884  return last;
885 
886  const int_iterator it = std::lower_bound(first, last, value);
887  assert((it == last || *it == value) && "internal logic error");
888 
889  return it;
890 }
891 
892 }//unnamed namespace.
893 
894 //_____________________________________________________________________________________________________
895 void CommandBuffer::BuildClipRegion(const WidgetRect &rect)
896 {
897  //Input requirements:
898  // 1) all rects are valid (non-empty and x1 < x2, y1 < y2);
899  // 2) all rects intersect with widget's rect.
900  //I do not check these conditions here, this is done when filling rectsToClip.
901 
902  //I did not find any reasonable algorithm (have to search better?),
903  //code in gdk and pixman has to many dependencies and is lib-specific +
904  //they require input to be quite special:
905  // a) no overlaps (in my case I have overlaps)
906  // b) sorted in a special way.
907  //To convert my input into such a format
908  //means to implement everything myself (for example, to work out overlaps).
909 
910  //Also, my case is more simple: gdk and pixman substract region (== set of rectangles)
911  //from another region, I have to substract region from _one_ rectangle.
912 
913  //This is quite straightforward implementation - I'm calculation rectangles, which are part of
914  //a widget's rect, not hidden by any of fRectsToClip.
915  //TODO: find a better algorithm.
916  typedef std::vector<WidgetRect>::const_iterator rect_const_iterator;
917  typedef std::vector<bool>::size_type size_type;
918 
919  assert(fRectsToClip.size() != 0 && "BuildClipRegion, nothing to clip");
920 
921  fClippedRegion.clear();
922  fXBounds.clear();
923  fYBounds.clear();
924 
925  //[First, we "cut" the original rect into stripes.
926  rect_const_iterator recIt = fRectsToClip.begin(), endIt = fRectsToClip.end();
927  for (; recIt != endIt; ++recIt) {
928  if (recIt->fX1 <= rect.fX1 && recIt->fX2 >= rect.fX2 &&
929  recIt->fY1 <= rect.fY1 && recIt->fY2 >= rect.fY2) {
930  //this rect completely overlaps our view, not need to calculate anything at all.
931  fClippedRegion.push_back(CGRectMake(0., 0., 0., 0.));
932  return;
933  }
934 
935  if (recIt->fX1 > rect.fX1)//recIt->x1 is always < rect.x2 (input validation).
936  fXBounds.push_back(recIt->fX1);
937 
938  if (recIt->fX2 < rect.fX2)//recIt->x2 is always > rect.x1 (input validation).
939  fXBounds.push_back(recIt->fX2);
940 
941  if (recIt->fY1 > rect.fY1)
942  fYBounds.push_back(recIt->fY1);
943 
944  if (recIt->fY2 < rect.fY2)
945  fYBounds.push_back(recIt->fY2);
946  }
947 
948  std::sort(fXBounds.begin(), fXBounds.end());
949  std::sort(fYBounds.begin(), fYBounds.end());
950 
951  //We do not need duplicates.
952  const int_iterator xBoundsEnd = std::unique(fXBounds.begin(), fXBounds.end());
953  const int_iterator yBoundsEnd = std::unique(fYBounds.begin(), fYBounds.end());
954  //Rectangle is now "cut into pieces"].
955 
956  const size_type nXBands = size_type(xBoundsEnd - fXBounds.begin()) + 1;
957  const size_type nYBands = size_type(yBoundsEnd - fYBounds.begin()) + 1;
958 
959  fGrid.assign(nXBands * nYBands, false);
960 
961  //Mark the overlapped parts.
962  recIt = fRectsToClip.begin(), endIt = fRectsToClip.end();
963  for (; recIt != endIt; ++recIt) {
964  const int_iterator left = BinarySearchLeft(fXBounds.begin(), xBoundsEnd, recIt->fX1);
965  const size_type firstXBand = left == xBoundsEnd ? 0 : left - fXBounds.begin() + 1;
966 
967  const int_iterator right = BinarySearchRight(fXBounds.begin(), xBoundsEnd, recIt->fX2);
968  const size_type lastXBand = right - fXBounds.begin() + 1;
969 
970  const int_iterator bottom = BinarySearchLeft(fYBounds.begin(), yBoundsEnd, recIt->fY1);
971  const size_type firstYBand = bottom == yBoundsEnd ? 0 : bottom - fYBounds.begin() + 1;
972 
973  const int_iterator top = BinarySearchRight(fYBounds.begin(), yBoundsEnd, recIt->fY2);
974  const size_type lastYBand = top - fYBounds.begin() + 1;
975 
976  for (size_type i = firstYBand; i < lastYBand; ++i) {
977  const size_type baseIndex = i * nXBands;
978  for (size_type j = firstXBand; j < lastXBand; ++j)
979  fGrid[baseIndex + j] = true;
980  }
981  }
982 
983  //I do not merge rectangles.
984  //Search for non-overlapped parts and create rectangles for them.
985  CGRect newRect = {};
986 
987  for (size_type i = 0; i < nYBands; ++i) {
988  const size_type baseIndex = i * nXBands;
989  for (size_type j = 0; j < nXBands; ++j) {
990  if (!fGrid[baseIndex + j]) {
991  newRect.origin.x = j ? fXBounds[j - 1] : rect.fX1;
992  newRect.origin.y = i ? fYBounds[i - 1] : rect.fY1;
993 
994  newRect.size.width = (j == nXBands - 1 ? rect.fX2 : fXBounds[j]) - newRect.origin.x;
995  newRect.size.height = (i == nYBands - 1 ? rect.fY2 : fYBounds[i]) - newRect.origin.y;
996 
997  fClippedRegion.push_back(newRect);
998  }
999  }
1000  }
1001 
1002  if (!fClippedRegion.size())//Completely hidden
1003  fClippedRegion.push_back(CGRectMake(0., 0., 0., 0.));
1004 }
1005 
1006 }//X11
1007 }//MacOSX
1008 }//ROOT
UShort_t fWidth
Definition: GuiTypes.h:364
virtual void Execute() const =0
Short_t fY
Definition: GuiTypes.h:363
NSObject< X11Drawable > * GetDrawable(Drawable_t drawableD) const
Definition: CocoaPrivate.mm:91
UShort_t fHeight
Definition: GuiTypes.h:364
void DrawSegmentsAux(Drawable_t wid, const GCValues_t &gcVals, const Segment_t *segments, Int_t nSegments)
Definition: TGCocoa.mm:1724
Namespace for new ROOT classes and functions.
Definition: ROOT.py:1
#define assert(cond)
Definition: unittest.h:542
unsigned short UShort_t
Definition: RtypesCore.h:36
void DrawRectangleAux(Drawable_t wid, const GCValues_t &gcVals, Int_t x, Int_t y, UInt_t w, UInt_t h)
Definition: TGCocoa.mm:1777
void swap(ROOT::THist< DIMENSIONS, PRECISION > &a, ROOT::THist< DIMENSIONS, PRECISION > &b) noexcept
Swap two histograms.
Definition: THist.h:196
QuartzImage * fShapeCombineMask
Definition: QuartzWindow.h:48
int Int_t
Definition: RtypesCore.h:41
Handle_t Drawable_t
Definition: GuiTypes.h:32
QuartzPixmap * fBackBuffer
Definition: QuartzWindow.h:195
void ClearAreaAux(Window_t wid, Int_t x, Int_t y, UInt_t w, UInt_t h)
Definition: TGCocoa.mm:2252
Command(Drawable_t wid)
Definition: X11Buffer.mm:44
QuartzView * fParentView
Definition: QuartzWindow.h:180
Double_t x[n]
Definition: legend1.C:17
void DrawLineAux(Drawable_t wid, const GCValues_t &gcVals, Int_t x1, Int_t y1, Int_t x2, Int_t y2)
Definition: TGCocoa.mm:1638
unsigned fHeight
Definition: QuartzPixmap.h:46
QuartzWindow * fQuartzWindow
Definition: QuartzWindow.h:256
void FillRectangleAux(Drawable_t wid, const GCValues_t &gcVals, Int_t x, Int_t y, UInt_t w, UInt_t h)
Definition: TGCocoa.mm:1854
if(pyself &&pyself!=Py_None)
Short_t fX
Definition: GuiTypes.h:363
void CopyAreaAux(Drawable_t src, Drawable_t dst, const GCValues_t &gc, Int_t srcX, Int_t srcY, UInt_t width, UInt_t height, Int_t dstX, Int_t dstY)
Definition: TGCocoa.mm:2080
void DrawStringAux(Drawable_t wid, const GCValues_t &gc, Int_t x, Int_t y, const char *s, Int_t len)
Definition: TGCocoa.mm:2161
SVector< double, 2 > v
Definition: Dict.h:5
unsigned int UInt_t
Definition: RtypesCore.h:42
lv DrawLine(0.33, 0.0, 0.33, 1.0)
#define gVirtualX
Definition: TVirtualX.h:362
unsigned fWidth
Definition: QuartzPixmap.h:45
const Drawable_t fID
Definition: X11Buffer.h:53
CGContextRef fContext
Definition: QuartzWindow.h:170
virtual bool HasOperand(Drawable_t drawable) const
Definition: X11Buffer.mm:61
NSObject< X11Window > * GetWindow(Window_t windowID) const
Double_t y[n]
Definition: legend1.C:17
void ClipToShapeMask(NSView< X11Window > *view, CGContextRef ctx)
virtual bool IsGraphicsCommand() const
Definition: X11Buffer.mm:67
Handle_t Window_t
Definition: GuiTypes.h:30
This class implements TVirtualX interface for MacOS X, using Cocoa and Quartz 2D. ...
Definition: TGCocoa.h:64
Handle_t Pixmap_t
Definition: GuiTypes.h:31