xref: /aosp_15_r20/external/cronet/third_party/quic_trace/src/tools/render/text.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2018 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "tools/render/text.h"
16 
17 #include "absl/strings/str_cat.h"
18 #include "tools/render/font_data.h"
19 
20 namespace quic_trace {
21 namespace render {
22 
23 namespace {
24 // The following shader renders the text as specified in window coordinates and
25 // assigns appropriate texture
26 const char* kVertexShader = R"(
27 uniform float texture_w;
28 uniform float texture_h;
29 
30 in vec2 coord;
31 out vec2 texcoord;
32 void main(void) {
33   gl_Position = windowToGl(coord);
34 
35   // Textures the squares based on the following assumed ordering of vertices:
36   //   0  1
37   //   2  3
38   texcoord = vec2((gl_VertexID % 2) * texture_w, (gl_VertexID % 4) < 2 ? 0 : texture_h);
39 }
40 )";
41 
42 const char* kFragmentShader = R"(
43 in vec2 texcoord;
44 out vec4 color;
45 
46 uniform sampler2D tex;
47 
48 void main(void) {
49   color = texture(tex, texcoord);
50 }
51 )";
52 }  // namespace
53 
TextRenderer(const ProgramState * state)54 TextRenderer::TextRenderer(const ProgramState* state)
55     : state_(state), shader_(kVertexShader, kFragmentShader) {
56   CHECK_EQ(TTF_Init(), 0) << "Failed to initialize SDL_ttf: " << TTF_GetError();
57 
58   SDL_RWops* font_data =
59       SDL_RWFromConstMem(::kDroidSansBlob.data(), ::kDroidSansBlob.size());
60   font_ = TTF_OpenFontRW(font_data, /*freesrc=*/1, state->ScaleForDpi(14));
61   CHECK(font_ != nullptr) << "Failed to load regular font";
62 }
63 
~TextRenderer()64 TextRenderer::~TextRenderer() {
65   if (font_ != nullptr) {
66     TTF_CloseFont(font_);
67   }
68 
69   TTF_Quit();
70 }
71 
NearestPowerOfTwo(size_t x)72 static size_t NearestPowerOfTwo(size_t x) {
73   size_t result = 1;
74   while (result < x) {
75     result *= 2;
76   }
77   return result;
78 }
79 
AddText(std::shared_ptr<const Text> text,int x,int y)80 void TextRenderer::AddText(std::shared_ptr<const Text> text, int x, int y) {
81   texts_to_draw_.push_back(TextToDraw{
82       .text = std::move(text),
83       .x = x,
84       .y = y,
85   });
86 }
87 
DrawAll()88 void TextRenderer::DrawAll() {
89   // Common setup for drawing text.
90   glUseProgram(*shader_);
91   state_->Bind(shader_);
92 
93   for (const TextToDraw& text_to_draw : texts_to_draw_) {
94     text_to_draw.text->Draw(text_to_draw.x, text_to_draw.y);
95   }
96   texts_to_draw_.clear();
97 
98   // Clean up the cache.
99   for (auto it = cache_.begin(); it != cache_.end();) {
100     if (it->second.live) {
101       it->second.live = false;
102       it++;
103     } else {
104       cache_.erase(it++);
105     }
106   }
107   DCHECK(cache_.size() < 4096) << "The text cache is too big, which indicates "
108                                   "that it's most likely leaking";
109 }
110 
RenderTextInner(const std::string & text,int style)111 std::shared_ptr<const Text> TextRenderer::RenderTextInner(
112     const std::string& text,
113     int style) {
114   // Check if we have the text in the cache.
115   std::string cache_key = absl::StrCat(style, "/", text);
116   auto cache_it = cache_.find(cache_key);
117   if (cache_it != cache_.end()) {
118     cache_it->second.live = true;
119     return cache_it->second.text;
120   }
121 
122   if (TTF_GetFontStyle(font_) != style) {
123     TTF_SetFontStyle(font_, style);
124   }
125 
126   // Render the text.
127   ScopedSdlSurface surface;
128   if (text.empty()) {
129     // SDL2_ttf returns error when asked to render an empty string.  Return a
130     // 1x1 white square instead.
131     surface = ScopedSdlSurface(
132         SDL_CreateRGBSurfaceWithFormat(0, 1, 1, 32, SDL_PIXELFORMAT_RGBA32));
133     CHECK(!SDL_MUSTLOCK(*surface));
134     memset(surface->pixels, 0xff, surface->h * surface->pitch);
135   } else {
136     surface = TTF_RenderUTF8_Blended(font_, text.c_str(), SDL_Color{0, 0, 0});
137   }
138   if (*surface == nullptr) {
139     LOG(FATAL) << "Failed to render text: \"" << text.size() << "\"";
140   }
141 
142   std::unique_ptr<Text> result(new Text(this));
143   // Find the correct size for the texture, which is required to be a power of
144   // two.
145   size_t size =
146       std::max(NearestPowerOfTwo(surface->w), NearestPowerOfTwo(surface->h));
147   result->width_ = surface->w;
148   result->height_ = surface->h;
149   result->texture_size_ = size;
150 
151   // Create a |size|x|size| texture buffer in RAM, and move the texture there.
152   ScopedSdlSurface scaled_surface(SDL_CreateRGBSurfaceWithFormat(
153       0, size, size, 32, SDL_PIXELFORMAT_RGBA32));
154   SDL_Rect target{0, 0, surface->w, surface->h};
155   SDL_BlitSurface(*surface, nullptr, *scaled_surface, &target);
156 
157   CHECK_EQ(scaled_surface->format->format, SDL_PIXELFORMAT_RGBA32);
158   CHECK_EQ(scaled_surface->pitch, size * 4);
159   CHECK(!SDL_MUSTLOCK(*scaled_surface));
160 
161   // Upload the texture onto the GPU.
162   glBindTexture(GL_TEXTURE_2D, *result->texture_);
163   glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
164   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA,
165                GL_UNSIGNED_BYTE, scaled_surface->pixels);
166 
167   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
168   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
169 
170   std::shared_ptr<const Text> cached_result(result.release());
171   cache_.emplace(cache_key, CacheEntry{cached_result, true});
172   return cached_result;
173 }
174 
Draw(int x,int y) const175 void Text::Draw(int x, int y) const {
176   const float fx = x, fy = y, fw = width_, fh = height_;
177   const float vertices[] = {
178       fx,      fy + fh,  // upper left
179       fx + fw, fy + fh,  // upper right
180       fx,      fy,       // lower left
181       fx + fw, fy,       // lower right
182   };
183 
184   GlVertexBuffer buffer;
185   glBindBuffer(GL_ARRAY_BUFFER, *buffer);
186   glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW);
187 
188   GlVertexArray array;
189   glBindVertexArray(*array);
190 
191   glBindTexture(GL_TEXTURE_2D, *texture_);
192   factory_->shader_.SetUniform("texture_w", width_ / texture_size_);
193   factory_->shader_.SetUniform("texture_h", height_ / texture_size_);
194 
195   GlVertexArrayAttrib coord(factory_->shader_, "coord");
196   glVertexAttribPointer(*coord, 2, GL_FLOAT, GL_FALSE, 0, 0);
197   glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
198 }
199 
200 }  // namespace render
201 }  // namespace quic_trace
202