2 Copyright (C) 2016 Paul Davis
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.
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.
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.
22 #include <cairomm/region.h>
23 #include <cairomm/surface.h>
24 #include <cairomm/context.h>
26 #include "pbd/compose.h"
27 #include "pbd/error.h"
30 #include "ardour/debug.h"
37 #define Rect ArdourCanvas::Rect
40 using namespace ArdourCanvas;
41 using namespace ArdourSurface;
44 const int Push2Canvas::pixels_per_row = 1024;
46 Push2Canvas::Push2Canvas (Push2& pr, int c, int r)
50 , frame_buffer (Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, _cols, _rows))
52 context = Cairo::Context::create (frame_buffer);
53 expose_region = Cairo::Region::create ();
55 device_frame_buffer = new uint16_t[pixel_area()];
56 memset (device_frame_buffer, 0, sizeof (uint16_t) * pixel_area());
58 frame_header[0] = 0xef;
59 frame_header[1] = 0xcd;
60 frame_header[2] = 0xab;
61 frame_header[3] = 0x89;
63 memset (&frame_header[4], 0, 12);
66 Push2Canvas::~Push2Canvas ()
68 delete [] device_frame_buffer;
69 device_frame_buffer = 0;
73 Push2Canvas::vblank ()
75 /* re-render dirty areas, if any */
78 /* something rendered, update device_frame_buffer */
79 blit_to_device_frame_buffer ();
83 if (p2.current_layout()) {
84 std::string s = p2.current_layout()->name();
86 frame_buffer->write_to_png (s);
92 const int timeout_msecs = 1000;
95 /* transfer to device */
97 if ((err = libusb_bulk_transfer (p2.usb_handle(), 0x01, frame_header, sizeof (frame_header), &transferred, timeout_msecs))) {
101 if ((err = libusb_bulk_transfer (p2.usb_handle(), 0x01, (uint8_t*) device_frame_buffer, 2 * pixel_area (), &transferred, timeout_msecs))) {
109 Push2Canvas::request_redraw ()
111 request_redraw (Rect (0, 0, _cols, _rows));
115 Push2Canvas::request_redraw (Rect const & r)
117 Cairo::RectangleInt cr;
121 cr.width = r.width();
122 cr.height = r.height();
124 // DEBUG_TRACE (DEBUG::Push2, string_compose ("invalidate rect %1\n", r));
126 expose_region->do_union (cr);
128 /* next vblank will redraw */
132 Push2Canvas::expose ()
134 if (expose_region->empty()) {
135 return false; /* nothing drawn */
138 /* set up clipping */
140 const int nrects = expose_region->get_num_rectangles ();
142 //DEBUG_TRACE (DEBUG::Push2, string_compose ("expose with %1 rects\n", nrects));
144 for (int n = 0; n < nrects; ++n) {
145 Cairo::RectangleInt r = expose_region->get_rectangle (n);
146 context->rectangle (r.x, r.y, r.width, r.height);
151 Push2Layout* layout = p2.current_layout();
154 /* all layouts cover (at least) the full size of the video
155 display, so we do not need to check if the layout intersects
156 the bounding box of the full expose region.
158 Cairo::RectangleInt r = expose_region->get_extents();
159 Rect rr (r.x, r.y, r.x + r.width, r.y + r.height);
160 //DEBUG_TRACE (DEBUG::Push2, string_compose ("render layout with %1\n", rr));
161 layout->render (Rect (r.x, r.y, r.x + r.width, r.y + r.height), context);
164 context->reset_clip ();
166 /* why is there no "reset()" method for Cairo::Region? */
168 expose_region = Cairo::Region::create ();
173 /** render host-side frame buffer (a Cairo ImageSurface) to the current
174 * device-side frame buffer. The device frame buffer will be pushed to the
175 * device on the next call to vblank()
179 Push2Canvas::blit_to_device_frame_buffer ()
181 /* ensure that all drawing has been done before we fetch pixel data */
183 frame_buffer->flush ();
185 const int stride = 3840; /* bytes per row for Cairo::FORMAT_ARGB32 */
186 const uint8_t* data = frame_buffer->get_data ();
188 /* fill frame buffer (320kB) */
190 uint16_t* fb = (uint16_t*) device_frame_buffer;
192 for (int row = 0; row < _rows; ++row) {
194 const uint8_t* dp = data + row * stride;
196 for (int col = 0; col < _cols; ++col) {
198 /* fetch r, g, b (range 0..255). Ignore alpha */
200 const int r = (*((const uint32_t*)dp) >> 16) & 0xff;
201 const int g = (*((const uint32_t*)dp) >> 8) & 0xff;
202 const int b = *((const uint32_t*)dp) & 0xff;
204 /* convert to 5 bits, 6 bits, 5 bits, respectively */
205 /* generate 16 bit BGB565 value */
207 *fb++ = (r >> 3) | ((g & 0xfc) << 3) | ((b & 0xf8) << 8);
209 /* the push2 docs state that we should xor the pixel
210 * data. Doing so doesn't work correctly, and not doing
211 * so seems to work fine (colors roughly match intended
218 /* skip 128 bytes to next line. This is filler, used to avoid line borders occuring in the middle of 512
222 fb += 64; /* 128 bytes = 64 int16_t */
229 Push2Canvas::request_size (Duple)
231 /* fixed size canvas */
235 Push2Canvas::visible_area () const
237 /* may need to get more sophisticated once we do scrolling */
238 return Rect (0, 0, 960, 160);
241 Glib::RefPtr<Pango::Context>
242 Push2Canvas::get_pango_context ()
244 if (!pango_context) {
245 PangoFontMap* map = pango_cairo_font_map_get_default ();
247 error << _("Default Cairo font map is null!") << endmsg;
248 return Glib::RefPtr<Pango::Context> ();
251 PangoContext* context = pango_font_map_create_context (map);
254 error << _("cannot create new PangoContext from cairo font map") << endmsg;
255 return Glib::RefPtr<Pango::Context> ();
258 pango_context = Glib::wrap (context);
261 return pango_context;