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