1 /*
2  * Copyright 2015 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 
9 #include "include/gpu/GrBackendSurface.h"
10 #include "include/gpu/GrDirectContext.h"
11 #include "SDL.h"
12 #include "include/core/SkCanvas.h"
13 #include "include/core/SkFont.h"
14 #include "include/core/SkSurface.h"
15 #include "include/utils/SkRandom.h"
16 
17 #include "include/gpu/gl/GrGLInterface.h"
18 #include "src/gpu/gl/GrGLUtil.h"
19 
20 #if defined(SK_BUILD_FOR_ANDROID)
21 #include <GLES/gl.h>
22 #elif defined(SK_BUILD_FOR_UNIX)
23 #include <GL/gl.h>
24 #elif defined(SK_BUILD_FOR_MAC)
25 #include <OpenGL/gl.h>
26 #elif defined(SK_BUILD_FOR_IOS)
27 #include <OpenGLES/ES2/gl.h>
28 #endif
29 
30 /*
31  * This application is a simple example of how to combine SDL and Skia it demonstrates:
32  *   how to setup gpu rendering to the main window
33  *   how to perform cpu-side rendering and draw the result to the gpu-backed screen
34  *   draw simple primitives (rectangles)
35  *   draw more complex primitives (star)
36  */
37 
38 struct ApplicationState {
ApplicationStateApplicationState39     ApplicationState() : fQuit(false) {}
40     // Storage for the user created rectangles. The last one may still be being edited.
41     SkTArray<SkRect> fRects;
42     bool fQuit;
43 };
44 
handle_error()45 static void handle_error() {
46     const char* error = SDL_GetError();
47     SkDebugf("SDL Error: %s\n", error);
48     SDL_ClearError();
49 }
50 
handle_events(ApplicationState * state,SkCanvas * canvas)51 static void handle_events(ApplicationState* state, SkCanvas* canvas) {
52     SDL_Event event;
53     while(SDL_PollEvent(&event)) {
54         switch (event.type) {
55             case SDL_MOUSEMOTION:
56                 if (event.motion.state == SDL_PRESSED) {
57                     SkRect& rect = state->fRects.back();
58                     rect.fRight = event.motion.x;
59                     rect.fBottom = event.motion.y;
60                 }
61                 break;
62             case SDL_MOUSEBUTTONDOWN:
63                 if (event.button.state == SDL_PRESSED) {
64                     state->fRects.push_back() = SkRect::MakeLTRB(SkIntToScalar(event.button.x),
65                                                                  SkIntToScalar(event.button.y),
66                                                                  SkIntToScalar(event.button.x),
67                                                                  SkIntToScalar(event.button.y));
68                 }
69                 break;
70             case SDL_KEYDOWN: {
71                 SDL_Keycode key = event.key.keysym.sym;
72                 if (key == SDLK_ESCAPE) {
73                     state->fQuit = true;
74                 }
75                 break;
76             }
77             case SDL_QUIT:
78                 state->fQuit = true;
79                 break;
80             default:
81                 break;
82         }
83     }
84 }
85 
86 // Creates a star type shape using a SkPath
create_star()87 static SkPath create_star() {
88     static const int kNumPoints = 5;
89     SkPath concavePath;
90     SkPoint points[kNumPoints] = {{0, SkIntToScalar(-50)} };
91     SkMatrix rot;
92     rot.setRotate(SkIntToScalar(360) / kNumPoints);
93     for (int i = 1; i < kNumPoints; ++i) {
94         rot.mapPoints(points + i, points + i - 1, 1);
95     }
96     concavePath.moveTo(points[0]);
97     for (int i = 0; i < kNumPoints; ++i) {
98         concavePath.lineTo(points[(2 * i) % kNumPoints]);
99     }
100     concavePath.setFillType(SkPathFillType::kEvenOdd);
101     SkASSERT(!concavePath.isConvex());
102     concavePath.close();
103     return concavePath;
104 }
105 
106 #if defined(SK_BUILD_FOR_ANDROID)
SDL_main(int argc,char ** argv)107 int SDL_main(int argc, char** argv) {
108 #else
109 int main(int argc, char** argv) {
110 #endif
111     uint32_t windowFlags = 0;
112 
113     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
114     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
115 
116     SDL_GLContext glContext = nullptr;
117 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_IOS)
118     // For Android/iOS we need to set up for OpenGL ES and we make the window hi res & full screen
119     SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
120     windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE |
121                   SDL_WINDOW_BORDERLESS | SDL_WINDOW_FULLSCREEN_DESKTOP |
122                   SDL_WINDOW_ALLOW_HIGHDPI;
123 #else
124     // For all other clients we use the core profile and operate in a window
125     SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
126 
127     windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE;
128 #endif
129     static const int kStencilBits = 8;  // Skia needs 8 stencil bits
130     SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
131     SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
132     SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
133     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
134     SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
135     SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, kStencilBits);
136 
137     SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
138 
139     // If you want multisampling, uncomment the below lines and set a sample count
140     static const int kMsaaSampleCount = 0; //4;
141     // SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
142     // SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, kMsaaSampleCount);
143 
144     /*
145      * In a real application you might want to initialize more subsystems
146      */
147     if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
148         handle_error();
149         return 1;
150     }
151 
152     // Setup window
153     // This code will create a window with the same resolution as the user's desktop.
154     SDL_DisplayMode dm;
155     if (SDL_GetDesktopDisplayMode(0, &dm) != 0) {
156         handle_error();
157         return 1;
158     }
159 
160     SDL_Window* window = SDL_CreateWindow("SDL Window", SDL_WINDOWPOS_CENTERED,
161                                           SDL_WINDOWPOS_CENTERED, dm.w, dm.h, windowFlags);
162 
163     if (!window) {
164         handle_error();
165         return 1;
166     }
167 
168     // To go fullscreen
169     // SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN);
170 
171     // try and setup a GL context
172     glContext = SDL_GL_CreateContext(window);
173     if (!glContext) {
174         handle_error();
175         return 1;
176     }
177 
178     int success =  SDL_GL_MakeCurrent(window, glContext);
179     if (success != 0) {
180         handle_error();
181         return success;
182     }
183 
184     uint32_t windowFormat = SDL_GetWindowPixelFormat(window);
185     int contextType;
186     SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &contextType);
187 
188 
189     int dw, dh;
190     SDL_GL_GetDrawableSize(window, &dw, &dh);
191 
192     glViewport(0, 0, dw, dh);
193     glClearColor(1, 1, 1, 1);
194     glClearStencil(0);
195     glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
196 
197     // setup GrContext
198     auto interface = GrGLMakeNativeInterface();
199 
200     // setup contexts
201     sk_sp<GrDirectContext> grContext(GrDirectContext::MakeGL(interface));
202     SkASSERT(grContext);
203 
204     // Wrap the frame buffer object attached to the screen in a Skia render target so Skia can
205     // render to it
206     GrGLint buffer;
207     GR_GL_GetIntegerv(interface.get(), GR_GL_FRAMEBUFFER_BINDING, &buffer);
208     GrGLFramebufferInfo info;
209     info.fFBOID = (GrGLuint) buffer;
210     SkColorType colorType;
211 
212     //SkDebugf("%s", SDL_GetPixelFormatName(windowFormat));
213     // TODO: the windowFormat is never any of these?
214     if (SDL_PIXELFORMAT_RGBA8888 == windowFormat) {
215         info.fFormat = GR_GL_RGBA8;
216         colorType = kRGBA_8888_SkColorType;
217     } else {
218         colorType = kBGRA_8888_SkColorType;
219         if (SDL_GL_CONTEXT_PROFILE_ES == contextType) {
220             info.fFormat = GR_GL_BGRA8;
221         } else {
222             // We assume the internal format is RGBA8 on desktop GL
223             info.fFormat = GR_GL_RGBA8;
224         }
225     }
226 
227     GrBackendRenderTarget target(dw, dh, kMsaaSampleCount, kStencilBits, info);
228 
229     // setup SkSurface
230     // To use distance field text, use commented out SkSurfaceProps instead
231     // SkSurfaceProps props(SkSurfaceProps::kUseDeviceIndependentFonts_Flag,
232     //                      SkSurfaceProps::kUnknown_SkPixelGeometry);
233     SkSurfaceProps props;
234 
235     sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(grContext.get(), target,
236                                                                     kBottomLeft_GrSurfaceOrigin,
237                                                                     colorType, nullptr, &props));
238 
239     SkCanvas* canvas = surface->getCanvas();
240     canvas->scale((float)dw/dm.w, (float)dh/dm.h);
241 
242     ApplicationState state;
243 
244     const char* helpMessage = "Click and drag to create rects.  Press esc to quit.";
245 
246     SkPaint paint;
247 
248     // create a surface for CPU rasterization
249     sk_sp<SkSurface> cpuSurface(SkSurface::MakeRaster(canvas->imageInfo()));
250 
251     SkCanvas* offscreen = cpuSurface->getCanvas();
252     offscreen->save();
253     offscreen->translate(50.0f, 50.0f);
254     offscreen->drawPath(create_star(), paint);
255     offscreen->restore();
256 
257     sk_sp<SkImage> image = cpuSurface->makeImageSnapshot();
258 
259     int rotation = 0;
260     SkFont font;
261     while (!state.fQuit) { // Our application loop
262         SkRandom rand;
263         canvas->clear(SK_ColorWHITE);
264         handle_events(&state, canvas);
265 
266         paint.setColor(SK_ColorBLACK);
267         canvas->drawString(helpMessage, 100.0f, 100.0f, font, paint);
268         for (int i = 0; i < state.fRects.count(); i++) {
269             paint.setColor(rand.nextU() | 0x44808080);
270             canvas->drawRect(state.fRects[i], paint);
271         }
272 
273         // draw offscreen canvas
274         canvas->save();
275         canvas->translate(dm.w / 2.0, dm.h / 2.0);
276         canvas->rotate(rotation++);
277         canvas->drawImage(image, -50.0f, -50.0f);
278         canvas->restore();
279 
280         canvas->flush();
281         SDL_GL_SwapWindow(window);
282     }
283 
284     if (glContext) {
285         SDL_GL_DeleteContext(glContext);
286     }
287 
288     //Destroy window
289     SDL_DestroyWindow(window);
290 
291     //Quit SDL subsystems
292     SDL_Quit();
293     return 0;
294 }
295