12c404fb9e579f851253e589f6374c2ba43e2a04
[ardour.git] / libs / surfaces / push2 / canvas.cc
1 /*
2     Copyright (C) 2016 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <cairomm/region.h>
21 #include <cairomm/surface.h>
22 #include <cairomm/context.h>
23
24 #include "pbd/compose.h"
25
26 #include "ardour/debug.h"
27
28 #include "canvas.h"
29 #include "layout.h"
30 #include "push2.h"
31
32 using namespace ArdourCanvas;
33 using namespace ArdourSurface;
34 using namespace PBD;
35
36 const int Push2Canvas::pixels_per_row = 1024;
37
38 Push2Canvas::Push2Canvas (Push2& pr, int c, int r)
39         : p2 (pr)
40         , _cols (c)
41         , _rows (r)
42         , frame_buffer (Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, _cols, _rows))
43 {
44         context = Cairo::Context::create (frame_buffer);
45         expose_region = Cairo::Region::create ();
46
47         device_frame_buffer = new uint16_t[pixel_area()];
48         memset (device_frame_buffer, 0, sizeof (uint16_t) * pixel_area());
49
50         frame_header[0] = 0xef;
51         frame_header[1] = 0xcd;
52         frame_header[2] = 0xab;
53         frame_header[3] = 0x89;
54
55         memset (&frame_header[4], 0, 12);
56 }
57
58 Push2Canvas::~Push2Canvas ()
59 {
60         delete [] device_frame_buffer;
61         device_frame_buffer = 0;
62 }
63
64 bool
65 Push2Canvas::vblank ()
66 {
67         /* re-render dirty areas, if any */
68
69         if (expose ()) {
70                 DEBUG_TRACE (DEBUG::Push2, "re-blit to device frame buffer\n");
71                 /* something rendered, update device_frame_buffer */
72                 blit_to_device_frame_buffer ();
73
74 #define RENDER_LAYOUTS
75 #ifdef  RENDER_LAYOUTS
76                 if (p2.current_layout()) {
77                         std::string s = p2.current_layout()->name();
78                         s += ".png";
79                         frame_buffer->write_to_png (s);
80                 }
81 #endif
82         }
83
84         int transferred = 0;
85         const int timeout_msecs = 1000;
86         int err;
87
88         /* transfer to device */
89
90         if ((err = libusb_bulk_transfer (p2.usb_handle(), 0x01, frame_header, sizeof (frame_header), &transferred, timeout_msecs))) {
91                 return false;
92         }
93
94         if ((err = libusb_bulk_transfer (p2.usb_handle(), 0x01, (uint8_t*) device_frame_buffer, 2 * pixel_area (), &transferred, timeout_msecs))) {
95                 return false;
96         }
97
98         return true;
99 }
100
101 void
102 Push2Canvas::request_redraw ()
103 {
104         request_redraw (Rect (0, 0, _cols, _rows));
105 }
106
107 void
108 Push2Canvas::request_redraw (Rect const & r)
109 {
110         Cairo::RectangleInt cr;
111
112         cr.x = r.x0;
113         cr.y = r.y0;
114         cr.width = r.width();
115         cr.height = r.height();
116
117         // DEBUG_TRACE (DEBUG::Push2, string_compose ("invalidate rect %1\n", r));
118
119         expose_region->do_union (cr);
120
121         /* next vblank will redraw */
122 }
123
124 bool
125 Push2Canvas::expose ()
126 {
127         if (expose_region->empty()) {
128                 return false; /* nothing drawn */
129         }
130
131         /* set up clipping */
132
133         const int nrects = expose_region->get_num_rectangles ();
134
135         //DEBUG_TRACE (DEBUG::Push2, string_compose ("expose with %1 rects\n", nrects));
136
137         for (int n = 0; n < nrects; ++n) {
138                 Cairo::RectangleInt r = expose_region->get_rectangle (n);
139                 context->rectangle (r.x, r.y, r.width, r.height);
140         }
141
142         context->clip ();
143
144         Push2Layout* layout = p2.current_layout();
145
146         if (layout) {
147                 /* all layouts cover (at least) the full size of the video
148                    display, so we do not need to check if the layout intersects
149                    the bounding box of the full expose region.
150                 */
151                 Cairo::RectangleInt r = expose_region->get_extents();
152                 Rect rr (r.x, r.y, r.x + r.width, r.y + r.height);
153                 //DEBUG_TRACE (DEBUG::Push2, string_compose ("render layout with %1\n", rr));
154                 layout->render (Rect (r.x, r.y, r.x + r.width, r.y + r.height), context);
155         }
156
157         context->reset_clip ();
158
159         /* why is there no "reset()" method for Cairo::Region? */
160
161         expose_region = Cairo::Region::create ();
162
163         return true;
164 }
165
166 /** render host-side frame buffer (a Cairo ImageSurface) to the current
167  * device-side frame buffer. The device frame buffer will be pushed to the
168  * device on the next call to vblank()
169  */
170
171 int
172 Push2Canvas::blit_to_device_frame_buffer ()
173 {
174         /* ensure that all drawing has been done before we fetch pixel data */
175
176         frame_buffer->flush ();
177
178         const int stride = 3840; /* bytes per row for Cairo::FORMAT_ARGB32 */
179         const uint8_t* data = frame_buffer->get_data ();
180
181         /* fill frame buffer (320kB) */
182
183         uint16_t* fb = (uint16_t*) device_frame_buffer;
184
185         for (int row = 0; row < _rows; ++row) {
186
187                 const uint8_t* dp = data + row * stride;
188
189                 for (int col = 0; col < _cols; ++col) {
190
191                         /* fetch r, g, b (range 0..255). Ignore alpha */
192
193                         const int r = (*((const uint32_t*)dp) >> 16) & 0xff;
194                         const int g = (*((const uint32_t*)dp) >> 8) & 0xff;
195                         const int b = *((const uint32_t*)dp) & 0xff;
196
197                         /* convert to 5 bits, 6 bits, 5 bits, respectively */
198                         /* generate 16 bit BGB565 value */
199
200                         *fb++ = (r >> 3) | ((g & 0xfc) << 3) | ((b & 0xf8) << 8);
201
202                         /* the push2 docs state that we should xor the pixel
203                          * data. Doing so doesn't work correctly, and not doing
204                          * so seems to work fine (colors roughly match intended
205                          * values).
206                          */
207
208                         dp += 4;
209                 }
210
211                 /* skip 128 bytes to next line. This is filler, used to avoid line borders occuring in the middle of 512
212                    byte USB buffers
213                 */
214
215                 fb += 64; /* 128 bytes = 64 int16_t */
216         }
217
218         return 0;
219 }
220
221 void
222 Push2Canvas::request_size (Duple)
223 {
224         /* fixed size canvas */
225 }
226
227 Rect
228 Push2Canvas::visible_area () const
229 {
230         /* may need to get more sophisticated once we do scrolling */
231         return Rect (0, 0, 960, 160);
232 }