mtklein | d6043b2 | 2014-06-16 20:21:06 -0700 | [diff] [blame] | 1 | #include "nanomsg/src/nn.h" |
| 2 | #include "nanomsg/src/pipeline.h" |
| 3 | #include "nanomsg/src/reqrep.h" |
| 4 | |
| 5 | #include "SkCanvas.h" |
| 6 | #include "SkCommandLineFlags.h" |
| 7 | #include "SkData.h" |
| 8 | #include "SkForceLinking.h" |
| 9 | #include "SkGraphics.h" |
| 10 | #include "SkImageEncoder.h" |
| 11 | #include "SkOSFile.h" |
| 12 | #include "SkPicture.h" |
| 13 | #include "SkRandom.h" |
| 14 | #include "SkStream.h" |
| 15 | |
| 16 | __SK_FORCE_IMAGE_DECODER_LINKING; |
| 17 | |
| 18 | // To keep things simple, PictureHeader is fixed-size POD. |
| 19 | struct PictureHeader { |
| 20 | SkMatrix matrix; |
| 21 | SkRect clip; |
| 22 | SkXfermode::Mode xfermode; |
| 23 | pid_t pid; |
| 24 | uint8_t alpha; |
| 25 | |
| 26 | PictureHeader() |
| 27 | : matrix(SkMatrix::I()) |
| 28 | , clip(SkRect::MakeLargest()) |
| 29 | , xfermode(SkXfermode::kSrcOver_Mode) |
| 30 | , pid(getpid()) |
| 31 | , alpha(0xFF) {} |
| 32 | }; |
| 33 | |
| 34 | // A little adaptor: nn_iovec wants a non-const pointer for no obvious reason. |
| 35 | static struct nn_iovec create_iov(const void* ptr, size_t size) { |
| 36 | struct nn_iovec iov = { const_cast<void*>(ptr), size }; |
| 37 | return iov; |
| 38 | } |
| 39 | |
| 40 | static void send_picture(int socket, const PictureHeader& header, const SkData& skp) { |
| 41 | // Vectored IO lets us send header and skp contiguously without first |
| 42 | // copying them to a contiguous buffer. |
| 43 | struct nn_iovec iov[] = { |
| 44 | create_iov(&header, sizeof(header)), |
| 45 | create_iov(skp.data(), skp.size()), |
| 46 | }; |
| 47 | |
| 48 | struct nn_msghdr msg; |
| 49 | sk_bzero(&msg, sizeof(msg)); |
| 50 | msg.msg_iov = iov; |
| 51 | msg.msg_iovlen = SK_ARRAY_COUNT(iov); |
| 52 | |
| 53 | nn_sendmsg(socket, &msg, 0/*flags*/); |
| 54 | } |
| 55 | |
| 56 | static SkPicture* recv_picture(int socket, PictureHeader* header) { |
| 57 | static const size_t hSize = sizeof(*header); // It's easy to slip up and use sizeof(header). |
| 58 | |
| 59 | void* msg; |
| 60 | int size = nn_recv(socket, &msg, NN_MSG, 0/*flags*/); |
| 61 | SkDebugf("%d bytes", size); |
| 62 | |
| 63 | // msg is first a fixed-size header, then an .skp. |
| 64 | memcpy(header, msg, hSize); |
| 65 | SkMemoryStream stream((uint8_t*)msg + hSize, size - hSize); |
| 66 | SkPicture* pic = SkPicture::CreateFromStream(&stream); |
| 67 | |
| 68 | SkDebugf(" from proccess %d:", header->pid); |
| 69 | |
| 70 | nn_freemsg(msg); |
| 71 | return pic; |
| 72 | } |
| 73 | |
| 74 | static void client(const char* skpPath, const char* dataEndpoint) { |
| 75 | // Read the .skp. |
| 76 | SkAutoTUnref<const SkData> skp(SkData::NewFromFileName(skpPath)); |
| 77 | if (!skp) { |
| 78 | SkDebugf("Couldn't read %s\n", skpPath); |
| 79 | exit(1); |
| 80 | } |
| 81 | SkMemoryStream stream(skp->data(), skp->size()); |
| 82 | SkAutoTUnref<SkPicture> picture(SkPicture::CreateFromStream(&stream)); |
| 83 | |
| 84 | PictureHeader header; |
| 85 | SkRandom rand(picture->width() * picture->height()); |
| 86 | SkScalar r = rand.nextRangeScalar(0, picture->width()), |
| 87 | b = rand.nextRangeScalar(0, picture->height()), |
| 88 | l = rand.nextRangeScalar(0, r), |
| 89 | t = rand.nextRangeScalar(0, b); |
| 90 | header.clip.setLTRB(l,t,r,b); |
| 91 | header.matrix.setTranslate(-l, -t); |
| 92 | header.matrix.postRotate(rand.nextRangeScalar(-25, 25)); |
| 93 | header.alpha = 0x7F; |
| 94 | |
| 95 | //Clients use NN_REQ (request) type sockets. |
| 96 | int socket = nn_socket(AF_SP, NN_REQ); |
| 97 | |
| 98 | // Clients connect a socket to an endpoint. |
| 99 | nn_connect(socket, dataEndpoint); |
| 100 | |
| 101 | // Send the picture and its header. |
| 102 | SkDebugf("Sending %s (%d bytes)...", skpPath, skp->size()); |
| 103 | send_picture(socket, header, *skp); |
| 104 | |
| 105 | // Wait for ack. |
| 106 | uint8_t ack; |
| 107 | nn_recv(socket, &ack, sizeof(ack), 0/*flags*/); |
| 108 | SkDebugf(" ok.\n"); |
| 109 | } |
| 110 | |
| 111 | // Wait until socketA or socketB has something to tell us, and return which one. |
| 112 | static int poll_in(int socketA, int socketB) { |
| 113 | struct nn_pollfd polls[] = { |
| 114 | { socketA, NN_POLLIN, 0 }, |
| 115 | { socketB, NN_POLLIN, 0 }, |
| 116 | }; |
| 117 | |
| 118 | nn_poll(polls, SK_ARRAY_COUNT(polls), -1/*no timeout*/); |
| 119 | |
| 120 | if (polls[0].revents & NN_POLLIN) { return socketA; } |
| 121 | if (polls[1].revents & NN_POLLIN) { return socketB; } |
| 122 | |
| 123 | SkFAIL("unreachable"); |
| 124 | return 0; |
| 125 | } |
| 126 | |
| 127 | static void server(const char* dataEndpoint, const char* controlEndpoint, SkCanvas* canvas) { |
| 128 | // NN_REP sockets receive a request then make a reply. NN_PULL sockets just receive a request. |
| 129 | int data = nn_socket(AF_SP, NN_REP); |
| 130 | int control = nn_socket(AF_SP, NN_PULL); |
| 131 | |
| 132 | // Servers bind a socket to an endpoint. |
| 133 | nn_bind(data, dataEndpoint); |
| 134 | nn_bind(control, controlEndpoint); |
| 135 | |
| 136 | while (true) { |
| 137 | int ready = poll_in(data, control); |
| 138 | |
| 139 | // If we got any message on the control socket, we can stop. |
| 140 | if (ready == control) { |
| 141 | break; |
| 142 | } |
| 143 | |
| 144 | // We should have an .skp waiting for us on data socket. |
| 145 | PictureHeader header; |
| 146 | SkAutoTUnref<SkPicture> picture(recv_picture(data, &header)); |
| 147 | |
| 148 | SkPaint paint; |
| 149 | paint.setAlpha(header.alpha); |
| 150 | paint.setXfermodeMode(header.xfermode); |
| 151 | |
| 152 | canvas->saveLayer(NULL, &paint); |
| 153 | canvas->concat(header.matrix); |
| 154 | canvas->clipRect(header.clip); |
| 155 | picture->draw(canvas); |
| 156 | canvas->restore(); |
| 157 | SkDebugf(" drew"); |
| 158 | |
| 159 | // Send back an ack. |
| 160 | uint8_t ack = 42; |
| 161 | nn_send(data, &ack, sizeof(ack), 0/*flags*/); |
| 162 | SkDebugf(" and acked.\n"); |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | static void stop(const char* controlEndpoint) { |
| 167 | // An NN_PUSH socket can send messages but not receive them. |
| 168 | int control = nn_socket(AF_SP, NN_PUSH); |
| 169 | nn_connect(control, controlEndpoint); |
| 170 | |
| 171 | // Sending anything (including this 0-byte message) will tell server() to stop. |
| 172 | nn_send(control, NULL, 0, 0/*flags*/); |
| 173 | } |
| 174 | |
| 175 | DEFINE_string2(skp, r, "", ".skp to send (as client)"); |
| 176 | DEFINE_string2(png, w, "", ".png to write (as server)"); |
| 177 | DEFINE_bool(stop, false, "If true, tell server to stop and write its canvas out as a .png."); |
| 178 | DEFINE_string(data, "ipc://nanomsg-picture-data", "Endpoint for sending pictures."); |
| 179 | DEFINE_string(control, "ipc://nanomsg-picture-control", "Endpoint for control channel."); |
| 180 | |
| 181 | int main(int argc, char** argv) { |
| 182 | SkAutoGraphics ag; |
| 183 | SkCommandLineFlags::Parse(argc, argv); |
| 184 | |
| 185 | if (FLAGS_stop) { |
| 186 | stop(FLAGS_control[0]); |
| 187 | } |
| 188 | |
| 189 | if (!FLAGS_skp.isEmpty()) { |
| 190 | client(FLAGS_skp[0], FLAGS_data[0]); |
| 191 | } |
| 192 | |
| 193 | if (!FLAGS_png.isEmpty()) { |
| 194 | SkBitmap bitmap; |
| 195 | bitmap.allocN32Pixels(1000, 1000); |
| 196 | SkCanvas canvas(bitmap); |
| 197 | canvas.clear(0xFFFFFFFF); |
| 198 | |
| 199 | server(FLAGS_data[0], FLAGS_control[0], &canvas); |
| 200 | canvas.flush(); |
| 201 | |
| 202 | SkImageEncoder::EncodeFile(FLAGS_png[0], bitmap, SkImageEncoder::kPNG_Type, 100); |
| 203 | SkDebugf("Wrote %s.\n", FLAGS_png[0]); |
| 204 | } |
| 205 | |
| 206 | return 0; |
| 207 | } |