cairo_base.h
1 /*
2  @copyright Russell Standish 2000-2013
3  @author Russell Standish
4  This file is part of EcoLab
5 
6  Open source licensed under the MIT license. See LICENSE for details.
7 */
8 
9 #ifndef CAIRO_BASE_H
10 #define CAIRO_BASE_H
11 #if defined(CAIRO)
12 #include "tcl++.h"
13 #include "classdesc.h"
14 #include "arrays.h"
15 
16 #include <cairo/cairo.h>
17 #ifdef TK
18 #include <tk.h>
19 #endif
20 #include <vector>
21 #include <string>
22 #include <limits>
23 #include <math.h>
24 #include <memory>
25 #include <typeinfo>
26 
27 namespace ecolab
28 {
29  namespace cairo
30  {
31 
33  class Surface
34  {
35  private:
36  cairo_surface_t* m_surface;
37  cairo_t* m_cairo;
38  mutable double m_width, m_height;
39  bool recomputeSizes;
40  void operator=(const Surface&);
41  Surface(const Surface&);
42 
43  void computeSizes() const {
44  if (m_surface && recomputeSizes)
45  switch (cairo_surface_get_type(m_surface))
46  {
47  case CAIRO_SURFACE_TYPE_IMAGE:
48  m_width=cairo_image_surface_get_width(m_surface);
49  m_height=cairo_image_surface_get_height(m_surface);
50  break;
51 #if CAIRO_HAS_RECORDING_SURFACE
52  case CAIRO_SURFACE_TYPE_RECORDING:
53  {
54  double x, y;
55  cairo_recording_surface_ink_extents
56  (m_surface, &x, &y, &m_width, &m_height);
57  }
58  break;
59 #endif
60  default: break;
61  }
62  }
63 
64  public:
69  Surface(cairo_surface_t* s=NULL,
70  double width=-1, double height=-1): m_surface(NULL), m_cairo(NULL)
71  {surface(s, width, height);}
72  ~Surface() {
73  if (m_cairo) cairo_destroy(m_cairo);
74  if (m_surface) cairo_surface_destroy(m_surface);
75  }
76 
77  cairo_surface_t* surface() const {return m_surface;}
82  cairo_surface_t* surface(cairo_surface_t* s,
83  int width=-1, int height=-1) {
84  if (m_cairo) cairo_destroy(m_cairo);
85  if (m_surface) cairo_surface_destroy(m_surface);
86  m_surface = s;
87  m_cairo = s? cairo_create(s): NULL;
88  m_width=width; m_height=height;
89  recomputeSizes=width<0 || height<0;
90  return m_surface;
91  }
92 
93  cairo_t* cairo() const {return m_cairo;}
94  double width() const {computeSizes(); return m_width;}
95  double height() const {computeSizes(); return m_height;}
98  double top() const {
99 #if CAIRO_HAS_RECORDING_SURFACE
100  if (m_surface &&
101  cairo_surface_get_type(m_surface)==CAIRO_SURFACE_TYPE_RECORDING)
102  {
103  double x, y, w, h;
104  cairo_recording_surface_ink_extents(m_surface, &x, &y, &w, &h);
105  return y;
106  }
107  else
108 #endif
109  return 0;
110  }
113  double left() const {
114 #if CAIRO_HAS_RECORDING_SURFACE
115  if (m_surface &&
116  cairo_surface_get_type(m_surface)==CAIRO_SURFACE_TYPE_RECORDING)
117  {
118  double x, y, w, h;
119  cairo_recording_surface_ink_extents(m_surface, &x, &y, &w, &h);
120  return x;
121  }
122  else
123 #endif
124  return 0;
125  }
129  virtual void clear() {
130  // TODO not sure if this should be pure virtual, or whether a
131  // sensible default implementation can be provided.
132  }
136  // TODO - can we obsolete this method?
137  virtual void blit(unsigned x, unsigned y, unsigned width, unsigned height) {}
139  virtual void blit() {}
141  virtual void resize(size_t width, size_t height) {}
143  virtual void requestRedraw() {}
144  };
145 
146  // fix the surfacePtr type so that objects and headers are consistent
147  // TODO - convert to std::shared_ptr once EcoLab is 100% c++11 compiled
148  typedef std::tr1::shared_ptr<Surface> SurfacePtr;
149 
150 #ifdef TK
151  struct PhotoImageBlock: public Tk_PhotoImageBlock
152  {
153  PhotoImageBlock(): transparency(true) {}
154  PhotoImageBlock(int x, int y, bool transparency):
155  transparency(transparency)
156  {
157  pixelPtr=NULL;
158  width=x;
159  height=y;
160  cairo_format_t format = transparency?
161  CAIRO_FORMAT_ARGB32: CAIRO_FORMAT_RGB24;
162  pitch = cairo_format_stride_for_width(format, width);
163  pixelSize=4;
164  for (int i=0; i<3; ++i) offset[i]=2-i;
165  offset[3]=3;
166  }
167  bool transparency;
168  };
169 
170  class TkPhotoSurface: public Surface
171  {
172  Tk_PhotoHandle photo;
173  std::vector<unsigned char> imageData;
174  PhotoImageBlock imageBlock;
175  public:
177  bool compositing;
178 
179  int width() const {return imageBlock.width;}
180  int height() const {return imageBlock.height;}
181  void clear() {
182  if (imageBlock.transparency)
183  // make it transparent
184  memset(&imageData[0],0,imageData.size());
185  else
186  // make it white
187  memset(&imageData[0],255,imageData.size());
188  }
189  TkPhotoSurface(Tk_PhotoHandle photo, bool transparency=true):
190  photo(photo), compositing(false)
191  {
192  init(transparency);
193  }
194 
195  void init(bool transparency=true)
196  {
197  int width, height;
198  Tk_PhotoGetSize(photo, &width, &height);
199  imageBlock = PhotoImageBlock(width, height, transparency);
200  imageData.resize(imageBlock.pitch * imageBlock.height);
201  imageBlock.pixelPtr = &imageData[0];
202 
203  surface( cairo_image_surface_create_for_data
204  (&imageData[0], transparency?
205  CAIRO_FORMAT_ARGB32: CAIRO_FORMAT_RGB24,
206  width, height, imageBlock.pitch));
207  }
208 
209  void resize(size_t width, size_t height)
210  {
211 #if TK_MAJOR_VERSION == 8 && TK_MINOR_VERSION < 5
212  Tk_PhotoSetSize(photo, width, height);
213 #else
214  Tk_PhotoSetSize(interp(), photo, width, height);
215 #endif
216  init(imageBlock.transparency);
217  }
218 
220  void blit(unsigned x, unsigned y, unsigned width, unsigned height)
221  {
222  imageBlock.pixelPtr=&imageData[x*imageBlock.pixelSize + y*imageBlock.pitch];
223  if (width != unsigned(imageBlock.width) ||
224  height != unsigned(imageBlock.height))
225 #if TK_MAJOR_VERSION == 8 && TK_MINOR_VERSION < 5
226  Tk_PhotoSetSize(photo, width, height);
227  Tk_PhotoPutBlock
228  (photo, &imageBlock, 0, 0, width, height,
229  compositing? TK_PHOTO_COMPOSITE_OVERLAY:TK_PHOTO_COMPOSITE_SET);
230 #else
231  Tk_PhotoSetSize(interp(), photo, width, height);
232  Tk_PhotoPutBlock(interp(), photo, &imageBlock, 0, 0, width, height,
233  compositing? TK_PHOTO_COMPOSITE_OVERLAY:TK_PHOTO_COMPOSITE_SET);
234 #endif
235  }
236  void blit() {blit(0,0,imageBlock.width, imageBlock.height);}
237  void requestRedraw() {blit();}
238  };
239 
240  class CairoImage
241  {
242 
243  protected:
244  void resize(size_t width, size_t height);
245 
251  double xScale, yScale;
252  double m_scale, rotation;
253 
254  public:
255 
256  SurfacePtr cairoSurface;
258  static Tk_ConfigSpec configSpecs[];
259 
263  void initMatrix();
264 
266  void setMatrix(double scale, double rotation)
267  {m_scale=scale, this->rotation=rotation;}
268 
269  // apply an additional scale transformation
270  void scale(double xScale, double yScale) {
271  if (xScale!=1 || yScale!=1)
272  {this->xScale*=xScale; this->yScale*=yScale;}
273  }
274 
275  CairoImage(): xScale(1), yScale(1) {setMatrix(1,0);}
276  CairoImage(const SurfacePtr& cairoSurface):
277  xScale(1), yScale(1), cairoSurface(cairoSurface) {setMatrix(1,0);}
278 
279  virtual ~CairoImage() {}
280 
281  void attachToImage(const std::string& imgName);
282 
284  double distanceFrom(double x, double y) const;
285 
287  virtual array_ns::array<double> boundingBox();
288 
292  int inClip(double x0, double y0, double x1, double y1) const;
293 
295  virtual void draw()=0;
296  void redrawIfSurfaceTooSmall();
297  void blit() {cairoSurface->blit();}
298 
299  };
300 
302  class Path
303  {
304  cairo_path_t* m_path;
305  cairo_matrix_t m_transformation;
306  Path(const Path&);
307  void operator=(const Path&);
308  public:
309  Path(cairo_t* cairo): m_path(cairo_copy_path(cairo))
310  {cairo_get_matrix(cairo, &m_transformation);}
311  ~Path() {cairo_path_destroy(m_path);}
312  void appendToCurrent(cairo_t* cairo) {
313  // apply saved transformation along with path
314  cairo_save(cairo);
315  cairo_set_matrix(cairo,&m_transformation);
316  cairo_append_path(cairo,m_path);
317  cairo_restore(cairo);
318  }
319  };
320 
321 
323  struct ImageItem {
324  Tk_Item header; /* Generic stuff that's the same for all
325  * types. MUST BE FIRST IN STRUCTURE. */
326  Tk_Canvas canvas; /* Canvas containing the image. */
327  double x, y; /* Coordinates of positioning point for
328  * image. */
329  double scale; /* scale = 1 means cairo drawing
330  coordinates in the range [-1,1] map
331  to [x0,x1], [y0,y1] of the image
332  pixmap */
333 
334  double rotation; /* rotation opf cairo user coordinate
335  with respect to pixmap coordinates,
336  in radians */
337 
338  Tk_Anchor anchor; /* Where to anchor image relative to (x,y). */
339  char *imageString; /* String describing -image option
340  * (malloc-ed). NULL means no image right
341  * now. */
342  char *activeImageString; /* String describing -activeimage option.
343  * NULL means no image right now. */
344  char *disabledImageString; /* String describing -disabledimage option.
345  * NULL means no image right now. */
346  Tk_Image image; /* Image to display in window, or NULL if no
347  * image at present. */
348  Tk_Image activeImage; /* Image to display in window, or NULL if no
349  * image at present. */
350  Tk_Image disabledImage; /* Image to display in window, or NULL if no
351  * image at present. */
352  bool redrawing; // set this flag to disable enqueing of eventually redraw
353  CairoImage *cairoItem;
354  };
355 
356  namespace TkImageCode
357  {
358  int CreateImage(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr,
359  int objc,Tcl_Obj *CONST objv[],Tk_ConfigSpec[]);
360  void DeleteImage(Tk_Canvas canvas, Tk_Item *itemPtr, Display *display);
362  int configureCairoItem(Tcl_Interp *interp, Tk_Canvas canvas,
363  Tk_Item *itemPtr, int objc,
364  Tcl_Obj *CONST objv[],
365  int flags, Tk_ConfigSpec configSpecs[]);
366  void ComputeImageBbox(Tk_Canvas canvas, ImageItem *imgPtr);
367  }
368 
371  template <class C>
372  int createImage(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr,
373  int objc,Tcl_Obj *CONST objv[])
374  {
375  if (TkImageCode::CreateImage(interp,canvas,itemPtr,objc,objv,C::configSpecs)==TCL_OK)
376  {
377  ImageItem* imgPtr=(ImageItem*)(itemPtr);
378  imgPtr->cairoItem = new C;
379  imgPtr->cairoItem->setMatrix(imgPtr->scale, imgPtr->rotation);
380  TkImageCode::ComputeImageBbox(canvas, imgPtr);
381  return TCL_OK;
382  }
383  TkImageCode::DeleteImage(canvas, itemPtr,
384  Tk_Display(Tk_CanvasTkwin(canvas)));
385  return TCL_ERROR;
386  }
387 
388  const Tk_ItemType& cairoItemType();
389 #endif // #ifdef TK
390 
391  }
392 
393 }
394 #endif
395 #endif
Tcl_Interp * interp()
default interpreter. Set to NULL when interp() is destroyed
Definition: tcl++.h:222
Basic C++ TCL access.
dynamic array class
_OPENMP
Definition: accessor.h:16