1212
1313from ._frontend import module_name , module_version
1414
15+ from .binary import array_to_binary
16+
1517
1618def to_camel_case (snake_str ):
1719 """Turn a snake_case string into a camelCase one."""
@@ -42,6 +44,7 @@ def __init__(self, *args, **kwargs):
4244 """Create a Canvas widget."""
4345 self .caching = kwargs .get ('caching' , False )
4446 self ._commands_cache = []
47+ self ._buffers_cache = []
4548
4649 super (Canvas , self ).__init__ (* args , ** kwargs )
4750 self .layout .width = str (self .size [0 ]) + 'px'
@@ -50,19 +53,19 @@ def __init__(self, *args, **kwargs):
5053 # Rectangles methods
5154 def fill_rect (self , x , y , width , height ):
5255 """Draw a filled rectangle."""
53- self ._send_canvas_command ('fillRect' , x , y , width , height )
56+ self ._send_canvas_command ('fillRect' , ( x , y , width , height ) )
5457
5558 def stroke_rect (self , x , y , width , height ):
5659 """Draw a rectangular outline."""
57- self ._send_canvas_command ('strokeRect' , x , y , width , height )
60+ self ._send_canvas_command ('strokeRect' , ( x , y , width , height ) )
5861
5962 def clear_rect (self , x , y , width , height ):
6063 """Clear the specified rectangular area, making it fully transparent."""
61- self ._send_canvas_command ('clearRect' , x , y , width , height )
64+ self ._send_canvas_command ('clearRect' , ( x , y , width , height ) )
6265
6366 def rect (self , x , y , width , height ):
6467 """Draw a rectangle whose top-left corner is specified by (x, y) with the specified width and height."""
65- self ._send_canvas_command ('rect' , x , y , width , height )
68+ self ._send_canvas_command ('rect' , ( x , y , width , height ) )
6669
6770 # Paths methods
6871 def begin_path (self ):
@@ -87,27 +90,27 @@ def fill(self):
8790
8891 def move_to (self , x , y ):
8992 """Move the "pen" to the given (x, y) coordinates."""
90- self ._send_canvas_command ('moveTo' , x , y )
93+ self ._send_canvas_command ('moveTo' , ( x , y ) )
9194
9295 def line_to (self , x , y ):
9396 """Add a straight line to the current path by connecting the path's last point to the specified (x, y) coordinates.
9497
9598 Like other methods that modify the current path, this method does not directly render anything. To
9699 draw the path onto the canvas, you can use the fill() or stroke() methods.
97100 """
98- self ._send_canvas_command ('lineTo' , x , y )
101+ self ._send_canvas_command ('lineTo' , ( x , y ) )
99102
100103 def arc (self , x , y , radius , start_angle , end_angle , anticlockwise = False ):
101104 """Create a circular arc centered at (x, y) with a radius of radius.
102105
103106 The path starts at startAngle and ends at endAngle, and travels in the direction given by
104107 anticlockwise (defaulting to clockwise).
105108 """
106- self ._send_canvas_command ('arc' , x , y , radius , start_angle , end_angle , anticlockwise )
109+ self ._send_canvas_command ('arc' , ( x , y , radius , start_angle , end_angle , anticlockwise ) )
107110
108111 def arc_to (self , x1 , y1 , x2 , y2 , radius ):
109112 """Add a circular arc to the current path, using the given control points and radius."""
110- self ._send_canvas_command ('arcTo' , x1 , y1 , x2 , y2 , radius )
113+ self ._send_canvas_command ('arcTo' , ( x1 , y1 , x2 , y2 , radius ) )
111114
112115 def quadratic_curve_to (self , cp1x , cp1y , x , y ):
113116 """Add a quadratic Bezier curve to the current path.
@@ -116,7 +119,7 @@ def quadratic_curve_to(self, cp1x, cp1y, x, y):
116119 The starting point is the latest point in the current path, which can be changed using move_to()
117120 before creating the quadratic Bezier curve.
118121 """
119- self ._send_canvas_command ('quadraticCurveTo' , cp1x , cp1y , x , y )
122+ self ._send_canvas_command ('quadraticCurveTo' , ( cp1x , cp1y , x , y ) )
120123
121124 def bezier_curve_to (self , cp1x , cp1y , cp2x , cp2y , x , y ):
122125 """Add a cubic Bezier curve to the current path.
@@ -125,16 +128,27 @@ def bezier_curve_to(self, cp1x, cp1y, cp2x, cp2y, x, y):
125128 The starting point is the latest point in the current path, which can be changed using move_to()
126129 before creating the Bezier curve.
127130 """
128- self ._send_canvas_command ('bezierCurveTo' , cp1x , cp1y , cp2x , cp2y , x , y )
131+ self ._send_canvas_command ('bezierCurveTo' , ( cp1x , cp1y , cp2x , cp2y , x , y ) )
129132
130133 # Text methods
131134 def fill_text (self , text , x , y , max_width = None ):
132135 """Fill a given text at the given (x,y) position. Optionally with a maximum width to draw."""
133- self ._send_canvas_command ('fillText' , text , x , y , max_width )
136+ self ._send_canvas_command ('fillText' , ( text , x , y , max_width ) )
134137
135138 def stroke_text (self , text , x , y , max_width = None ):
136139 """Stroke a given text at the given (x,y) position. Optionally with a maximum width to draw."""
137- self ._send_canvas_command ('strokeText' , text , x , y , max_width )
140+ self ._send_canvas_command ('strokeText' , (text , x , y , max_width ))
141+
142+ # Image methods
143+ def put_image_data (self , image_data , dx , dy ):
144+ """Draw an image on the Canvas.
145+
146+ `image_data` being a NumPy array defining the image to draw
147+ and `x` and `y` the pixel position where to draw.
148+ This method is not affected by the canvas transformation matrix.
149+ """
150+ shape , image_buffer = array_to_binary (image_data )
151+ self ._send_canvas_command ('putImageData' , ({'shape' : shape }, dx , dy ), (image_buffer , ))
138152
139153 def clear (self ):
140154 """Clear the entire canvas."""
@@ -145,10 +159,11 @@ def flush(self):
145159 if not self .caching :
146160 return
147161
148- self .send (self ._commands_cache )
162+ self .send (self ._commands_cache , self . _buffers_cache )
149163
150164 self .caching = False
151165 self ._commands_cache = []
166+ self ._buffers_cache = []
152167
153168 @observe ('fill_style' , 'stroke_style' , 'global_alpha' , 'font' , 'textAlign' , 'textBaseline' , 'direction' )
154169 def _on_set_attr (self , change ):
@@ -159,14 +174,20 @@ def _on_set_attr(self, change):
159174 }
160175 self ._send_command (command )
161176
162- def _send_canvas_command (self , name , * args ):
163- self ._send_command ({'name' : name , 'args' : [arg for arg in args if arg is not None ]})
177+ def _send_canvas_command (self , name , args = [], buffers = []):
178+ command = {
179+ 'name' : name ,
180+ 'n_buffers' : len (buffers ),
181+ 'args' : [arg for arg in args if arg is not None ]
182+ }
183+ self ._send_command (command , buffers )
164184
165- def _send_command (self , command ):
185+ def _send_command (self , command , buffers = [] ):
166186 if self .caching :
167187 self ._commands_cache .append (command )
188+ self ._buffers_cache += buffers
168189 else :
169- self .send (command )
190+ self .send (command , buffers )
170191
171192
172193class MultiCanvas (DOMWidget ):
0 commit comments