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