From bc8eb5e9cffbdf3c707ec4f2379e23cc8e31a793 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 11 Sep 2015 16:35:37 +0100 Subject: [PATCH] ImDrawList, ImFontAtlas: comments, tweaks. moved less scary functions at the top of the file. --- imgui_draw.cpp | 238 +++++++++++++++++++++++++------------------------ 1 file changed, 121 insertions(+), 117 deletions(-) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 97c7ac570..7ba0bf1a3 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -159,6 +159,69 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) AddDrawCmd(); } +void ImDrawList::UpdateClipRect() +{ + ImDrawCmd* current_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL; + if (!current_cmd || (current_cmd->ElemCount != 0) || current_cmd->UserCallback != NULL) + { + AddDrawCmd(); + } + else + { + ImVec4 current_clip_rect = _ClipRectStack.Size ? _ClipRectStack.back() : GNullClipRect; + if (CmdBuffer.Size >= 2 && ImLengthSqr(CmdBuffer.Data[CmdBuffer.Size-2].ClipRect - current_clip_rect) < 0.00001f) + CmdBuffer.pop_back(); + else + current_cmd->ClipRect = current_clip_rect; + } +} + +// Scissoring. The values in clip_rect are x1, y1, x2, y2. +void ImDrawList::PushClipRect(const ImVec4& clip_rect) +{ + _ClipRectStack.push_back(clip_rect); + UpdateClipRect(); +} + +void ImDrawList::PushClipRectFullScreen() +{ + PushClipRect(GNullClipRect); + + // FIXME-OPT: This would be more correct but we're not supposed to access ImGuiState from here? + //ImGuiState& g = *GImGui; + //PushClipRect(GetVisibleRect()); +} + +void ImDrawList::PopClipRect() +{ + IM_ASSERT(_ClipRectStack.Size > 0); + _ClipRectStack.pop_back(); + UpdateClipRect(); +} + +void ImDrawList::UpdateTextureID() +{ + ImDrawCmd* current_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL; + const ImTextureID texture_id = _TextureIdStack.Size ? _TextureIdStack.back() : NULL; + if (!current_cmd || (current_cmd->ElemCount != 0 && current_cmd->TextureId != texture_id) || current_cmd->UserCallback != NULL) + AddDrawCmd(); + else + current_cmd->TextureId = texture_id; +} + +void ImDrawList::PushTextureID(const ImTextureID& texture_id) +{ + _TextureIdStack.push_back(texture_id); + UpdateTextureID(); +} + +void ImDrawList::PopTextureID() +{ + IM_ASSERT(_TextureIdStack.Size > 0); + _TextureIdStack.pop_back(); + UpdateTextureID(); +} + void ImDrawList::ChannelsSplit(int channels_count) { IM_ASSERT(_ChannelsCurrent == 0 && _ChannelsCount == 1); @@ -195,7 +258,6 @@ void ImDrawList::ChannelsSplit(int channels_count) void ImDrawList::ChannelsMerge() { // Note that we never use or rely on channels.Size because it is merely a buffer that we never shrink back to 0 to keep all sub-buffers ready for use. - // This is why this function takes 'channel_count' as a parameter of how many channels to merge (the user knows) if (_ChannelsCount <= 1) return; @@ -238,69 +300,6 @@ void ImDrawList::ChannelsSetCurrent(int idx) _IdxWritePtr = IdxBuffer.Data + IdxBuffer.Size; } -void ImDrawList::UpdateClipRect() -{ - ImDrawCmd* current_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL; - if (!current_cmd || (current_cmd->ElemCount != 0) || current_cmd->UserCallback != NULL) - { - AddDrawCmd(); - } - else - { - ImVec4 current_clip_rect = _ClipRectStack.Size ? _ClipRectStack.back() : GNullClipRect; - if (CmdBuffer.Size >= 2 && ImLengthSqr(CmdBuffer.Data[CmdBuffer.Size-2].ClipRect - current_clip_rect) < 0.00001f) - CmdBuffer.pop_back(); - else - current_cmd->ClipRect = current_clip_rect; - } -} - -// Scissoring. The values in clip_rect are x1, y1, x2, y2. -void ImDrawList::PushClipRect(const ImVec4& clip_rect) -{ - _ClipRectStack.push_back(clip_rect); - UpdateClipRect(); -} - -void ImDrawList::PushClipRectFullScreen() -{ - PushClipRect(GNullClipRect); - - // This would be more correct but we're not supposed to access ImGuiState from here? - //ImGuiState& g = *GImGui; - //PushClipRect(GetVisibleRect()); -} - -void ImDrawList::PopClipRect() -{ - IM_ASSERT(_ClipRectStack.Size > 0); - _ClipRectStack.pop_back(); - UpdateClipRect(); -} - -void ImDrawList::UpdateTextureID() -{ - ImDrawCmd* current_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL; - const ImTextureID texture_id = _TextureIdStack.Size ? _TextureIdStack.back() : NULL; - if (!current_cmd || (current_cmd->ElemCount != 0 && current_cmd->TextureId != texture_id) || current_cmd->UserCallback != NULL) - AddDrawCmd(); - else - current_cmd->TextureId = texture_id; -} - -void ImDrawList::PushTextureID(const ImTextureID& texture_id) -{ - _TextureIdStack.push_back(texture_id); - UpdateTextureID(); -} - -void ImDrawList::PopTextureID() -{ - IM_ASSERT(_TextureIdStack.Size > 0); - _TextureIdStack.pop_back(); - UpdateTextureID(); -} - // NB: this can be called with negative count for removing primitives (as long as the result does not underflow) void ImDrawList::PrimReserve(int idx_count, int vtx_count) { @@ -357,7 +356,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 const ImVec2 uv = GImGui->FontTexUvWhitePixel; anti_aliased &= GImGui->Style.AntiAliasedLines; - //if (ImGui::GetIO().KeyCtrl) anti_aliased = false; + //if (ImGui::GetIO().KeyCtrl) anti_aliased = false; // Debug int count = points_count; if (!closed) @@ -535,7 +534,7 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun { const ImVec2 uv = GImGui->FontTexUvWhitePixel; anti_aliased &= GImGui->Style.AntiAliasedShapes; - //if (ImGui::GetIO().KeyCtrl) anti_aliased = false; + //if (ImGui::GetIO().KeyCtrl) anti_aliased = false; // Debug if (anti_aliased) { @@ -786,6 +785,7 @@ void ImDrawList::AddTriangleFilled(const ImVec2& a, const ImVec2& b, const ImVec { if ((col >> 24) == 0) return; + PathLineTo(a); PathLineTo(b); PathLineTo(c); @@ -834,7 +834,7 @@ void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, IM_ASSERT(font->ContainerAtlas->TexID == _TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. - // reserve vertices for worse case + // reserve vertices for worse case (over-reserving is useful and easily amortized) const int char_count = (int)(text_end - text_begin); const int vtx_count_max = char_count * 4; const int idx_count_max = char_count * 6; @@ -853,7 +853,7 @@ void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, font->RenderText(font_size, pos, col, clip_rect, text_begin, text_end, this, wrap_width, cpu_fine_clip_rect != NULL); // give back unused vertices - // FIXME-OPT + // FIXME-OPT: clean this up VtxBuffer.resize((int)(_VtxWritePtr - VtxBuffer.Data)); IdxBuffer.resize((int)(_IdxWritePtr - IdxBuffer.Data)); int vtx_unused = vtx_count_max - (VtxBuffer.Size - vtx_begin); @@ -869,7 +869,8 @@ void ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, c { if ((col >> 24) == 0) return; - AddText(ImGui::GetWindowFont(), ImGui::GetWindowFontSize(), pos, col, text_begin, text_end); + + AddText(GImGui->Font, GImGui->FontSize, pos, col, text_begin, text_end); } void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv0, const ImVec2& uv1, ImU32 col) @@ -927,7 +928,7 @@ void ImDrawData::ScaleClipRects(const ImVec2& scale) } //----------------------------------------------------------------------------- -// ImFontAtlias +// ImFontAtlas //----------------------------------------------------------------------------- ImFontConfig::ImFontConfig() @@ -971,14 +972,14 @@ void ImFontAtlas::ClearInputData() ConfigData[i].FontData = NULL; } - // When clearing this we lose access to the font name and other information used to build the font. - for (int i = 0; i < Fonts.Size; i++) - if (Fonts[i]->ConfigData >= ConfigData.Data && Fonts[i]->ConfigData < ConfigData.Data + ConfigData.Size) - { - Fonts[i]->ConfigData = NULL; - Fonts[i]->ConfigDataCount = 0; - } - ConfigData.clear(); + // When clearing this we lose access to the font name and other information used to build the font. + for (int i = 0; i < Fonts.Size; i++) + if (Fonts[i]->ConfigData >= ConfigData.Data && Fonts[i]->ConfigData < ConfigData.Data + ConfigData.Size) + { + Fonts[i]->ConfigData = NULL; + Fonts[i]->ConfigDataCount = 0; + } + ConfigData.clear(); } void ImFontAtlas::ClearTexData() @@ -1010,7 +1011,7 @@ void ImFontAtlas::Clear() void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel) { - // Lazily build + // Build atlas on demand if (TexPixelsAlpha8 == NULL) { if (ConfigData.empty()) @@ -1026,8 +1027,8 @@ void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_wid void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel) { - // Lazily convert to RGBA32 format - // Although it is likely to be the most commonly used format, our font rendering is 8 bpp + // Convert to RGBA32 format on demand + // Although it is likely to be the most commonly used format, our font rendering is 1 channel / 8 bpp if (!TexPixelsRGBA32) { unsigned char* pixels; @@ -1073,7 +1074,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg) return Fonts.back(); } -// Default font ttf is compressed with stb_compress then base85 encoded (see extra_fonts/binary_to_compressed_c.cpp for encoder) +// Default font TTF is compressed with stb_compress then base85 encoded (see extra_fonts/binary_to_compressed_c.cpp for encoder) static unsigned int stb_decompress_length(unsigned char *input); static unsigned int stb_decompress(unsigned char *output, unsigned char *i, unsigned int length); static const char* GetDefaultCompressedFontDataTTFBase85(); @@ -1116,7 +1117,7 @@ ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, return AddFontFromMemoryTTF(data, data_size, size_pixels, &font_cfg, glyph_ranges); } -// Transfer ownership of 'ttf_data' to ImFontAtlas, will be deleted after Build() +// NBM Transfer ownership of 'ttf_data' to ImFontAtlas, unless font_cfg_template->FontDataOwnedByAtlas == false. Owned TTF buffer will be deleted after Build(). ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) { ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); @@ -1183,6 +1184,7 @@ bool ImFontAtlas::Build() if (!stbtt_InitFont(&tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset)) return false; + // Count glyphs if (!cfg.GlyphRanges) cfg.GlyphRanges = GetGlyphRangesDefault(); for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2) @@ -1217,7 +1219,7 @@ bool ImFontAtlas::Build() memset(buf_rects, 0, total_glyph_count * sizeof(stbrp_rect)); // Unnecessary but let's clear this for the sake of sanity. memset(buf_ranges, 0, total_glyph_range_count * sizeof(stbtt_pack_range)); - // First font pass: pack all glyphs (no rendering at this point, we are working with glyph sizes only) + // First font pass: pack all glyphs (no rendering at this point, we are working with rectangles in an infinitely tall texture at this point) for (int input_i = 0; input_i < ConfigData.Size; input_i++) { ImFontConfig& cfg = ConfigData[input_i]; @@ -1355,7 +1357,8 @@ bool ImFontAtlas::Build() void ImFontAtlas::RenderCustomTexData(int pass, void* p_rects) { - // . = white layer, X = black layer, others are blank + // A work of art lies ahead! (. = white layer, X = black layer, others are blank) + // The white texels on the top left are the ones we'll use everywhere in ImGui to render filled shapes. const int TEX_DATA_W = 90; const int TEX_DATA_H = 27; const char texture_data[TEX_DATA_W*TEX_DATA_H+1] = @@ -1411,35 +1414,36 @@ void ImFontAtlas::RenderCustomTexData(int pass, void* p_rects) TexPixelsAlpha8[offset0] = texture_data[n] == '.' ? 0xFF : 0x00; TexPixelsAlpha8[offset1] = texture_data[n] == 'X' ? 0xFF : 0x00; } - const ImVec2 tex_uv_scale(1.0f / TexWidth, 1.0f / TexHeight); - TexUvWhitePixel = ImVec2((r.x + 0.5f) * tex_uv_scale.x, (r.y + 0.5f) * tex_uv_scale.y); + const ImVec2 tex_uv_scale(1.0f / TexWidth, 1.0f / TexHeight); + TexUvWhitePixel = ImVec2((r.x + 0.5f) * tex_uv_scale.x, (r.y + 0.5f) * tex_uv_scale.y); - const ImVec2 cursor_datas[ImGuiMouseCursor_Count_][3] = - { - // Pos ........ Size ......... Offset ...... - { ImVec2(0,3), ImVec2(12,19), ImVec2( 0, 0) }, // ImGuiMouseCursor_Arrow - { ImVec2(13,0), ImVec2(7,16), ImVec2( 4, 8) }, // ImGuiMouseCursor_TextInput - { ImVec2(31,0), ImVec2(23,23), ImVec2(11,11) }, // ImGuiMouseCursor_Move - { ImVec2(21,0), ImVec2( 9,23), ImVec2( 5,11) }, // ImGuiMouseCursor_ResizeNS - { ImVec2(55,18),ImVec2(23, 9), ImVec2(11, 5) }, // ImGuiMouseCursor_ResizeEW - { ImVec2(73,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNESW - { ImVec2(55,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNWSE - }; + // Setup mouse cursors + const ImVec2 cursor_datas[ImGuiMouseCursor_Count_][3] = + { + // Pos ........ Size ......... Offset ...... + { ImVec2(0,3), ImVec2(12,19), ImVec2( 0, 0) }, // ImGuiMouseCursor_Arrow + { ImVec2(13,0), ImVec2(7,16), ImVec2( 4, 8) }, // ImGuiMouseCursor_TextInput + { ImVec2(31,0), ImVec2(23,23), ImVec2(11,11) }, // ImGuiMouseCursor_Move + { ImVec2(21,0), ImVec2( 9,23), ImVec2( 5,11) }, // ImGuiMouseCursor_ResizeNS + { ImVec2(55,18),ImVec2(23, 9), ImVec2(11, 5) }, // ImGuiMouseCursor_ResizeEW + { ImVec2(73,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNESW + { ImVec2(55,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNWSE + }; - for (int type = 0; type < ImGuiMouseCursor_Count_; type++) - { - ImGuiMouseCursorData& cursor_data = GImGui->MouseCursorData[type]; - ImVec2 pos = cursor_datas[type][0] + ImVec2((float)r.x, (float)r.y); - const ImVec2 size = cursor_datas[type][1]; - cursor_data.Type = type; - cursor_data.Size = size; - cursor_data.HotOffset = cursor_datas[type][2]; - cursor_data.TexUvMin[0] = (pos) * tex_uv_scale; - cursor_data.TexUvMax[0] = (pos + size) * tex_uv_scale; - pos.x += TEX_DATA_W+1; - cursor_data.TexUvMin[1] = (pos) * tex_uv_scale; - cursor_data.TexUvMax[1] = (pos + size) * tex_uv_scale; - } + for (int type = 0; type < ImGuiMouseCursor_Count_; type++) + { + ImGuiMouseCursorData& cursor_data = GImGui->MouseCursorData[type]; + ImVec2 pos = cursor_datas[type][0] + ImVec2((float)r.x, (float)r.y); + const ImVec2 size = cursor_datas[type][1]; + cursor_data.Type = type; + cursor_data.Size = size; + cursor_data.HotOffset = cursor_datas[type][2]; + cursor_data.TexUvMin[0] = (pos) * tex_uv_scale; + cursor_data.TexUvMax[0] = (pos + size) * tex_uv_scale; + pos.x += TEX_DATA_W+1; + cursor_data.TexUvMin[1] = (pos) * tex_uv_scale; + cursor_data.TexUvMax[1] = (pos + size) * tex_uv_scale; + } } } @@ -1603,7 +1607,7 @@ void ImFont::BuildLookupTable() } // Create a glyph to handle TAB - // FIXME: Needs proper TAB handling but it needs to be contextualized (can arbitrary say that each string starts at "column 0" + // FIXME: Needs proper TAB handling but it needs to be contextualized (or we could arbitrary say that each string starts at "column 0" ?) if (FindGlyph((unsigned short)' ')) { if (Glyphs.back().Codepoint != '\t') // So we can call this function multiple times @@ -1783,7 +1787,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons } } - // Decode and advance source (handle unlikely UTF-8 decoding failure by skipping to the next byte) + // Decode and advance source const char* prev_s = s; unsigned int c = (unsigned int)*s; if (c < 0x80) @@ -1886,7 +1890,7 @@ void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_re } } - // Decode and advance source (handle unlikely UTF-8 decoding failure by skipping to the next byte) + // Decode and advance source unsigned int c = (unsigned int)*s; if (c < 0x80) { @@ -1925,7 +1929,7 @@ void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_re // Clipping on Y is more likely if (c != ' ' && c != '\t') { - // We don't do a second finer clipping test on the Y axis (todo: do some measurement see if it is worth it, probably not) + // We don't do a second finer clipping test on the Y axis (TODO: do some measurement see if it is worth it, probably not) float y1 = (float)(y + glyph->Y0 * scale); float y2 = (float)(y + glyph->Y1 * scale); @@ -1939,7 +1943,7 @@ void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_re float u2 = glyph->U1; float v2 = glyph->V1; - // CPU side clipping used to fit text in their frame when the frame is too small. Only does clipping for axis aligned quads + // CPU side clipping used to fit text in their frame when the frame is too small. Only does clipping for axis aligned quads. if (cpu_fine_clip) { if (x1 < clip_rect.x)