summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-07-22 21:40:36 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-07-22 21:40:36 +0300
commit366f4036453ee879d50820110275bf7ed69d6f8c (patch)
tree4b917f9336bb094c07fa13b7c6bc5cfbd434de36 /src
parentbaddad63d8c775d9d024ef8ee80b978dcf030af9 (diff)
Text: Half-pixel glyph variants
The cache contains two variants of each glyph: zero and half-pixel offsets. This allows positioning glyph more accurately inside a text run.
Diffstat (limited to 'src')
-rw-r--r--src/ui/documentwidget.c18
-rw-r--r--src/ui/text.c76
2 files changed, 62 insertions, 32 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 55ac6b80..eae7c00a 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -172,9 +172,21 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
172 if (ev->type == SDL_KEYDOWN) { 172 if (ev->type == SDL_KEYDOWN) {
173 const int mods = keyMods_Sym(ev->key.keysym.mod); 173 const int mods = keyMods_Sym(ev->key.keysym.mod);
174 const int key = ev->key.keysym.sym; 174 const int key = ev->key.keysym.sym;
175 if (mods == KMOD_PRIMARY && key == 'r') { 175 switch (key) {
176 fetch_DocumentWidget_(d); 176 case 'r':
177 return iTrue; 177 if (mods == KMOD_PRIMARY) {
178 fetch_DocumentWidget_(d);
179 return iTrue;
180 }
181 break;
182 case '0': {
183 extern int enableHalfPixelGlyphs_Text;
184 enableHalfPixelGlyphs_Text = !enableHalfPixelGlyphs_Text;
185 postRefresh_App();
186 printf("halfpixel: %d\n", enableHalfPixelGlyphs_Text);
187 fflush(stdout);
188 break;
189 }
178 } 190 }
179 } 191 }
180 else if (ev->type == SDL_MOUSEWHEEL) { 192 else if (ev->type == SDL_MOUSEWHEEL) {
diff --git a/src/ui/text.c b/src/ui/text.c
index 6ed95dc0..e5045899 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -23,14 +23,16 @@ iDeclareTypeConstructionArgs(Glyph, iChar ch)
23 23
24struct Impl_Glyph { 24struct Impl_Glyph {
25 iHashNode node; 25 iHashNode node;
26 iRect rect; 26 iRect rect[2]; /* zero and half pixel offset */
27 int advance; 27 int advance;
28 int dx, dy; 28 //int dx, dy;
29 iInt2 d[2];
29}; 30};
30 31
31void init_Glyph(iGlyph *d, iChar ch) { 32void init_Glyph(iGlyph *d, iChar ch) {
32 d->node.key = ch; 33 d->node.key = ch;
33 d->rect = zero_Rect(); 34 d->rect[0] = zero_Rect();
35 d->rect[1] = zero_Rect();
34 d->advance = 0; 36 d->advance = 0;
35} 37}
36 38
@@ -141,9 +143,10 @@ void deinit_Text(void) {
141 d->render = NULL; 143 d->render = NULL;
142} 144}
143 145
144static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, iChar ch) { 146static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, iChar ch, float xShift) {
145 int w, h; 147 int w, h;
146 uint8_t *bmp = stbtt_GetCodepointBitmap(&d->font, d->scale, d->scale, ch, &w, &h, 0, 0); 148 uint8_t *bmp = stbtt_GetCodepointBitmapSubpixel(
149 &d->font, d->scale, d->scale, xShift, 0.0f, ch, &w, &h, 0, 0);
147 /* Note: `bmp` must be freed afterwards. */ 150 /* Note: `bmp` must be freed afterwards. */
148 SDL_Surface *surface = 151 SDL_Surface *surface =
149 SDL_CreateRGBSurfaceWithFormatFrom(bmp, w, h, 8, w, SDL_PIXELFORMAT_INDEX8); 152 SDL_CreateRGBSurfaceWithFormatFrom(bmp, w, h, 8, w, SDL_PIXELFORMAT_INDEX8);
@@ -204,33 +207,44 @@ static void fillTriangle_(SDL_Surface *surface, const SDL_Rect *rect, int dir) {
204} 207}
205#endif 208#endif
206 209
207static void cache_Font_(iFont *d, iGlyph *glyph) { 210static void cache_Font_(iFont *d, iGlyph *glyph, int hoff) {
208 iText *txt = &text_; 211 iText *txt = &text_;
209 SDL_Renderer *render = txt->render; 212 SDL_Renderer *render = txt->render;
210 SDL_Texture *tex = NULL; 213 SDL_Texture *tex = NULL;
211 SDL_Surface *surface = NULL; 214 SDL_Surface *surface = NULL;
212 const iChar ch = char_Glyph(glyph); 215 const iChar ch = char_Glyph(glyph);
213 iBool fromStb = iFalse; 216 iBool fromStb = iFalse;
217 iRect *glRect = &glyph->rect[hoff];
214 if (!isSpecialChar_(ch)) { 218 if (!isSpecialChar_(ch)) {
215 /* Rasterize the glyph using stbtt. */ 219 /* Rasterize the glyph using stbtt. */
216 surface = rasterizeGlyph_Font_(d, ch); 220 surface = rasterizeGlyph_Font_(d, ch, hoff * 0.5f);
217 int lsb; 221 if (hoff == 0) {
218 stbtt_GetCodepointHMetrics(&d->font, ch, &glyph->advance, &lsb); 222 int lsb;
219 stbtt_GetCodepointBitmapBox( 223 stbtt_GetCodepointHMetrics(&d->font, ch, &glyph->advance, &lsb);
220 &d->font, ch, d->scale, d->scale, &glyph->dx, &glyph->dy, NULL, NULL); 224 }
225 stbtt_GetCodepointBitmapBoxSubpixel(&d->font,
226 ch,
227 d->scale,
228 d->scale,
229 hoff * 0.5f,
230 0.0f,
231 &glyph->d[hoff].x,
232 &glyph->d[hoff].y,
233 NULL,
234 NULL);
221 fromStb = iTrue; 235 fromStb = iTrue;
222 tex = SDL_CreateTextureFromSurface(render, surface); 236 tex = SDL_CreateTextureFromSurface(render, surface);
223 glyph->rect.size = init_I2(surface->w, surface->h); 237 glRect->size = init_I2(surface->w, surface->h);
224 } 238 }
225 else { 239 else {
226 /* Metrics for special symbols. */ 240 /* Metrics for special symbols. */
227 int em, lsb; 241 int em, lsb;
228 const int symbol = specialChar_(ch); 242 const int symbol = specialChar_(ch);
229 stbtt_GetCodepointHMetrics(&d->font, 'M', &em, &lsb); 243 stbtt_GetCodepointHMetrics(&d->font, 'M', &em, &lsb);
230 glyph->dx = d->baseline / 10; 244 glyph->d[hoff].x = d->baseline / 10;
231 glyph->dy = -d->baseline; 245 glyph->d[hoff].y = -d->baseline;
232 glyph->advance = em * symbolAdvance_(symbol); 246 glyph->advance = em * symbolAdvance_(symbol);
233 glyph->rect.size = init_I2(symbolEmWidth_(symbol) * em * d->scale, d->height); 247 glyph->rect[hoff].size = init_I2(symbolEmWidth_(symbol) * em * d->scale, d->height);
234#if 0 248#if 0
235 if (isRasterizedSymbol_(ch)) { 249 if (isRasterizedSymbol_(ch)) {
236 /* Rasterize manually. */ 250 /* Rasterize manually. */
@@ -264,18 +278,19 @@ static void cache_Font_(iFont *d, iGlyph *glyph) {
264#endif 278#endif
265 } 279 }
266 /* Determine placement in the glyph cache texture, advancing in rows. */ 280 /* Determine placement in the glyph cache texture, advancing in rows. */
267 if (txt->cachePos.x + glyph->rect.size.x > txt->cacheSize.x) { 281 if (txt->cachePos.x + glRect->size.x > txt->cacheSize.x) {
268 txt->cachePos.x = 0; 282 txt->cachePos.x = 0;
269 txt->cachePos.y += txt->cacheRowHeight; 283 txt->cachePos.y += txt->cacheRowHeight;
270 txt->cacheRowHeight = 0; 284 txt->cacheRowHeight = 0;
271 } 285 }
272 glyph->rect.pos = txt->cachePos; 286 glRect->pos = txt->cachePos;
273 SDL_SetRenderTarget(render, txt->cache); 287 SDL_SetRenderTarget(render, txt->cache);
274 const SDL_Rect dstRect = sdlRect_(glyph->rect); 288 const SDL_Rect dstRect = sdlRect_(*glRect);
275 if (surface) { 289 if (surface) {
276 SDL_RenderCopy(render, tex, &(SDL_Rect){ 0, 0, dstRect.w, dstRect.h }, &dstRect); 290 SDL_RenderCopy(render, tex, &(SDL_Rect){ 0, 0, dstRect.w, dstRect.h }, &dstRect);
277 } 291 }
278 else { 292 else {
293#if 0
279 /* Draw a special symbol. */ 294 /* Draw a special symbol. */
280 SDL_SetRenderDrawColor(render, 255, 255, 255, 255); 295 SDL_SetRenderDrawColor(render, 255, 255, 255, 255);
281 const iInt2 tl = init_I2(dstRect.x, dstRect.y); 296 const iInt2 tl = init_I2(dstRect.x, dstRect.y);
@@ -283,7 +298,6 @@ static void cache_Font_(iFont *d, iGlyph *glyph) {
283 const int midX = tl.x + dstRect.w / 2; 298 const int midX = tl.x + dstRect.w / 2;
284 const int midY = tl.y + dstRect.h / 2; 299 const int midY = tl.y + dstRect.h / 2;
285 const int symH = dstRect.h * 2 / 6; 300 const int symH = dstRect.h * 2 / 6;
286#if 0
287 /* Frame. */ 301 /* Frame. */
288 if (isFramedSymbol_(ch)) { 302 if (isFramedSymbol_(ch)) {
289 SDL_RenderDrawLines( 303 SDL_RenderDrawLines(
@@ -350,8 +364,8 @@ static void cache_Font_(iFont *d, iGlyph *glyph) {
350 SDL_FreeSurface(surface); 364 SDL_FreeSurface(surface);
351 } 365 }
352 /* Update cache cursor. */ 366 /* Update cache cursor. */
353 txt->cachePos.x += glyph->rect.size.x; 367 txt->cachePos.x += glRect->size.x;
354 txt->cacheRowHeight = iMax(txt->cacheRowHeight, glyph->rect.size.y); 368 txt->cacheRowHeight = iMax(txt->cacheRowHeight, glRect->size.y);
355} 369}
356 370
357static const iGlyph *glyph_Font_(iFont *d, iChar ch) { 371static const iGlyph *glyph_Font_(iFont *d, iChar ch) {
@@ -360,7 +374,8 @@ static const iGlyph *glyph_Font_(iFont *d, iChar ch) {
360 return node; 374 return node;
361 } 375 }
362 iGlyph *glyph = new_Glyph(ch); 376 iGlyph *glyph = new_Glyph(ch);
363 cache_Font_(d, glyph); 377 cache_Font_(d, glyph, 0);
378 cache_Font_(d, glyph, 1); /* half-pixel offset */
364 insert_Hash(&d->glyphs, &glyph->node); 379 insert_Hash(&d->glyphs, &glyph->node);
365 return glyph; 380 return glyph;
366} 381}
@@ -381,6 +396,8 @@ static iChar nextChar_(const char **chPos, const char *end) {
381 return ch; 396 return ch;
382} 397}
383 398
399int enableHalfPixelGlyphs_Text = iTrue;
400
384static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLen, iInt2 pos, 401static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLen, iInt2 pos,
385 int xposLimit, const char **continueFrom_out, int *runAdvance_out) { 402 int xposLimit, const char **continueFrom_out, int *runAdvance_out) {
386 iInt2 size = zero_I2(); 403 iInt2 size = zero_I2();
@@ -415,8 +432,9 @@ static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe
415 } 432 }
416 } 433 }
417 const iGlyph *glyph = glyph_Font_(d, ch); 434 const iGlyph *glyph = glyph_Font_(d, ch);
418 float x1 = xpos; 435 int x1 = xpos;
419 float x2 = x1 + glyph->rect.size.x; 436 const int hoff = enableHalfPixelGlyphs_Text ? (xpos - x1 > 0.5f ? 1 : 0) : 0;
437 int x2 = x1 + glyph->rect[hoff].size.x;
420 if (xposLimit > 0 && x2 > xposLimit) { 438 if (xposLimit > 0 && x2 > xposLimit) {
421 /* Out of space. */ 439 /* Out of space. */
422 *continueFrom_out = lastWordEnd; 440 *continueFrom_out = lastWordEnd;
@@ -425,11 +443,11 @@ static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe
425 size.x = iMax(size.x, x2 - orig.x); 443 size.x = iMax(size.x, x2 - orig.x);
426 size.y = iMax(size.y, pos.y + d->height - orig.y); 444 size.y = iMax(size.y, pos.y + d->height - orig.y);
427 if (mode != measure_RunMode) { 445 if (mode != measure_RunMode) {
428 SDL_FRect dst = { x1 + glyph->dx, 446 SDL_Rect dst = { x1 + glyph->d[hoff].x,
429 pos.y + d->baseline + glyph->dy, 447 pos.y + d->baseline + glyph->d[hoff].y,
430 glyph->rect.size.x, 448 glyph->rect[hoff].size.x,
431 glyph->rect.size.y }; 449 glyph->rect[hoff].size.y };
432 SDL_RenderCopyF(text_.render, text_.cache, (const SDL_Rect *) &glyph->rect, &dst); 450 SDL_RenderCopy(text_.render, text_.cache, (const SDL_Rect *) &glyph->rect[hoff], &dst);
433 } 451 }
434 xpos += d->scale * glyph->advance; 452 xpos += d->scale * glyph->advance;
435 xposMax = iMax(xposMax, xpos); 453 xposMax = iMax(xposMax, xpos);