Fix T80900: Crash lasso-selecting in particle editmode in certain
[blender.git] / source / blender / editors / space_info / info_stats.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file
18  * \ingroup spinfo
19  */
20
21 #include <stdio.h>
22 #include <string.h>
23
24 #include "MEM_guardedalloc.h"
25
26 #include "DNA_armature_types.h"
27 #include "DNA_collection_types.h"
28 #include "DNA_curve_types.h"
29 #include "DNA_gpencil_types.h"
30 #include "DNA_lattice_types.h"
31 #include "DNA_mesh_types.h"
32 #include "DNA_meta_types.h"
33 #include "DNA_scene_types.h"
34 #include "DNA_windowmanager_types.h"
35
36 #include "BLF_api.h"
37
38 #include "BLI_listbase.h"
39 #include "BLI_math.h"
40 #include "BLI_string.h"
41 #include "BLI_utildefines.h"
42
43 #include "BLT_translation.h"
44
45 #include "BKE_blender_version.h"
46 #include "BKE_context.h"
47 #include "BKE_curve.h"
48 #include "BKE_displist.h"
49 #include "BKE_editmesh.h"
50 #include "BKE_gpencil.h"
51 #include "BKE_key.h"
52 #include "BKE_layer.h"
53 #include "BKE_main.h"
54 #include "BKE_object.h"
55 #include "BKE_paint.h"
56 #include "BKE_particle.h"
57 #include "BKE_scene.h"
58 #include "BKE_subdiv_ccg.h"
59
60 #include "DEG_depsgraph_query.h"
61
62 #include "ED_armature.h"
63 #include "ED_info.h"
64
65 #include "UI_resources.h"
66
67 #include "GPU_capabilities.h"
68
69 #define MAX_INFO_NUM_LEN 16
70
71 typedef struct SceneStats {
72   uint64_t totvert, totvertsel;
73   uint64_t totedge, totedgesel;
74   uint64_t totface, totfacesel;
75   uint64_t totbone, totbonesel;
76   uint64_t totobj, totobjsel;
77   uint64_t totlamp, totlampsel;
78   uint64_t tottri;
79   uint64_t totgplayer, totgpframe, totgpstroke, totgppoint;
80 } SceneStats;
81
82 typedef struct SceneStatsFmt {
83   /* Totals */
84   char totvert[MAX_INFO_NUM_LEN], totvertsel[MAX_INFO_NUM_LEN];
85   char totface[MAX_INFO_NUM_LEN], totfacesel[MAX_INFO_NUM_LEN];
86   char totedge[MAX_INFO_NUM_LEN], totedgesel[MAX_INFO_NUM_LEN];
87   char totbone[MAX_INFO_NUM_LEN], totbonesel[MAX_INFO_NUM_LEN];
88   char totobj[MAX_INFO_NUM_LEN], totobjsel[MAX_INFO_NUM_LEN];
89   char totlamp[MAX_INFO_NUM_LEN], totlampsel[MAX_INFO_NUM_LEN];
90   char tottri[MAX_INFO_NUM_LEN];
91   char totgplayer[MAX_INFO_NUM_LEN], totgpframe[MAX_INFO_NUM_LEN];
92   char totgpstroke[MAX_INFO_NUM_LEN], totgppoint[MAX_INFO_NUM_LEN];
93 } SceneStatsFmt;
94
95 static bool stats_mesheval(Mesh *me_eval, bool is_selected, SceneStats *stats)
96 {
97   if (me_eval == NULL) {
98     return false;
99   }
100
101   int totvert, totedge, totface, totloop;
102   if (me_eval->runtime.subdiv_ccg != NULL) {
103     const SubdivCCG *subdiv_ccg = me_eval->runtime.subdiv_ccg;
104     BKE_subdiv_ccg_topology_counters(subdiv_ccg, &totvert, &totedge, &totface, &totloop);
105   }
106   else {
107     totvert = me_eval->totvert;
108     totedge = me_eval->totedge;
109     totface = me_eval->totpoly;
110     totloop = me_eval->totloop;
111   }
112
113   stats->totvert += totvert;
114   stats->totedge += totedge;
115   stats->totface += totface;
116   stats->tottri += poly_to_tri_count(totface, totloop);
117
118   if (is_selected) {
119     stats->totvertsel += totvert;
120     stats->totfacesel += totface;
121   }
122   return true;
123 }
124
125 static void stats_object(Object *ob, SceneStats *stats, GSet *objects_gset)
126 {
127   if ((ob->base_flag & BASE_VISIBLE_VIEWLAYER) == 0) {
128     return;
129   }
130
131   const bool is_selected = (ob->base_flag & BASE_SELECTED) != 0;
132
133   stats->totobj++;
134   if (is_selected) {
135     stats->totobjsel++;
136   }
137
138   switch (ob->type) {
139     case OB_MESH: {
140       /* we assume evaluated mesh is already built, this strictly does stats now. */
141       Mesh *me_eval = BKE_object_get_evaluated_mesh(ob);
142       if (!BLI_gset_add(objects_gset, me_eval)) {
143         break;
144       }
145       stats_mesheval(me_eval, is_selected, stats);
146       break;
147     }
148     case OB_LAMP:
149       stats->totlamp++;
150       if (is_selected) {
151         stats->totlampsel++;
152       }
153       break;
154     case OB_SURF:
155     case OB_CURVE:
156     case OB_FONT: {
157       Mesh *me_eval = BKE_object_get_evaluated_mesh(ob);
158       if ((me_eval != NULL) && !BLI_gset_add(objects_gset, me_eval)) {
159         break;
160       }
161
162       if (stats_mesheval(me_eval, is_selected, stats)) {
163         break;
164       }
165       ATTR_FALLTHROUGH; /* Fallthrough to displist. */
166     }
167     case OB_MBALL: {
168       int totv = 0, totf = 0, tottri = 0;
169
170       if (ob->runtime.curve_cache && ob->runtime.curve_cache->disp.first) {
171         /* Note: We only get the same curve_cache for instances of the same curve/font/...
172          * For simple linked duplicated objects, each has its own dispList. */
173         if (!BLI_gset_add(objects_gset, ob->runtime.curve_cache)) {
174           break;
175         }
176
177         BKE_displist_count(&ob->runtime.curve_cache->disp, &totv, &totf, &tottri);
178       }
179
180       stats->totvert += totv;
181       stats->totface += totf;
182       stats->tottri += tottri;
183
184       if (is_selected) {
185         stats->totvertsel += totv;
186         stats->totfacesel += totf;
187       }
188       break;
189     }
190     case OB_GPENCIL: {
191       if (is_selected) {
192         bGPdata *gpd = (bGPdata *)ob->data;
193         if (!BLI_gset_add(objects_gset, gpd)) {
194           break;
195         }
196         /* GPXX Review if we can move to other place when object change
197          * maybe to depsgraph evaluation
198          */
199         BKE_gpencil_stats_update(gpd);
200
201         stats->totgplayer += gpd->totlayer;
202         stats->totgpframe += gpd->totframe;
203         stats->totgpstroke += gpd->totstroke;
204         stats->totgppoint += gpd->totpoint;
205       }
206       break;
207     }
208     case OB_HAIR:
209     case OB_POINTCLOUD:
210     case OB_VOLUME: {
211       break;
212     }
213   }
214 }
215
216 static void stats_object_edit(Object *obedit, SceneStats *stats)
217 {
218   if (obedit->type == OB_MESH) {
219     BMEditMesh *em = BKE_editmesh_from_object(obedit);
220
221     stats->totvert += em->bm->totvert;
222     stats->totvertsel += em->bm->totvertsel;
223
224     stats->totedge += em->bm->totedge;
225     stats->totedgesel += em->bm->totedgesel;
226
227     stats->totface += em->bm->totface;
228     stats->totfacesel += em->bm->totfacesel;
229
230     stats->tottri += em->tottri;
231   }
232   else if (obedit->type == OB_ARMATURE) {
233     /* Armature Edit */
234     bArmature *arm = obedit->data;
235     EditBone *ebo;
236
237     for (ebo = arm->edbo->first; ebo; ebo = ebo->next) {
238       stats->totbone++;
239
240       if ((ebo->flag & BONE_CONNECTED) && ebo->parent) {
241         stats->totvert--;
242       }
243
244       if (ebo->flag & BONE_TIPSEL) {
245         stats->totvertsel++;
246       }
247       if (ebo->flag & BONE_ROOTSEL) {
248         stats->totvertsel++;
249       }
250
251       if (ebo->flag & BONE_SELECTED) {
252         stats->totbonesel++;
253       }
254
255       /* if this is a connected child and it's parent is being moved, remove our root */
256       if ((ebo->flag & BONE_CONNECTED) && (ebo->flag & BONE_ROOTSEL) && ebo->parent &&
257           (ebo->parent->flag & BONE_TIPSEL)) {
258         stats->totvertsel--;
259       }
260
261       stats->totvert += 2;
262     }
263   }
264   else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { /* OB_FONT has no cu->editnurb */
265     /* Curve Edit */
266     Curve *cu = obedit->data;
267     Nurb *nu;
268     BezTriple *bezt;
269     BPoint *bp;
270     int a;
271     ListBase *nurbs = BKE_curve_editNurbs_get(cu);
272
273     for (nu = nurbs->first; nu; nu = nu->next) {
274       if (nu->type == CU_BEZIER) {
275         bezt = nu->bezt;
276         a = nu->pntsu;
277         while (a--) {
278           stats->totvert += 3;
279           if (bezt->f1 & SELECT) {
280             stats->totvertsel++;
281           }
282           if (bezt->f2 & SELECT) {
283             stats->totvertsel++;
284           }
285           if (bezt->f3 & SELECT) {
286             stats->totvertsel++;
287           }
288           bezt++;
289         }
290       }
291       else {
292         bp = nu->bp;
293         a = nu->pntsu * nu->pntsv;
294         while (a--) {
295           stats->totvert++;
296           if (bp->f1 & SELECT) {
297             stats->totvertsel++;
298           }
299           bp++;
300         }
301       }
302     }
303   }
304   else if (obedit->type == OB_MBALL) {
305     /* MetaBall Edit */
306     MetaBall *mball = obedit->data;
307     MetaElem *ml;
308
309     for (ml = mball->editelems->first; ml; ml = ml->next) {
310       stats->totvert++;
311       if (ml->flag & SELECT) {
312         stats->totvertsel++;
313       }
314     }
315   }
316   else if (obedit->type == OB_LATTICE) {
317     /* Lattice Edit */
318     Lattice *lt = obedit->data;
319     Lattice *editlatt = lt->editlatt->latt;
320     BPoint *bp;
321     int a;
322
323     bp = editlatt->def;
324
325     a = editlatt->pntsu * editlatt->pntsv * editlatt->pntsw;
326     while (a--) {
327       stats->totvert++;
328       if (bp->f1 & SELECT) {
329         stats->totvertsel++;
330       }
331       bp++;
332     }
333   }
334 }
335
336 static void stats_object_pose(Object *ob, SceneStats *stats)
337 {
338   if (ob->pose) {
339     bArmature *arm = ob->data;
340     bPoseChannel *pchan;
341
342     for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
343       stats->totbone++;
344       if (pchan->bone && (pchan->bone->flag & BONE_SELECTED)) {
345         if (pchan->bone->layer & arm->layer) {
346           stats->totbonesel++;
347         }
348       }
349     }
350   }
351 }
352
353 static void stats_object_sculpt_dynamic_topology(Object *ob, SceneStats *stats)
354 {
355   stats->totvert = ob->sculpt->bm->totvert;
356   stats->tottri = ob->sculpt->bm->totface;
357 }
358
359 static bool stats_is_object_dynamic_topology_sculpt(Object *ob, const eObjectMode object_mode)
360 {
361   return (ob && (object_mode & OB_MODE_SCULPT) && ob->sculpt && ob->sculpt->bm);
362 }
363
364 /* Statistics displayed in info header. Called regularly on scene changes. */
365 static void stats_update(Depsgraph *depsgraph, ViewLayer *view_layer)
366 {
367   SceneStats stats = {0};
368   Object *ob = OBACT(view_layer);
369   Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
370
371   if (obedit) {
372     /* Edit Mode */
373     FOREACH_OBJECT_BEGIN (view_layer, ob_iter) {
374       if (ob_iter->base_flag & BASE_VISIBLE_VIEWLAYER) {
375         if (ob_iter->mode == OB_MODE_EDIT) {
376           stats_object_edit(ob_iter, &stats);
377           stats.totobjsel++;
378         }
379         stats.totobj++;
380       }
381     }
382     FOREACH_OBJECT_END;
383   }
384   else if (ob && (ob->mode & OB_MODE_POSE)) {
385     /* Pose Mode */
386     stats_object_pose(ob, &stats);
387   }
388   else if (ob && stats_is_object_dynamic_topology_sculpt(ob, ob->mode)) {
389     /* Dynamic-topology sculpt mode */
390     stats_object_sculpt_dynamic_topology(ob, &stats);
391   }
392   else {
393     /* Objects */
394     GSet *objects_gset = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
395     DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (depsgraph, ob_iter) {
396       stats_object(ob_iter, &stats, objects_gset);
397     }
398     DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END;
399     BLI_gset_free(objects_gset, NULL);
400   }
401
402   if (!view_layer->stats) {
403     view_layer->stats = MEM_callocN(sizeof(SceneStats), "SceneStats");
404   }
405
406   *(view_layer->stats) = stats;
407 }
408
409 void ED_info_stats_clear(ViewLayer *view_layer)
410 {
411   if (view_layer->stats) {
412     MEM_freeN(view_layer->stats);
413     view_layer->stats = NULL;
414   }
415 }
416
417 static bool format_stats(Main *bmain,
418                          Scene *scene,
419                          ViewLayer *view_layer,
420                          SceneStatsFmt *stats_fmt)
421 {
422   /* Create stats if they don't already exist. */
423   if (!view_layer->stats) {
424     /* Do not not access dependency graph if interface is marked as locked. */
425     wmWindowManager *wm = bmain->wm.first;
426     if (wm->is_interface_locked) {
427       return false;
428     }
429     Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer);
430     stats_update(depsgraph, view_layer);
431   }
432
433   SceneStats *stats = view_layer->stats;
434
435   /* Generate formatted numbers. */
436 #define SCENE_STATS_FMT_INT(_id) BLI_str_format_uint64_grouped(stats_fmt->_id, stats->_id)
437
438   SCENE_STATS_FMT_INT(totvert);
439   SCENE_STATS_FMT_INT(totvertsel);
440
441   SCENE_STATS_FMT_INT(totedge);
442   SCENE_STATS_FMT_INT(totedgesel);
443
444   SCENE_STATS_FMT_INT(totface);
445   SCENE_STATS_FMT_INT(totfacesel);
446
447   SCENE_STATS_FMT_INT(totbone);
448   SCENE_STATS_FMT_INT(totbonesel);
449
450   SCENE_STATS_FMT_INT(totobj);
451   SCENE_STATS_FMT_INT(totobjsel);
452
453   SCENE_STATS_FMT_INT(totlamp);
454   SCENE_STATS_FMT_INT(totlampsel);
455
456   SCENE_STATS_FMT_INT(tottri);
457
458   SCENE_STATS_FMT_INT(totgplayer);
459   SCENE_STATS_FMT_INT(totgpframe);
460   SCENE_STATS_FMT_INT(totgpstroke);
461   SCENE_STATS_FMT_INT(totgppoint);
462
463 #undef SCENE_STATS_FMT_INT
464   return true;
465 }
466
467 static void get_stats_string(
468     char *info, int len, size_t *ofs, ViewLayer *view_layer, SceneStatsFmt *stats_fmt)
469 {
470   Object *ob = OBACT(view_layer);
471   Object *obedit = OBEDIT_FROM_OBACT(ob);
472   eObjectMode object_mode = ob ? ob->mode : OB_MODE_OBJECT;
473   LayerCollection *layer_collection = view_layer->active_collection;
474
475   if (object_mode == OB_MODE_OBJECT) {
476     *ofs += BLI_snprintf(info + *ofs,
477                          len - *ofs,
478                          "%s | ",
479                          BKE_collection_ui_name_get(layer_collection->collection));
480   }
481
482   if (ob) {
483     *ofs += BLI_snprintf(info + *ofs, len - *ofs, "%s | ", ob->id.name + 2);
484   }
485
486   if (obedit) {
487     if (BKE_keyblock_from_object(obedit)) {
488       *ofs += BLI_strncpy_rlen(info + *ofs, TIP_("(Key) "), len - *ofs);
489     }
490
491     if (obedit->type == OB_MESH) {
492       *ofs += BLI_snprintf(info + *ofs,
493                            len - *ofs,
494                            TIP_("Verts:%s/%s | Edges:%s/%s | Faces:%s/%s | Tris:%s"),
495                            stats_fmt->totvertsel,
496                            stats_fmt->totvert,
497                            stats_fmt->totedgesel,
498                            stats_fmt->totedge,
499                            stats_fmt->totfacesel,
500                            stats_fmt->totface,
501                            stats_fmt->tottri);
502     }
503     else if (obedit->type == OB_ARMATURE) {
504       *ofs += BLI_snprintf(info + *ofs,
505                            len - *ofs,
506                            TIP_("Verts:%s/%s | Bones:%s/%s"),
507                            stats_fmt->totvertsel,
508                            stats_fmt->totvert,
509                            stats_fmt->totbonesel,
510                            stats_fmt->totbone);
511     }
512     else {
513       *ofs += BLI_snprintf(
514           info + *ofs, len - *ofs, TIP_("Verts:%s/%s"), stats_fmt->totvertsel, stats_fmt->totvert);
515     }
516   }
517   else if (ob && (object_mode & OB_MODE_POSE)) {
518     *ofs += BLI_snprintf(
519         info + *ofs, len - *ofs, TIP_("Bones:%s/%s"), stats_fmt->totbonesel, stats_fmt->totbone);
520   }
521   else if ((ob) && (ob->type == OB_GPENCIL)) {
522     *ofs += BLI_snprintf(info + *ofs,
523                          len - *ofs,
524                          TIP_("Layers:%s | Frames:%s | Strokes:%s | Points:%s"),
525                          stats_fmt->totgplayer,
526                          stats_fmt->totgpframe,
527                          stats_fmt->totgpstroke,
528                          stats_fmt->totgppoint);
529   }
530   else if (stats_is_object_dynamic_topology_sculpt(ob, object_mode)) {
531     *ofs += BLI_snprintf(info + *ofs,
532                          len - *ofs,
533                          TIP_("Verts:%s | Tris:%s"),
534                          stats_fmt->totvert,
535                          stats_fmt->tottri);
536   }
537   else {
538     *ofs += BLI_snprintf(info + *ofs,
539                          len - *ofs,
540                          TIP_("Verts:%s | Faces:%s | Tris:%s"),
541                          stats_fmt->totvert,
542                          stats_fmt->totface,
543                          stats_fmt->tottri);
544   }
545
546   *ofs += BLI_snprintf(
547       info + *ofs, len - *ofs, TIP_(" | Objects:%s/%s"), stats_fmt->totobjsel, stats_fmt->totobj);
548 }
549
550 static const char *info_statusbar_string(Main *bmain,
551                                          Scene *scene,
552                                          ViewLayer *view_layer,
553                                          char statusbar_flag)
554 {
555   char formatted_mem[15];
556   size_t ofs = 0;
557   static char info[256];
558   int len = sizeof(info);
559
560   info[0] = '\0';
561
562   /* Scene statistics. */
563   if (statusbar_flag & STATUSBAR_SHOW_STATS) {
564     SceneStatsFmt stats_fmt;
565     if (format_stats(bmain, scene, view_layer, &stats_fmt)) {
566       get_stats_string(info + ofs, len, &ofs, view_layer, &stats_fmt);
567     }
568   }
569
570   /* Memory status. */
571   if (statusbar_flag & STATUSBAR_SHOW_MEMORY) {
572     if (info[0]) {
573       ofs += BLI_snprintf(info + ofs, len - ofs, " | ");
574     }
575     uintptr_t mem_in_use = MEM_get_memory_in_use();
576     BLI_str_format_byte_unit(formatted_mem, mem_in_use, false);
577     ofs += BLI_snprintf(info + ofs, len, TIP_("Memory: %s"), formatted_mem);
578   }
579
580   /* GPU VRAM status. */
581   if ((statusbar_flag & STATUSBAR_SHOW_VRAM) && (GPU_mem_stats_supported())) {
582     int gpu_free_mem_kb, gpu_tot_mem_kb;
583     GPU_mem_stats_get(&gpu_tot_mem_kb, &gpu_free_mem_kb);
584     float gpu_total_gb = gpu_tot_mem_kb / 1048576.0f;
585     float gpu_free_gb = gpu_free_mem_kb / 1048576.0f;
586     if (info[0]) {
587       ofs += BLI_snprintf(info + ofs, len - ofs, " | ");
588     }
589     if (gpu_free_mem_kb && gpu_tot_mem_kb) {
590       ofs += BLI_snprintf(info + ofs,
591                           len - ofs,
592                           TIP_("VRAM: %.1f/%.1f GiB"),
593                           gpu_total_gb - gpu_free_gb,
594                           gpu_total_gb);
595     }
596     else {
597       /* Can only show amount of GPU VRAM available. */
598       ofs += BLI_snprintf(info + ofs, len - ofs, TIP_("VRAM: %.1f GiB Free"), gpu_free_gb);
599     }
600   }
601
602   /* Blender version. */
603   if (statusbar_flag & STATUSBAR_SHOW_VERSION) {
604     if (info[0]) {
605       ofs += BLI_snprintf(info + ofs, len - ofs, " | ");
606     }
607     ofs += BLI_snprintf(info + ofs, len - ofs, TIP_("%s"), BKE_blender_version_string());
608   }
609
610   return info;
611 }
612
613 const char *ED_info_statusbar_string(Main *bmain, Scene *scene, ViewLayer *view_layer)
614 {
615   return info_statusbar_string(bmain, scene, view_layer, U.statusbar_flag);
616 }
617
618 const char *ED_info_statistics_string(Main *bmain, Scene *scene, ViewLayer *view_layer)
619 {
620   const eUserpref_StatusBar_Flag statistics_status_bar_flag = STATUSBAR_SHOW_STATS |
621                                                               STATUSBAR_SHOW_MEMORY |
622                                                               STATUSBAR_SHOW_VERSION;
623
624   return info_statusbar_string(bmain, scene, view_layer, statistics_status_bar_flag);
625 }
626
627 static void stats_row(int col1,
628                       const char *key,
629                       int col2,
630                       const char *value1,
631                       const char *value2,
632                       int *y,
633                       int height)
634 {
635   *y -= height;
636   BLF_draw_default(col1, *y, 0.0f, key, 128);
637   char values[128];
638   BLI_snprintf(values, sizeof(values), (value2) ? "%s / %s" : "%s", value1, value2);
639   BLF_draw_default(col2, *y, 0.0f, values, sizeof(values));
640 }
641
642 void ED_info_draw_stats(
643     Main *bmain, Scene *scene, ViewLayer *view_layer, int x, int *y, int height)
644 {
645   SceneStatsFmt stats_fmt;
646   if (!format_stats(bmain, scene, view_layer, &stats_fmt)) {
647     return;
648   }
649
650   Object *ob = OBACT(view_layer);
651   Object *obedit = OBEDIT_FROM_OBACT(ob);
652   eObjectMode object_mode = ob ? ob->mode : OB_MODE_OBJECT;
653   const int font_id = BLF_default();
654
655   UI_FontThemeColor(font_id, TH_TEXT_HI);
656   BLF_enable(font_id, BLF_SHADOW);
657   BLF_shadow(font_id, 5, (const float[4]){0.0f, 0.0f, 0.0f, 1.0f});
658   BLF_shadow_offset(font_id, 1, -1);
659
660   /* Translated labels for each stat row. */
661   enum {
662     OBJ,
663     VERTS,
664     EDGES,
665     FACES,
666     TRIS,
667     BONES,
668     LAYERS,
669     FRAMES,
670     STROKES,
671     POINTS,
672     MAX_LABELS_COUNT
673   };
674   char labels[MAX_LABELS_COUNT][64];
675
676   STRNCPY(labels[OBJ], IFACE_("Objects"));
677   STRNCPY(labels[VERTS], IFACE_("Vertices"));
678   STRNCPY(labels[EDGES], IFACE_("Edges"));
679   STRNCPY(labels[FACES], IFACE_("Faces"));
680   STRNCPY(labels[TRIS], IFACE_("Triangles"));
681   STRNCPY(labels[BONES], IFACE_("Bones"));
682   STRNCPY(labels[LAYERS], IFACE_("Layers"));
683   STRNCPY(labels[FRAMES], IFACE_("Frames"));
684   STRNCPY(labels[STROKES], IFACE_("Strokes"));
685   STRNCPY(labels[POINTS], IFACE_("Points"));
686
687   int longest_label = 0;
688   int i;
689   for (i = 0; i < MAX_LABELS_COUNT; ++i) {
690     longest_label = max_ii(longest_label, BLF_width(font_id, labels[i], sizeof(labels[i])));
691   }
692
693   int col1 = x;
694   int col2 = x + longest_label + (0.5f * U.widget_unit);
695
696   /* Add some extra margin above this section. */
697   *y -= (0.6f * height);
698
699   if (object_mode == OB_MODE_OBJECT) {
700     stats_row(col1, labels[OBJ], col2, stats_fmt.totobjsel, stats_fmt.totobj, y, height);
701   }
702
703   if (obedit) {
704     if (obedit->type == OB_MESH) {
705       stats_row(col1, labels[OBJ], col2, stats_fmt.totobjsel, stats_fmt.totobj, y, height);
706       stats_row(col1, labels[VERTS], col2, stats_fmt.totvertsel, stats_fmt.totvert, y, height);
707       stats_row(col1, labels[EDGES], col2, stats_fmt.totedgesel, stats_fmt.totedge, y, height);
708       stats_row(col1, labels[FACES], col2, stats_fmt.totfacesel, stats_fmt.totface, y, height);
709       stats_row(col1, labels[TRIS], col2, stats_fmt.tottri, NULL, y, height);
710     }
711     else if (obedit->type == OB_ARMATURE) {
712       stats_row(col1, labels[VERTS], col2, stats_fmt.totvertsel, stats_fmt.totvert, y, height);
713       stats_row(col1, labels[BONES], col2, stats_fmt.totbonesel, stats_fmt.totbone, y, height);
714     }
715     else {
716       stats_row(col1, labels[VERTS], col2, stats_fmt.totvertsel, stats_fmt.totvert, y, height);
717     }
718   }
719   else if (ob && (object_mode & OB_MODE_POSE)) {
720     stats_row(col1, labels[BONES], col2, stats_fmt.totbonesel, stats_fmt.totbone, y, height);
721   }
722   else if ((ob) && (ob->type == OB_GPENCIL)) {
723     stats_row(col1, labels[LAYERS], col2, stats_fmt.totgplayer, NULL, y, height);
724     stats_row(col1, labels[FRAMES], col2, stats_fmt.totgpframe, NULL, y, height);
725     stats_row(col1, labels[STROKES], col2, stats_fmt.totgpstroke, NULL, y, height);
726     stats_row(col1, labels[POINTS], col2, stats_fmt.totgppoint, NULL, y, height);
727   }
728   else if (stats_is_object_dynamic_topology_sculpt(ob, object_mode)) {
729     stats_row(col1, labels[VERTS], col2, stats_fmt.totvert, NULL, y, height);
730     stats_row(col1, labels[TRIS], col2, stats_fmt.tottri, NULL, y, height);
731   }
732   else {
733     stats_row(col1, labels[VERTS], col2, stats_fmt.totvert, NULL, y, height);
734     stats_row(col1, labels[EDGES], col2, stats_fmt.totedge, NULL, y, height);
735     stats_row(col1, labels[FACES], col2, stats_fmt.totface, NULL, y, height);
736     stats_row(col1, labels[TRIS], col2, stats_fmt.tottri, NULL, y, height);
737   }
738
739   BLF_disable(font_id, BLF_SHADOW);
740 }