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.
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;
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.
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);
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
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}
208