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