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#include <stdlib.h>
9#include <stdio.h>
10#include "GLFW/glfw3.h"
11#include "GrBackendSurface.h"
12#include "GrContext.h"
13#include "SkCanvas.h"
14#include "SkImage.h"
15#include "SkRSXform.h"
16#include "SkSurface.h"
17#include "Timer.h"
18
19GrContext* sContext = nullptr;
20SkSurface* sSurface = nullptr;
21
22static void error_callback(int error, const char* description) {
23    fputs(description, stderr);
24}
25
26static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
27    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
28        glfwSetWindowShouldClose(window, GL_TRUE);
29}
30
31
32static void init_skia(int w, int h) {
33    sContext = GrContext::MakeGL(nullptr).release();
34
35    GrGLFramebufferInfo framebufferInfo;
36    framebufferInfo.fFBOID = 0;  // assume default framebuffer
37    // We are always using OpenGL and we use RGBA8 internal format for both RGBA and BGRA configs in
38    // OpenGL.
39    framebufferInfo.fFormat = GR_GL_RGBA8;
40    SkColorType colorType;
41    if (kRGBA_8888_GrPixelConfig == kSkia8888_GrPixelConfig) {
42        colorType = kRGBA_8888_SkColorType;
43    } else {
44        colorType = kBGRA_8888_SkColorType;
45    }
46    GrBackendRenderTarget backendRenderTarget(w, h,
47                                              0, // sample count
48                                              0, // stencil bits
49                                              framebufferInfo);
50
51    sSurface = SkSurface::MakeFromBackendRenderTarget(sContext, backendRenderTarget,
52                                                      kBottomLeft_GrSurfaceOrigin, colortype,
53                                                      nullptr, nullptr).release();
54}
55
56static void cleanup_skia() {
57    delete sSurface;
58    delete sContext;
59}
60
61const int kGrid = 100;
62const int kWidth = 960;
63const int kHeight = 640;
64
65int main(void) {
66    GLFWwindow* window;
67    glfwSetErrorCallback(error_callback);
68    if (!glfwInit()) {
69        exit(EXIT_FAILURE);
70    }
71
72    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
73    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
74    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
75    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
76    glfwWindowHint(GLFW_SRGB_CAPABLE, GL_TRUE);
77
78    window = glfwCreateWindow(kWidth, kHeight, "Simple example", NULL, NULL);
79    if (!window) {
80        glfwTerminate();
81        exit(EXIT_FAILURE);
82    }
83    glfwMakeContextCurrent(window);
84
85    init_skia(kWidth, kHeight);
86
87    sk_sp<SkImage> atlas;
88    SkRSXform   xform[kGrid*kGrid+1];
89    SkRect      tex[kGrid*kGrid+1];
90    WallTimer   timer;
91    float       times[32];
92    int         currentTime;
93
94    sk_sp<SkData> imageData(SkData::MakeFromFileName("images/ship.png"));
95    atlas.reset(SkImage::NewFromEncoded(imageData.get()));
96    if (!atlas) {
97        SkDebugf("\nCould not decode file ship.png\n");
98
99        cleanup_skia();
100        glfwDestroyWindow(window);
101        glfwTerminate();
102        exit(EXIT_FAILURE);
103    }
104
105    SkScalar anchorX = atlas->width()*0.5f;
106    SkScalar anchorY = atlas->height()*0.5f;
107    int currIndex = 0;
108    for (int x = 0; x < kGrid; x++) {
109        for (int y = 0; y < kGrid; y++) {
110            float xPos = (x / (kGrid - 1.0)) * kWidth;
111            float yPos = (y / (kGrid - 1.0)) * kWidth;
112
113            tex[currIndex] = SkRect::MakeLTRB(0.0f, 0.0f, atlas->width(), atlas->height());
114            xform[currIndex] = SkRSXform::MakeFromRadians(2.0f, SK_ScalarPI*0.5f,
115                                                          xPos, yPos, anchorX, anchorY);
116            currIndex++;
117        }
118    }
119    tex[currIndex] = SkRect::MakeLTRB(0.0f, 0.0f, atlas->width(), atlas->height());
120    xform[currIndex] = SkRSXform::MakeFromRadians(2.0f, SK_ScalarPI*0.5f,
121                                                  kWidth*0.5f, kHeight*0.5f, anchorX, anchorY);
122
123    currentTime = 0;
124
125    glfwSwapInterval(1);
126    glfwSetKeyCallback(window, key_callback);
127
128    // Draw to the surface via its SkCanvas.
129    SkCanvas* canvas = sSurface->getCanvas();   // We don't manage this pointer's lifetime.
130    SkPaint paint;
131    paint.setFilterQuality(kLow_SkFilterQuality);
132    paint.setColor(SK_ColorWHITE);
133    paint.setTextSize(15.0f);
134
135    while (!glfwWindowShouldClose(window)) {
136        const float kCosDiff = 0.99984769515f;
137        const float kSinDiff = 0.01745240643f;
138
139        timer.start();
140
141        glfwPollEvents();
142
143        float meanTime = 0.0f;
144        for (int i = 0; i < 32; ++i) {
145            meanTime += times[i];
146        }
147        meanTime /= 32.f;
148        char outString[64];
149        float fps = 1000.f/meanTime;
150        sprintf(outString, "fps: %f ms: %f", fps, meanTime);
151
152        for (int i = 0; i < kGrid*kGrid+1; ++i) {
153            SkScalar c = xform[i].fSCos;
154            SkScalar s = xform[i].fSSin;
155
156            SkScalar dx = c*anchorX - s*anchorY;
157            SkScalar dy = s*anchorX + c*anchorY;
158
159            xform[i].fSCos = kCosDiff*c - kSinDiff*s;
160            xform[i].fSSin = kSinDiff*c + kCosDiff*s;
161
162            dx -= xform[i].fSCos*anchorX - xform[i].fSSin*anchorY;
163            dy -= xform[i].fSSin*anchorX + xform[i].fSCos*anchorY;
164            xform[i].fTx += dx;
165            xform[i].fTy += dy;
166        }
167
168        canvas->clear(SK_ColorBLACK);
169        canvas->drawAtlas(atlas, xform, tex, nullptr, kGrid*kGrid+1, SkXfermode::kSrcOver_Mode,
170                          nullptr, &paint);
171        canvas->drawText(outString, strlen(outString), 100.f, 100.f, paint);
172
173        canvas->flush();
174
175        timer.end();
176        times[currentTime] = (float)(timer.fWall);
177        currentTime = (currentTime + 1) & 0x1f;
178
179        glfwSwapBuffers(window);
180    }
181
182    cleanup_skia();
183
184    glfwDestroyWindow(window);
185    glfwTerminate();
186    exit(EXIT_SUCCESS);
187}
188