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
PictureHeaderPictureHeader26 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.
create_iov(const void * ptr,size_t size)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
send_picture(int socket,const PictureHeader & header,const SkData & skp)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
recv_picture(int socket,PictureHeader * header)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
client(const char * skpPath,const char * dataEndpoint)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->cullRect().width() * picture->cullRect().height());
86 SkScalar r = rand.nextRangeScalar(0, picture->cullRect().width()),
87 b = rand.nextRangeScalar(0, picture->cullRect().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.
poll_in(int socketA,int socketB)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
server(const char * dataEndpoint,const char * controlEndpoint,SkCanvas * canvas)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->playback(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
stop(const char * controlEndpoint)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
main(int argc,char ** argv)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 }
208