#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->cullRect().width() * picture->cullRect().height()); SkScalar r = rand.nextRangeScalar(0, picture->cullRect().width()), b = rand.nextRangeScalar(0, picture->cullRect().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->playback(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; }