diff options
Diffstat (limited to 'libs/surfaces/push2/canvas.cc')
-rw-r--r-- | libs/surfaces/push2/canvas.cc | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/libs/surfaces/push2/canvas.cc b/libs/surfaces/push2/canvas.cc new file mode 100644 index 0000000000..e178e0d8b5 --- /dev/null +++ b/libs/surfaces/push2/canvas.cc @@ -0,0 +1,190 @@ +/* + Copyright (C) 2016 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <cairomm/region.h> +#include <cairomm/surface.h> +#include <cairomm/context.h> + +#include "canvas.h" +#include "layout.h" +#include "push2.h" + +using namespace ArdourCanvas; +using namespace ArdourSurface; + +const int Push2Canvas::pixels_per_row = 1024; + +Push2Canvas::Push2Canvas (Push2& pr, int c, int r) + : p2 (pr) + , _cols (c) + , _rows (r) + , frame_buffer (Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, _cols, _rows)) +{ + context = Cairo::Context::create (frame_buffer); + expose_region = Cairo::Region::create (); + + device_frame_buffer = new uint16_t[pixel_area()]; + memset (device_frame_buffer, 0, sizeof (uint16_t) * pixel_area()); + + frame_header[0] = 0xef; + frame_header[1] = 0xcd; + frame_header[2] = 0xab; + frame_header[3] = 0x89; + + memset (&frame_header[4], 0, 12); +} + +Push2Canvas::~Push2Canvas () +{ + delete [] device_frame_buffer; + device_frame_buffer = 0; +} + +bool +Push2Canvas::vblank () +{ + /* re-render dirty areas, if any */ + + if (expose ()) { + /* something rendered, update device_frame_buffer */ + blit_to_device_frame_buffer (); + } + + int transferred = 0; + const int timeout_msecs = 1000; + int err; + + /* transfer to device */ + + if ((err = libusb_bulk_transfer (p2.usb_handle(), 0x01, frame_header, sizeof (frame_header), &transferred, timeout_msecs))) { + return false; + } + + if ((err = libusb_bulk_transfer (p2.usb_handle(), 0x01, (uint8_t*) device_frame_buffer, 2 * pixel_area (), &transferred, timeout_msecs))) { + return false; + } + + return true; +} + +void +Push2Canvas::request_redraw () +{ + request_redraw (Rect (0, 0, _cols, _rows)); +} + +void +Push2Canvas::request_redraw (Rect const & r) +{ + Cairo::RectangleInt cr; + + cr.x = r.x1; + cr.y = r.y1; + cr.width = r.width(); + cr.width = r.height(); + + expose_region->do_union (cr); + + /* next vblank will redraw */ +} + +bool +Push2Canvas::expose () +{ + if (expose_region->empty()) { + return false; /* nothing drawn */ + } + + /* set up clipping */ + + const int nrects = expose_region->get_num_rectangles (); + + for (int n = 0; n < nrects; ++n) { + Cairo::RectangleInt r = expose_region->get_rectangle (n); + context->rectangle (r.x, r.y, r.width, r.height); + } + + context->clip (); + + Push2Layout* layout = p2.current_layout(); + + if (layout) { + Cairo::RectangleInt r = expose_region->get_extents(); + layout->render (Rect (r.x, r.y, r.x + r.width, r.y + r.height), context); + } + + context->reset_clip (); + + return true; +} + +/** render host-side frame buffer (a Cairo ImageSurface) to the current + * device-side frame buffer. The device frame buffer will be pushed to the + * device on the next call to vblank() + */ + +int +Push2Canvas::blit_to_device_frame_buffer () +{ + /* ensure that all drawing has been done before we fetch pixel data */ + + frame_buffer->flush (); + + const int stride = 3840; /* bytes per row for Cairo::FORMAT_ARGB32 */ + const uint8_t* data = frame_buffer->get_data (); + + /* fill frame buffer (320kB) */ + + uint16_t* fb = (uint16_t*) device_frame_buffer; + + for (int row = 0; row < _rows; ++row) { + + const uint8_t* dp = data + row * stride; + + for (int col = 0; col < _cols; ++col) { + + /* fetch r, g, b (range 0..255). Ignore alpha */ + + const int r = (*((const uint32_t*)dp) >> 16) & 0xff; + const int g = (*((const uint32_t*)dp) >> 8) & 0xff; + const int b = *((const uint32_t*)dp) & 0xff; + + /* convert to 5 bits, 6 bits, 5 bits, respectively */ + /* generate 16 bit BGB565 value */ + + *fb++ = (r >> 3) | ((g & 0xfc) << 3) | ((b & 0xf8) << 8); + + /* the push2 docs state that we should xor the pixel + * data. Doing so doesn't work correctly, and not doing + * so seems to work fine (colors roughly match intended + * values). + */ + + dp += 4; + } + + /* skip 128 bytes to next line. This is filler, used to avoid line borders occuring in the middle of 512 + byte USB buffers + */ + + fb += 64; /* 128 bytes = 64 int16_t */ + } + + return 0; +} |