1 /*
2 * Copyright 2024 Google LLC
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 "include/core/SkCanvas.h"
9 #include "include/core/SkRRect.h"
10 #include "include/core/SkRect.h"
11 #include "include/core/SkStream.h"
12 #include "include/core/SkSurface.h"
13 #include "include/gpu/ganesh/GrDirectContext.h"
14 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
15 #include "include/gpu/ganesh/gl/GrGLDirectContext.h"
16 #include "include/gpu/ganesh/gl/GrGLInterface.h"
17 #include "include/encode/SkWebpEncoder.h"
18
19 #if defined(__linux__)
20 #include "include/gpu/ganesh/gl/glx/GrGLMakeGLXInterface.h"
21
22 #include <X11/Xlib.h>
23 #include <GL/glx.h>
24 #include <GL/gl.h>
25 #endif
26
27 #if defined(__APPLE__) && TARGET_OS_MAC == 1
28 #include "include/gpu/ganesh/gl/mac/GrGLMakeMacInterface.h"
29
30 #include "gl_context_helper.h"
31 #endif
32
33 #if defined(_MSC_VER)
34 #include <windows.h>
35 #include "include/gpu/ganesh/gl/win/GrGLMakeWinInterface.h"
36 #endif
37
38 #include <cstdio>
39
40 #if defined(__linux__)
41
42 // Set up an X Display that can be rendered to GL. This will not be visible while
43 // the program runs. It is cribbed from how Skia's tooling sets itself up (e.g. viewer).
initialize_gl_linux()44 bool initialize_gl_linux() {
45 Display* display = XOpenDisplay(nullptr);
46 if (!display) {
47 printf("Could not open an X display\n");
48 return false;
49 }
50 static int constexpr kChooseFBConfigAtt[] = {
51 GLX_RENDER_TYPE, GLX_RGBA_BIT,
52 GLX_DOUBLEBUFFER, True,
53 GLX_STENCIL_SIZE, 8,
54 None
55 };
56 int n;
57 GLXFBConfig* fbConfig = glXChooseFBConfig(display, DefaultScreen(display), kChooseFBConfigAtt, &n);
58 XVisualInfo* visualInfo;
59 if (n > 0) {
60 visualInfo = glXGetVisualFromFBConfig(display, *fbConfig);
61 } else {
62 // For some reason glXChooseVisual takes a non-const pointer to the attributes.
63 int chooseVisualAtt[] = {
64 GLX_RGBA,
65 GLX_DOUBLEBUFFER,
66 GLX_STENCIL_SIZE, 8,
67 None
68 };
69 visualInfo = glXChooseVisual(display, DefaultScreen(display), chooseVisualAtt);
70 }
71 if (!visualInfo) {
72 printf("Could not get X visualInfo\n");
73 return false;
74 }
75 GLXContext glContext = glXCreateContext(display, visualInfo, nullptr, GL_TRUE);
76 if (!glContext) {
77 printf("Could not make GL X context\n");
78 return false;
79 }
80 Colormap colorMap = XCreateColormap(display,
81 RootWindow(display, visualInfo->screen),
82 visualInfo->visual,
83 AllocNone);
84 XSetWindowAttributes swa;
85 swa.colormap = colorMap;
86 swa.event_mask = 0;
87 Window window = XCreateWindow(display,
88 RootWindow(display, visualInfo->screen),
89 0, 0, // x, y
90 1280, 960, // width, height
91 0, // border width
92 visualInfo->depth,
93 InputOutput,
94 visualInfo->visual,
95 CWEventMask | CWColormap,
96 &swa);
97
98 if (!glXMakeCurrent(display, window, glContext)) {
99 printf("Could not set GL X context to be the created one\n");
100 return false;
101 }
102 return true;
103 }
104 #endif // defined(__linux__)
105
106 #if defined(_MSC_VER)
107
108 // This was mostly taken from
109 // https://skia.googlesource.com/skia/+/f725a5ba8a29ec7c271805624d54755ce34968a1/tools/gpu/gl/win/CreatePlatformGLTestContext_win.cpp#57
initialize_gl_win()110 bool initialize_gl_win() {
111 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(nullptr);
112 WNDCLASS wc;
113 wc.cbClsExtra = 0;
114 wc.cbWndExtra = 0;
115 wc.hbrBackground = nullptr;
116 wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
117 wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
118 wc.hInstance = hInstance;
119 wc.lpfnWndProc = (WNDPROC) DefWindowProc;
120 wc.lpszClassName = TEXT("external_client");
121 wc.lpszMenuName = nullptr;
122 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
123
124 ATOM cls = RegisterClass(&wc);
125 if (!cls) {
126 printf("Could not register window class.\n");
127 return false;
128 }
129
130 HWND window;
131 if (!(window = CreateWindow(TEXT("external_client"),
132 TEXT("my-window"),
133 CS_OWNDC,
134 0, 0, 1, 1,
135 nullptr, nullptr,
136 hInstance, nullptr))) {
137 printf("Could not create window.\n");
138 return false;
139 }
140
141 HDC deviceContext;
142 if (!(deviceContext = GetDC(window))) {
143 printf("Could not get device context.\n");
144 return false;
145 }
146
147 // Taken from https://www.khronos.org/opengl/wiki/Creating_an_OpenGL_Context_(WGL)
148 PIXELFORMATDESCRIPTOR pfd = {
149 sizeof(PIXELFORMATDESCRIPTOR),
150 1,
151 PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, // Flags
152 PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette.
153 32, // Colordepth of the framebuffer.
154 0, 0, 0, 0, 0, 0,
155 0,
156 0,
157 0,
158 0, 0, 0, 0,
159 24, // Number of bits for the depthbuffer
160 8, // Number of bits for the stencilbuffer
161 0, // Number of Aux buffers in the framebuffer.
162 PFD_MAIN_PLANE,
163 0,
164 0, 0, 0
165 };
166 int pixelFormat = ChoosePixelFormat(deviceContext, &pfd);
167 SetPixelFormat(deviceContext, pixelFormat, &pfd);
168
169 HGLRC glrc;
170 if (!(glrc = wglCreateContext(deviceContext))) {
171 printf("Could not create rendering context.\n");
172 return false;
173 }
174
175 if (!(wglMakeCurrent(deviceContext, glrc))) {
176 printf("Could not set the context.\n");
177 return false;
178 }
179 return true;
180 }
181 #endif // defined(_MSC_VER)
182
main(int argc,char ** argv)183 int main(int argc, char** argv) {
184 if (argc != 2) {
185 printf("Usage: %s <name.webp>\n", argv[0]);
186 return 1;
187 }
188
189 SkFILEWStream output(argv[1]);
190 if (!output.isValid()) {
191 printf("Cannot open output file %s\n", argv[1]);
192 return 1;
193 }
194
195 GrContextOptions opts;
196 opts.fSuppressPrints = true;
197 #if defined(__linux__)
198 if (!initialize_gl_linux()) {
199 return 1;
200 }
201 sk_sp<const GrGLInterface> iface = GrGLInterfaces::MakeGLX();
202 #elif defined(__APPLE__) && TARGET_OS_MAC == 1
203 if (!initialize_gl_mac()) {
204 return 1;
205 }
206 sk_sp<const GrGLInterface> iface = GrGLInterfaces::MakeMac();
207 #elif defined(_MSC_VER)
208 if (!initialize_gl_win()) {
209 return 1;
210 }
211 sk_sp<const GrGLInterface> iface = GrGLInterfaces::MakeWin();
212 #endif
213 if (!iface) {
214 printf("Could not make GL interface\n");
215 return 1;
216 }
217
218 sk_sp<GrDirectContext> ctx = GrDirectContexts::MakeGL(iface, opts);
219 if (!ctx) {
220 printf("Could not make GrDirectContext\n");
221 return 1;
222 }
223 printf("Context made, now to make the surface\n");
224
225 SkImageInfo imageInfo =
226 SkImageInfo::Make(200, 400, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
227
228 sk_sp<SkSurface> surface =
229 SkSurfaces::RenderTarget(ctx.get(), skgpu::Budgeted::kYes, imageInfo);
230 if (!surface) {
231 printf("Could not make surface from GL DirectContext\n");
232 return 1;
233 }
234
235 SkCanvas* canvas = surface->getCanvas();
236 canvas->clear(SK_ColorRED);
237 SkRRect rrect = SkRRect::MakeRectXY(SkRect::MakeLTRB(10, 20, 50, 70), 10, 10);
238
239 SkPaint paint;
240 paint.setColor(SK_ColorBLUE);
241 paint.setAntiAlias(true);
242
243 canvas->drawRRect(rrect, paint);
244
245 ctx->flush();
246
247 printf("Drew to surface, now doing readback\n");
248 sk_sp<SkImage> img = surface->makeImageSnapshot();
249 sk_sp<SkData> webp = SkWebpEncoder::Encode(ctx.get(), img.get(), {});
250 if (!webp) {
251 printf("Readback of pixels (or encoding) failed\n");
252 return 1;
253 }
254 output.write(webp->data(), webp->size());
255 output.fsync();
256
257 return 0;
258 }
259