Index: experimental/nanomsg/picture_demo.cpp |
diff --git a/experimental/nanomsg/picture_demo.cpp b/experimental/nanomsg/picture_demo.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..5a3ebdc8167877e7f29a353f7388d19b192b4163 |
--- /dev/null |
+++ b/experimental/nanomsg/picture_demo.cpp |
@@ -0,0 +1,207 @@ |
+#include "nanomsg/src/nn.h" |
+#include "nanomsg/src/pipeline.h" |
+#include "nanomsg/src/reqrep.h" |
+ |
+#include "SkCanvas.h" |
+#include "SkCommandLineFlags.h" |
+#include "SkData.h" |
+#include "SkForceLinking.h" |
+#include "SkGraphics.h" |
+#include "SkImageEncoder.h" |
+#include "SkOSFile.h" |
+#include "SkPicture.h" |
+#include "SkRandom.h" |
+#include "SkStream.h" |
+ |
+__SK_FORCE_IMAGE_DECODER_LINKING; |
+ |
+// To keep things simple, PictureHeader is fixed-size POD. |
+struct PictureHeader { |
+ SkMatrix matrix; |
+ SkRect clip; |
+ SkXfermode::Mode xfermode; |
+ pid_t pid; |
+ uint8_t alpha; |
+ |
+ PictureHeader() |
+ : matrix(SkMatrix::I()) |
+ , clip(SkRect::MakeLargest()) |
+ , xfermode(SkXfermode::kSrcOver_Mode) |
+ , pid(getpid()) |
+ , alpha(0xFF) {} |
+}; |
+ |
+// A little adaptor: nn_iovec wants a non-const pointer for no obvious reason. |
+static struct nn_iovec create_iov(const void* ptr, size_t size) { |
+ struct nn_iovec iov = { const_cast<void*>(ptr), size }; |
+ return iov; |
+} |
+ |
+static void send_picture(int socket, const PictureHeader& header, const SkData& skp) { |
+ // Vectored IO lets us send header and skp contiguously without first |
+ // copying them to a contiguous buffer. |
+ struct nn_iovec iov[] = { |
+ create_iov(&header, sizeof(header)), |
+ create_iov(skp.data(), skp.size()), |
+ }; |
+ |
+ struct nn_msghdr msg; |
+ sk_bzero(&msg, sizeof(msg)); |
+ msg.msg_iov = iov; |
+ msg.msg_iovlen = SK_ARRAY_COUNT(iov); |
+ |
+ nn_sendmsg(socket, &msg, 0/*flags*/); |
+} |
+ |
+static SkPicture* recv_picture(int socket, PictureHeader* header) { |
+ static const size_t hSize = sizeof(*header); // It's easy to slip up and use sizeof(header). |
+ |
+ void* msg; |
+ int size = nn_recv(socket, &msg, NN_MSG, 0/*flags*/); |
+ SkDebugf("%d bytes", size); |
+ |
+ // msg is first a fixed-size header, then an .skp. |
+ memcpy(header, msg, hSize); |
+ SkMemoryStream stream((uint8_t*)msg + hSize, size - hSize); |
+ SkPicture* pic = SkPicture::CreateFromStream(&stream); |
+ |
+ SkDebugf(" from proccess %d:", header->pid); |
+ |
+ nn_freemsg(msg); |
+ return pic; |
+} |
+ |
+static void client(const char* skpPath, const char* dataEndpoint) { |
+ // Read the .skp. |
+ SkAutoTUnref<const SkData> skp(SkData::NewFromFileName(skpPath)); |
+ if (!skp) { |
+ SkDebugf("Couldn't read %s\n", skpPath); |
+ exit(1); |
+ } |
+ SkMemoryStream stream(skp->data(), skp->size()); |
+ SkAutoTUnref<SkPicture> picture(SkPicture::CreateFromStream(&stream)); |
+ |
+ PictureHeader header; |
+ SkRandom rand(picture->width() * picture->height()); |
+ SkScalar r = rand.nextRangeScalar(0, picture->width()), |
+ b = rand.nextRangeScalar(0, picture->height()), |
+ l = rand.nextRangeScalar(0, r), |
+ t = rand.nextRangeScalar(0, b); |
+ header.clip.setLTRB(l,t,r,b); |
+ header.matrix.setTranslate(-l, -t); |
+ header.matrix.postRotate(rand.nextRangeScalar(-25, 25)); |
+ header.alpha = 0x7F; |
+ |
+ //Clients use NN_REQ (request) type sockets. |
+ int socket = nn_socket(AF_SP, NN_REQ); |
+ |
+ // Clients connect a socket to an endpoint. |
+ nn_connect(socket, dataEndpoint); |
+ |
+ // Send the picture and its header. |
+ SkDebugf("Sending %s (%d bytes)...", skpPath, skp->size()); |
+ send_picture(socket, header, *skp); |
+ |
+ // Wait for ack. |
+ uint8_t ack; |
+ nn_recv(socket, &ack, sizeof(ack), 0/*flags*/); |
+ SkDebugf(" ok.\n"); |
+} |
+ |
+// Wait until socketA or socketB has something to tell us, and return which one. |
+static int poll_in(int socketA, int socketB) { |
+ struct nn_pollfd polls[] = { |
+ { socketA, NN_POLLIN, 0 }, |
+ { socketB, NN_POLLIN, 0 }, |
+ }; |
+ |
+ nn_poll(polls, SK_ARRAY_COUNT(polls), -1/*no timeout*/); |
+ |
+ if (polls[0].revents & NN_POLLIN) { return socketA; } |
+ if (polls[1].revents & NN_POLLIN) { return socketB; } |
+ |
+ SkFAIL("unreachable"); |
+ return 0; |
+} |
+ |
+static void server(const char* dataEndpoint, const char* controlEndpoint, SkCanvas* canvas) { |
+ // NN_REP sockets receive a request then make a reply. NN_PULL sockets just receive a request. |
+ int data = nn_socket(AF_SP, NN_REP); |
+ int control = nn_socket(AF_SP, NN_PULL); |
+ |
+ // Servers bind a socket to an endpoint. |
+ nn_bind(data, dataEndpoint); |
+ nn_bind(control, controlEndpoint); |
+ |
+ while (true) { |
+ int ready = poll_in(data, control); |
+ |
+ // If we got any message on the control socket, we can stop. |
+ if (ready == control) { |
+ break; |
+ } |
+ |
+ // We should have an .skp waiting for us on data socket. |
+ PictureHeader header; |
+ SkAutoTUnref<SkPicture> picture(recv_picture(data, &header)); |
+ |
+ SkPaint paint; |
+ paint.setAlpha(header.alpha); |
+ paint.setXfermodeMode(header.xfermode); |
+ |
+ canvas->saveLayer(NULL, &paint); |
+ canvas->concat(header.matrix); |
+ canvas->clipRect(header.clip); |
+ picture->draw(canvas); |
+ canvas->restore(); |
+ SkDebugf(" drew"); |
+ |
+ // Send back an ack. |
+ uint8_t ack = 42; |
+ nn_send(data, &ack, sizeof(ack), 0/*flags*/); |
+ SkDebugf(" and acked.\n"); |
+ } |
+} |
+ |
+static void stop(const char* controlEndpoint) { |
+ // An NN_PUSH socket can send messages but not receive them. |
+ int control = nn_socket(AF_SP, NN_PUSH); |
+ nn_connect(control, controlEndpoint); |
+ |
+ // Sending anything (including this 0-byte message) will tell server() to stop. |
+ nn_send(control, NULL, 0, 0/*flags*/); |
+} |
+ |
+DEFINE_string2(skp, r, "", ".skp to send (as client)"); |
+DEFINE_string2(png, w, "", ".png to write (as server)"); |
+DEFINE_bool(stop, false, "If true, tell server to stop and write its canvas out as a .png."); |
+DEFINE_string(data, "ipc://nanomsg-picture-data", "Endpoint for sending pictures."); |
+DEFINE_string(control, "ipc://nanomsg-picture-control", "Endpoint for control channel."); |
+ |
+int main(int argc, char** argv) { |
+ SkAutoGraphics ag; |
+ SkCommandLineFlags::Parse(argc, argv); |
+ |
+ if (FLAGS_stop) { |
+ stop(FLAGS_control[0]); |
+ } |
+ |
+ if (!FLAGS_skp.isEmpty()) { |
+ client(FLAGS_skp[0], FLAGS_data[0]); |
+ } |
+ |
+ if (!FLAGS_png.isEmpty()) { |
+ SkBitmap bitmap; |
+ bitmap.allocN32Pixels(1000, 1000); |
+ SkCanvas canvas(bitmap); |
+ canvas.clear(0xFFFFFFFF); |
+ |
+ server(FLAGS_data[0], FLAGS_control[0], &canvas); |
+ canvas.flush(); |
+ |
+ SkImageEncoder::EncodeFile(FLAGS_png[0], bitmap, SkImageEncoder::kPNG_Type, 100); |
+ SkDebugf("Wrote %s.\n", FLAGS_png[0]); |
+ } |
+ |
+ return 0; |
+} |