2011-08-08 15:12:05 +00:00
|
|
|
#include "SampleCode.h"
|
|
|
|
#include "SkView.h"
|
|
|
|
#include "SkCanvas.h"
|
|
|
|
#include "SkGPipe.h"
|
|
|
|
#include "SkSockets.h"
|
|
|
|
#include "SkNetPipeController.h"
|
|
|
|
#include "SkCornerPathEffect.h"
|
|
|
|
#include "SkColorPalette.h"
|
|
|
|
#include "SkOSMenu.h"
|
|
|
|
|
2011-08-12 14:27:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Drawing Client
|
|
|
|
*
|
2012-08-23 18:14:13 +00:00
|
|
|
* A drawing client that allows a user to perform simple brush stokes with
|
|
|
|
* a selected color and brush size. The drawing client communicates with a
|
2011-08-12 14:27:47 +00:00
|
|
|
* drawing server to send/receive data to/from other clients connected to the
|
|
|
|
* same server. The drawing client stores data in fData and fBuffer depending on
|
|
|
|
* the data type. Append type means that the drawing data is a completed stroke
|
2012-08-23 18:14:13 +00:00
|
|
|
* and Replace type means that the drawing data is in progress and will be
|
|
|
|
* replaced by subsequent data. fData and fBuffer are read by a pipe reader and
|
2011-08-12 14:27:47 +00:00
|
|
|
* reproduce the drawing. When the client is in a normal state, the data stored
|
|
|
|
* on the client and the server should be identical.
|
|
|
|
* The drawing client is also able to switch between vector and bitmap drawing.
|
|
|
|
* The drawing client also renders the latest drawing stroke locally in order to
|
2012-08-23 18:14:13 +00:00
|
|
|
* produce better reponses. This can be disabled by calling
|
|
|
|
* controller.disablePlayBack(), which will introduce a lag between the input
|
2011-08-12 14:27:47 +00:00
|
|
|
* and the drawing.
|
|
|
|
* Note: in order to keep up with the drawing data, the client will try to read
|
2012-08-23 18:14:13 +00:00
|
|
|
* a few times each frame in case more than one frame worth of data has been
|
2011-08-12 14:27:47 +00:00
|
|
|
* received and render them together. This behavior can be adjusted by tweaking
|
|
|
|
* MAX_READ_PER_FRAME or disabled by turning fSync to false
|
|
|
|
*/
|
|
|
|
|
2011-08-08 15:12:05 +00:00
|
|
|
#define MAX_READ_PER_FRAME 5
|
2011-08-12 14:27:47 +00:00
|
|
|
|
2011-08-08 15:12:05 +00:00
|
|
|
class DrawingClientView : public SampleView {
|
|
|
|
public:
|
2012-08-23 18:14:13 +00:00
|
|
|
DrawingClientView() {
|
2011-08-08 15:12:05 +00:00
|
|
|
fSocket = NULL;
|
|
|
|
fTotalBytesRead = 0;
|
|
|
|
fPalette = new SkColorPalette;
|
|
|
|
fPalette->setSize(100, 300);
|
|
|
|
fPalette->setVisibleP(true);
|
|
|
|
this->attachChildToFront(fPalette);
|
|
|
|
fPalette->unref();
|
2013-11-25 19:44:07 +00:00
|
|
|
fBrushSize = 2.5;
|
2011-08-08 15:12:05 +00:00
|
|
|
fAA = false;
|
|
|
|
fPaletteVisible = true;
|
2011-08-12 14:27:47 +00:00
|
|
|
fSync = true;
|
|
|
|
fVector = true;
|
2011-08-08 15:12:05 +00:00
|
|
|
}
|
|
|
|
~DrawingClientView() {
|
|
|
|
if (fSocket) {
|
|
|
|
delete fSocket;
|
|
|
|
}
|
|
|
|
fData.reset();
|
|
|
|
fBuffer.reset();
|
|
|
|
}
|
2012-08-23 18:14:13 +00:00
|
|
|
|
2011-08-08 15:12:05 +00:00
|
|
|
virtual void requestMenu(SkOSMenu* menu) {
|
|
|
|
menu->setTitle("Drawing Client");
|
2012-08-23 18:14:13 +00:00
|
|
|
menu->appendTextField("Server IP", "Server IP", this->getSinkID(),
|
2011-08-12 14:27:47 +00:00
|
|
|
"IP address or hostname");
|
2011-08-08 15:12:05 +00:00
|
|
|
menu->appendSwitch("Vector", "Vector", this->getSinkID(), fVector);
|
2012-08-23 18:14:13 +00:00
|
|
|
menu->appendSlider("Brush Size", "Brush Size", this->getSinkID(), 1.0,
|
2011-08-12 14:27:47 +00:00
|
|
|
100.0, fBrushSize);
|
2011-08-08 15:12:05 +00:00
|
|
|
menu->appendSwitch("Anti-Aliasing", "AA", this->getSinkID(), fAA);
|
2012-08-23 18:14:13 +00:00
|
|
|
menu->appendSwitch("Show Color Palette", "Palette", this->getSinkID(),
|
2011-08-12 14:27:47 +00:00
|
|
|
fPaletteVisible);
|
2011-08-08 15:12:05 +00:00
|
|
|
menu->appendSwitch("Sync", "Sync", this->getSinkID(), fSync);
|
|
|
|
menu->appendAction("Clear", this->getSinkID());
|
|
|
|
}
|
2012-08-23 18:14:13 +00:00
|
|
|
|
2011-08-08 15:12:05 +00:00
|
|
|
protected:
|
2012-08-23 18:14:13 +00:00
|
|
|
|
2011-08-08 15:12:05 +00:00
|
|
|
static void readData(int cid, const void* data, size_t size,
|
|
|
|
SkSocket::DataType type, void* context) {
|
|
|
|
DrawingClientView* view = (DrawingClientView*)context;
|
|
|
|
view->onRead(cid, data, size, type);
|
|
|
|
}
|
2012-08-23 18:14:13 +00:00
|
|
|
|
2011-08-08 15:12:05 +00:00
|
|
|
void onRead(int cid, const void* data, size_t size, SkSocket::DataType type) {
|
|
|
|
if (size > 0) {
|
|
|
|
fBuffer.reset();
|
|
|
|
if (type == SkSocket::kPipeReplace_type)
|
|
|
|
fBuffer.append(size, (const char*)data);
|
|
|
|
else if (type == SkSocket::kPipeAppend_type)
|
|
|
|
fData.append(size, (const char*)data);
|
|
|
|
else {
|
|
|
|
//other types of data
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-08-23 18:14:13 +00:00
|
|
|
|
2011-08-08 15:12:05 +00:00
|
|
|
bool onQuery(SkEvent* evt) {
|
|
|
|
if (SampleCode::TitleQ(*evt)) {
|
|
|
|
SampleCode::TitleR(evt, "Drawing Client");
|
|
|
|
return true;
|
|
|
|
}
|
2012-08-23 18:14:13 +00:00
|
|
|
|
2011-08-08 15:12:05 +00:00
|
|
|
return this->INHERITED::onQuery(evt);
|
|
|
|
}
|
2012-08-23 18:14:13 +00:00
|
|
|
|
2011-08-08 15:12:05 +00:00
|
|
|
bool onEvent(const SkEvent& evt) {;
|
2011-08-12 14:27:47 +00:00
|
|
|
if (SkOSMenu::FindSliderValue(evt, "Brush Size", &fBrushSize))
|
2011-08-08 15:12:05 +00:00
|
|
|
return true;
|
2012-08-23 18:14:13 +00:00
|
|
|
|
2011-08-08 15:12:05 +00:00
|
|
|
SkString s;
|
2011-08-12 14:27:47 +00:00
|
|
|
if (SkOSMenu::FindText(evt, "Server IP", &s)) {
|
2011-08-08 15:12:05 +00:00
|
|
|
if (NULL != fSocket) {
|
|
|
|
delete fSocket;
|
|
|
|
}
|
|
|
|
fSocket = new SkTCPClient(s.c_str(), 40000);
|
|
|
|
fSocket->connectToServer();
|
|
|
|
fSocket->suspendWrite();
|
|
|
|
SkDebugf("Connecting to %s\n", s.c_str());
|
|
|
|
fData.reset();
|
|
|
|
fBuffer.reset();
|
|
|
|
this->inval(NULL);
|
|
|
|
return true;
|
|
|
|
}
|
2011-08-12 14:27:47 +00:00
|
|
|
if (SkOSMenu::FindSwitchState(evt, "AA", &fAA) ||
|
|
|
|
SkOSMenu::FindSwitchState(evt, "Sync", &fSync))
|
2011-08-08 15:12:05 +00:00
|
|
|
return true;
|
2011-08-12 14:27:47 +00:00
|
|
|
if (SkOSMenu::FindSwitchState(evt, "Vector", &fVector)) {
|
2011-08-08 15:12:05 +00:00
|
|
|
this->clearBitmap();
|
|
|
|
return true;
|
|
|
|
}
|
2011-08-12 14:27:47 +00:00
|
|
|
if (SkOSMenu::FindAction(evt, "Clear")) {
|
2011-08-08 15:12:05 +00:00
|
|
|
this->clear();
|
|
|
|
return true;
|
|
|
|
}
|
2011-08-12 14:27:47 +00:00
|
|
|
if (SkOSMenu::FindSwitchState(evt, "Palette", &fPaletteVisible)) {
|
2011-08-08 15:12:05 +00:00
|
|
|
fPalette->setVisibleP(fPaletteVisible);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return this->INHERITED::onEvent(evt);
|
|
|
|
}
|
2012-08-23 18:14:13 +00:00
|
|
|
|
2011-08-08 15:12:05 +00:00
|
|
|
virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
|
|
|
|
return new Click(this);
|
|
|
|
}
|
2012-08-23 18:14:13 +00:00
|
|
|
|
2011-08-08 15:12:05 +00:00
|
|
|
virtual bool onClick(SkView::Click* click) {
|
|
|
|
switch (click->fState) {
|
|
|
|
case SkView::Click::kDown_State:
|
|
|
|
fCurrLine.moveTo(click->fCurr);
|
|
|
|
fType = SkSocket::kPipeReplace_type;
|
|
|
|
if (fSocket)
|
|
|
|
fSocket->resumeWrite();
|
|
|
|
break;
|
|
|
|
case SkView::Click::kMoved_State:
|
|
|
|
fCurrLine.lineTo(click->fCurr);
|
|
|
|
break;
|
|
|
|
case SkView::Click::kUp_State:
|
|
|
|
fType = SkSocket::kPipeAppend_type;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2012-08-23 18:14:13 +00:00
|
|
|
|
2011-08-08 15:12:05 +00:00
|
|
|
virtual void onDrawContent(SkCanvas* canvas) {
|
|
|
|
if (fSocket) {
|
|
|
|
if (fSocket->isConnected()) {
|
|
|
|
if (fSync) {
|
|
|
|
int count = 0;
|
|
|
|
while (fSocket->readPacket(readData, this) > 0 &&
|
2012-08-23 18:14:13 +00:00
|
|
|
count < MAX_READ_PER_FRAME)
|
2011-08-08 15:12:05 +00:00
|
|
|
++count;
|
|
|
|
}
|
2012-08-23 18:14:13 +00:00
|
|
|
else
|
2011-08-08 15:12:05 +00:00
|
|
|
fSocket->readPacket(readData, this);
|
|
|
|
}
|
2012-08-23 18:14:13 +00:00
|
|
|
else
|
2011-08-08 15:12:05 +00:00
|
|
|
fSocket->connectToServer();
|
|
|
|
}
|
|
|
|
size_t bytesRead = 0;
|
2011-08-12 14:27:47 +00:00
|
|
|
SkGPipeReader::Status status;
|
2011-08-08 15:12:05 +00:00
|
|
|
SkCanvas bufferCanvas(fBase);
|
|
|
|
SkCanvas* tempCanvas;
|
|
|
|
while (fTotalBytesRead < fData.count()) {
|
|
|
|
if (fVector)
|
|
|
|
tempCanvas = canvas;
|
|
|
|
else
|
|
|
|
tempCanvas = &bufferCanvas;
|
2012-08-23 18:14:13 +00:00
|
|
|
SkGPipeReader reader(tempCanvas);
|
2011-08-12 14:27:47 +00:00
|
|
|
status = reader.playback(fData.begin() + fTotalBytesRead,
|
|
|
|
fData.count() - fTotalBytesRead,
|
|
|
|
&bytesRead);
|
2011-08-08 15:12:05 +00:00
|
|
|
SkASSERT(SkGPipeReader::kError_Status != status);
|
|
|
|
fTotalBytesRead += bytesRead;
|
|
|
|
}
|
|
|
|
if (fVector)
|
|
|
|
fTotalBytesRead = 0;
|
|
|
|
else
|
|
|
|
canvas->drawBitmap(fBase, 0, 0, NULL);
|
2012-08-23 18:14:13 +00:00
|
|
|
|
2011-08-08 15:12:05 +00:00
|
|
|
size_t totalBytesRead = 0;
|
|
|
|
while (totalBytesRead < fBuffer.count()) {
|
|
|
|
SkGPipeReader reader(canvas);
|
2012-08-23 18:14:13 +00:00
|
|
|
status = reader.playback(fBuffer.begin() + totalBytesRead,
|
|
|
|
fBuffer.count() - totalBytesRead,
|
2011-08-12 14:27:47 +00:00
|
|
|
&bytesRead);
|
|
|
|
SkASSERT(SkGPipeReader::kError_Status != status);
|
2011-08-08 15:12:05 +00:00
|
|
|
totalBytesRead += bytesRead;
|
|
|
|
}
|
2012-08-23 18:14:13 +00:00
|
|
|
|
2011-08-08 15:12:05 +00:00
|
|
|
SkNetPipeController controller(canvas);
|
|
|
|
SkGPipeWriter writer;
|
2012-08-23 18:14:13 +00:00
|
|
|
SkCanvas* writerCanvas = writer.startRecording(&controller,
|
2011-08-08 15:12:05 +00:00
|
|
|
SkGPipeWriter::kCrossProcess_Flag);
|
2012-08-23 18:14:13 +00:00
|
|
|
|
2011-08-08 15:12:05 +00:00
|
|
|
//controller.disablePlayback();
|
|
|
|
SkPaint p;
|
|
|
|
p.setColor(fPalette->getColor());
|
|
|
|
p.setStyle(SkPaint::kStroke_Style);
|
|
|
|
p.setStrokeWidth(fBrushSize);
|
|
|
|
p.setStrokeCap(SkPaint::kRound_Cap);
|
|
|
|
p.setStrokeJoin(SkPaint::kRound_Join);
|
|
|
|
p.setAntiAlias(fAA);
|
|
|
|
p.setPathEffect(new SkCornerPathEffect(55))->unref();
|
|
|
|
writerCanvas->drawPath(fCurrLine, p);
|
|
|
|
writer.endRecording();
|
2012-08-23 18:14:13 +00:00
|
|
|
|
2011-08-08 15:12:05 +00:00
|
|
|
controller.writeToSocket(fSocket, fType);
|
|
|
|
if (fType == SkSocket::kPipeAppend_type && fSocket) {
|
|
|
|
fSocket->suspendWrite();
|
|
|
|
fCurrLine.reset();
|
|
|
|
}
|
2012-08-23 18:14:13 +00:00
|
|
|
|
2011-08-08 15:12:05 +00:00
|
|
|
this->inval(NULL);
|
|
|
|
}
|
2012-08-23 18:14:13 +00:00
|
|
|
|
2011-08-08 15:12:05 +00:00
|
|
|
virtual void onSizeChange() {
|
|
|
|
this->INHERITED::onSizeChange();
|
|
|
|
fPalette->setLoc(this->width()-100, 0);
|
|
|
|
fBase.setConfig(SkBitmap::kARGB_8888_Config, this->width(), this->height());
|
|
|
|
fBase.allocPixels(NULL);
|
|
|
|
this->clearBitmap();
|
|
|
|
}
|
2012-08-23 18:14:13 +00:00
|
|
|
|
2011-08-08 15:12:05 +00:00
|
|
|
private:
|
|
|
|
void clear() {
|
|
|
|
fData.reset();
|
|
|
|
fBuffer.reset();
|
|
|
|
fCurrLine.reset();
|
|
|
|
fTotalBytesRead = 0;
|
|
|
|
this->clearBitmap();
|
|
|
|
}
|
|
|
|
void clearBitmap() {
|
|
|
|
fTotalBytesRead = 0;
|
|
|
|
fBase.eraseColor(fBGColor);
|
|
|
|
}
|
|
|
|
SkTDArray<char> fData;
|
|
|
|
SkTDArray<char> fBuffer;
|
|
|
|
SkBitmap fBase;
|
|
|
|
SkPath fCurrLine;
|
|
|
|
SkTCPClient* fSocket;
|
|
|
|
SkSocket::DataType fType;
|
|
|
|
SkColorPalette* fPalette;
|
|
|
|
bool fPaletteVisible;
|
|
|
|
size_t fTotalBytesRead;
|
|
|
|
SkScalar fBrushSize;
|
|
|
|
bool fAA;
|
|
|
|
bool fSync;
|
|
|
|
bool fVector;
|
2012-08-23 18:14:13 +00:00
|
|
|
|
2011-08-08 15:12:05 +00:00
|
|
|
typedef SampleView INHERITED;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static SkView* MyFactory() { return new DrawingClientView; }
|
|
|
|
static SkViewRegister reg(MyFactory);
|