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