a slew of unconnected mostly minor tweaks to get Push2 support back to where it was...
[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
75         int transferred = 0;
76         const int timeout_msecs = 1000;
77         int err;
78
79         /* transfer to device */
80
81         if ((err = libusb_bulk_transfer (p2.usb_handle(), 0x01, frame_header, sizeof (frame_header), &transferred, timeout_msecs))) {
82                 return false;
83         }
84
85         if ((err = libusb_bulk_transfer (p2.usb_handle(), 0x01, (uint8_t*) device_frame_buffer, 2 * pixel_area (), &transferred, timeout_msecs))) {
86                 return false;
87         }
88
89         return true;
90 }
91
92 void
93 Push2Canvas::request_redraw ()
94 {
95         request_redraw (Rect (0, 0, _cols, _rows));
96 }
97
98 void
99 Push2Canvas::request_redraw (Rect const & r)
100 {
101         Cairo::RectangleInt cr;
102
103         cr.x = r.x0;
104         cr.y = r.y0;
105         cr.width = r.width();
106         cr.height = r.height();
107
108         DEBUG_TRACE (DEBUG::Push2, string_compose ("invalidate rect %1\n", r));
109
110         expose_region->do_union (cr);
111
112         /* next vblank will redraw */
113 }
114
115 bool
116 Push2Canvas::expose ()
117 {
118         if (expose_region->empty()) {
119                 return false; /* nothing drawn */
120         }
121
122         /* set up clipping */
123
124         const int nrects = expose_region->get_num_rectangles ();
125
126         DEBUG_TRACE (DEBUG::Push2, string_compose ("expose with %1 rects\n", nrects));
127
128         for (int n = 0; n < nrects; ++n) {
129                 Cairo::RectangleInt r = expose_region->get_rectangle (n);
130                 context->rectangle (r.x, r.y, r.width, r.height);
131         }
132
133         context->clip ();
134
135         Push2Layout* layout = p2.current_layout();
136
137         if (layout) {
138                 Cairo::RectangleInt r = expose_region->get_extents();
139                 Rect rr (r.x, r.y, r.x + r.width, r.y + r.height);
140                 DEBUG_TRACE (DEBUG::Push2, string_compose ("render layout with %1\n", rr));
141                 layout->render (Rect (r.x, r.y, r.x + r.width, r.y + r.height), context);
142         }
143
144         context->reset_clip ();
145
146         /* why is there no "reset()" method for Cairo::Region? */
147
148         expose_region = Cairo::Region::create ();
149
150         return true;
151 }
152
153 /** render host-side frame buffer (a Cairo ImageSurface) to the current
154  * device-side frame buffer. The device frame buffer will be pushed to the
155  * device on the next call to vblank()
156  */
157
158 int
159 Push2Canvas::blit_to_device_frame_buffer ()
160 {
161         /* ensure that all drawing has been done before we fetch pixel data */
162
163         frame_buffer->flush ();
164
165         const int stride = 3840; /* bytes per row for Cairo::FORMAT_ARGB32 */
166         const uint8_t* data = frame_buffer->get_data ();
167
168         /* fill frame buffer (320kB) */
169
170         uint16_t* fb = (uint16_t*) device_frame_buffer;
171
172         for (int row = 0; row < _rows; ++row) {
173
174                 const uint8_t* dp = data + row * stride;
175
176                 for (int col = 0; col < _cols; ++col) {
177
178                         /* fetch r, g, b (range 0..255). Ignore alpha */
179
180                         const int r = (*((const uint32_t*)dp) >> 16) & 0xff;
181                         const int g = (*((const uint32_t*)dp) >> 8) & 0xff;
182                         const int b = *((const uint32_t*)dp) & 0xff;
183
184                         /* convert to 5 bits, 6 bits, 5 bits, respectively */
185                         /* generate 16 bit BGB565 value */
186
187                         *fb++ = (r >> 3) | ((g & 0xfc) << 3) | ((b & 0xf8) << 8);
188
189                         /* the push2 docs state that we should xor the pixel
190                          * data. Doing so doesn't work correctly, and not doing
191                          * so seems to work fine (colors roughly match intended
192                          * values).
193                          */
194
195                         dp += 4;
196                 }
197
198                 /* skip 128 bytes to next line. This is filler, used to avoid line borders occuring in the middle of 512
199                    byte USB buffers
200                 */
201
202                 fb += 64; /* 128 bytes = 64 int16_t */
203         }
204
205         return 0;
206 }
207
208 void
209 Push2Canvas::request_size (Duple)
210 {
211         /* fixed size canvas */
212 }
213
214 Rect
215 Push2Canvas::visible_area () const
216 {
217         /* may need to get more sophisticated once we do scrolling */
218         return Rect (0, 0, 960, 160);
219 }