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