blob: 5efa03c72c28b4fdd7290cdeff56488c9acdba09 [file] [log] [blame]
mtkleind6043b22014-06-16 20:21:06 -07001#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.
19struct 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.
35static 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
40static 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
56static 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
74static 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;
robertphillipsa8d7f0b2014-08-29 08:03:56 -070085 SkRandom rand(picture->cullRect().width() * picture->cullRect().height());
86 SkScalar r = rand.nextRangeScalar(0, picture->cullRect().width()),
87 b = rand.nextRangeScalar(0, picture->cullRect().height()),
mtkleind6043b22014-06-16 20:21:06 -070088 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.
112static 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
127static 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);
robertphillipsc5ba71d2014-09-04 08:42:50 -0700155 picture->playback(canvas);
mtkleind6043b22014-06-16 20:21:06 -0700156 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
166static 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
175DEFINE_string2(skp, r, "", ".skp to send (as client)");
176DEFINE_string2(png, w, "", ".png to write (as server)");
177DEFINE_bool(stop, false, "If true, tell server to stop and write its canvas out as a .png.");
178DEFINE_string(data, "ipc://nanomsg-picture-data", "Endpoint for sending pictures.");
179DEFINE_string(control, "ipc://nanomsg-picture-control", "Endpoint for control channel.");
180
181int 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}