mirror of
https://github.com/ocornut/imgui.git
synced 2026-01-09 23:54:20 +00:00
Fonts: rename internal fields for consistency.
This commit is contained in:
parent
149587b85b
commit
db577cd445
4 changed files with 34 additions and 34 deletions
10
imgui.cpp
10
imgui.cpp
|
|
@ -7804,7 +7804,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
|
|||
|
||||
// Setup draw list and outer clipping rectangle
|
||||
IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0);
|
||||
window->DrawList->PushTexture(g.Font->ContainerAtlas->TexRef);
|
||||
window->DrawList->PushTexture(g.Font->OwnerAtlas->TexRef);
|
||||
PushClipRect(host_rect.Min, host_rect.Max, false);
|
||||
|
||||
// Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71)
|
||||
|
|
@ -8906,7 +8906,7 @@ void ImGui::SetCurrentFont(ImFont* font, float font_size_before_scaling, float f
|
|||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
IM_ASSERT(font->Scale > 0.0f);
|
||||
#endif
|
||||
ImFontAtlas* atlas = font->ContainerAtlas;
|
||||
ImFontAtlas* atlas = font->OwnerAtlas;
|
||||
g.DrawListSharedData.FontAtlas = atlas;
|
||||
g.DrawListSharedData.Font = font;
|
||||
ImFontAtlasUpdateDrawListsSharedData(atlas);
|
||||
|
|
@ -17083,7 +17083,7 @@ void ImGui::DebugNodeFont(ImFont* font)
|
|||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
|
||||
ImFontAtlas* atlas = font->ContainerAtlas;
|
||||
ImFontAtlas* atlas = font->OwnerAtlas;
|
||||
bool opened = TreeNode(font, "Font: \"%s\": %d sources(s)", font->GetDebugName(), font->Sources.Size);
|
||||
|
||||
// Display preview text
|
||||
|
|
@ -17198,7 +17198,7 @@ void ImGui::DebugNodeFont(ImFont* font)
|
|||
for (int baked_n = 0; baked_n < atlas->Builder->BakedPool.Size; baked_n++)
|
||||
{
|
||||
ImFontBaked* baked = &atlas->Builder->BakedPool[baked_n];
|
||||
if (baked->ContainerFont != font)
|
||||
if (baked->OwnerFont != font)
|
||||
continue;
|
||||
PushID(baked_n);
|
||||
if (TreeNode("Glyphs", "Baked at { %.2fpx, d.%.2f }: %d glyphs%s", baked->Size, baked->RasterizerDensity, baked->Glyphs.Size, (baked->LastUsedFrame < atlas->Builder->FrameCount - 1) ? " *Unused*" : ""))
|
||||
|
|
@ -17289,7 +17289,7 @@ void ImGui::DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph)
|
|||
Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1);
|
||||
if (glyph->PackId >= 0)
|
||||
{
|
||||
ImTextureRect* r = ImFontAtlasPackGetRect(font->ContainerAtlas, glyph->PackId);
|
||||
ImTextureRect* r = ImFontAtlasPackGetRect(font->OwnerAtlas, glyph->PackId);
|
||||
Text("PackId: 0x%X (%dx%d rect at %d,%d)", glyph->PackId, r->w, r->h, r->x, r->y);
|
||||
}
|
||||
Text("SourceIdx: %d", glyph->SourceIdx);
|
||||
|
|
|
|||
10
imgui.h
10
imgui.h
|
|
@ -3513,7 +3513,7 @@ struct ImFontConfig
|
|||
char Name[40]; // <auto> // Name (strictly to ease debugging, hence limited size buffer)
|
||||
void* FontData; // // TTF/OTF data
|
||||
int FontDataSize; // // TTF/OTF data size
|
||||
bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the container ImFontAtlas (will delete memory itself).
|
||||
bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the owner ImFontAtlas (will delete memory itself).
|
||||
|
||||
// Options
|
||||
bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights.
|
||||
|
|
@ -3792,7 +3792,7 @@ struct ImFontBaked
|
|||
unsigned int LoadNoRenderOnLayout:1;// 0 // // Enable a two-steps mode where CalcTextSize() calls will load AdvanceX *without* rendering/packing glyphs. Only advantagous if you know that the glyph is unlikely to actually be rendered, otherwise it is slower because we'd do one query on the first CalcTextSize and one query on the first Draw.
|
||||
int LastUsedFrame; // 4 // // Record of that time this was bounds
|
||||
ImGuiID BakedId; // 4 // // Unique ID for this baked storage
|
||||
ImFont* ContainerFont; // 4-8 // in // Parent font
|
||||
ImFont* OwnerFont; // 4-8 // in // Parent font
|
||||
void* FontLoaderDatas; // 4-8 // // Font loader opaque storage (per baked font * sources): single contiguous buffer allocated by imgui, passed to loader.
|
||||
|
||||
// Functions
|
||||
|
|
@ -3823,7 +3823,7 @@ struct ImFont
|
|||
{
|
||||
// [Internal] Members: Hot ~12-20 bytes
|
||||
ImFontBaked* LastBaked; // 4-8 // Cache last bound baked. NEVER USE DIRECTLY. Use GetFontBaked().
|
||||
ImFontAtlas* ContainerAtlas; // 4-8 // What we have been loaded into.
|
||||
ImFontAtlas* OwnerAtlas; // 4-8 // What we have been loaded into.
|
||||
ImFontFlags Flags; // 4 // Font flags.
|
||||
float CurrentRasterizerDensity; // Current rasterizer density. This is a varying state of the font.
|
||||
|
||||
|
|
@ -3831,7 +3831,7 @@ struct ImFont
|
|||
// Conceptually Sources[] is the list of font sources merged to create this font.
|
||||
ImGuiID FontId; // Unique identifier for the font
|
||||
float LegacySize; // 4 // in // Font size passed to AddFont(). Use for old code calling PushFont() expecting to use that size. (use ImGui::GetFontBaked() to get font baked at current bound size).
|
||||
ImVector<ImFontConfig*> Sources; // 16 // in // List of sources. Pointers within ContainerAtlas->Sources[]
|
||||
ImVector<ImFontConfig*> Sources; // 16 // in // List of sources. Pointers within OwnerAtlas->Sources[]
|
||||
ImWchar EllipsisChar; // 2-4 // out // Character used for ellipsis rendering ('...').
|
||||
ImWchar FallbackChar; // 2-4 // out // Character used if a glyph isn't found (U+FFFD, '?')
|
||||
ImU8 Used8kPagesMap[(IM_UNICODE_CODEPOINT_MAX+1)/8192/8]; // 1 bytes if ImWchar=ImWchar16, 16 bytes if ImWchar==ImWchar32. Store 1-bit for each block of 4K codepoints that has one active glyph. This is mainly used to facilitate iterations across all used codepoints.
|
||||
|
|
@ -3845,7 +3845,7 @@ struct ImFont
|
|||
IMGUI_API ImFont();
|
||||
IMGUI_API ~ImFont();
|
||||
IMGUI_API bool IsGlyphInFont(ImWchar c);
|
||||
bool IsLoaded() const { return ContainerAtlas != NULL; }
|
||||
bool IsLoaded() const { return OwnerAtlas != NULL; }
|
||||
const char* GetDebugName() const { return Sources.Size ? Sources[0]->Name : "<unknown>"; } // Fill ImFontConfig::Name.
|
||||
|
||||
// [Internal] Don't use!
|
||||
|
|
|
|||
|
|
@ -824,7 +824,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
|
|||
const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTex) && (integer_thickness < IM_DRAWLIST_TEX_LINES_WIDTH_MAX) && (fractional_thickness <= 0.00001f) && (AA_SIZE == 1.0f);
|
||||
|
||||
// We should never hit this, because NewFrame() doesn't set ImDrawListFlags_AntiAliasedLinesUseTex unless ImFontAtlasFlags_NoBakedLines is off
|
||||
IM_ASSERT_PARANOID(!use_texture || !(_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines));
|
||||
IM_ASSERT_PARANOID(!use_texture || !(_Data->Font->OwnerAtlas->Flags & ImFontAtlasFlags_NoBakedLines));
|
||||
|
||||
const int idx_count = use_texture ? (count * 6) : (thick_line ? count * 18 : count * 12);
|
||||
const int vtx_count = use_texture ? (points_count * 2) : (thick_line ? points_count * 4 : points_count * 3);
|
||||
|
|
@ -3242,7 +3242,7 @@ void ImFontAtlas::RemoveFont(ImFont* font)
|
|||
|
||||
ImFontAtlasBuildUpdatePointers(this);
|
||||
|
||||
font->ContainerAtlas = NULL;
|
||||
font->OwnerAtlas = NULL;
|
||||
IM_DELETE(font);
|
||||
|
||||
// Notify external systems
|
||||
|
|
@ -3620,7 +3620,7 @@ void ImFontAtlasFontSourceAddToFont(ImFontAtlas* atlas, ImFont* font, ImFontConf
|
|||
{
|
||||
font->ClearOutputData();
|
||||
//font->FontSize = src->SizePixels;
|
||||
font->ContainerAtlas = atlas;
|
||||
font->OwnerAtlas = atlas;
|
||||
IM_ASSERT(font->Sources[0] == src);
|
||||
}
|
||||
atlas->TexIsBuilt = false; // For legacy backends
|
||||
|
|
@ -3643,7 +3643,7 @@ void ImFontAtlasFontDestroySourceData(ImFontAtlas* atlas, ImFontConfig* src)
|
|||
// FIXME-NEWATLAS: This borrows too much from FontLoader's FontLoadGlyph() handlers and suggest that we should add further helpers.
|
||||
static ImFontGlyph* ImFontAtlasBuildSetupFontBakedEllipsis(ImFontAtlas* atlas, ImFontBaked* baked)
|
||||
{
|
||||
ImFont* font = baked->ContainerFont;
|
||||
ImFont* font = baked->OwnerFont;
|
||||
IM_ASSERT(font->EllipsisChar != 0);
|
||||
|
||||
const ImFontGlyph* dot_glyph = baked->FindGlyphNoFallback((ImWchar)'.');
|
||||
|
|
@ -3689,7 +3689,7 @@ static void ImFontAtlasBuildSetupFontBakedFallback(ImFontBaked* baked)
|
|||
{
|
||||
IM_ASSERT(baked->FallbackGlyphIndex == -1);
|
||||
IM_ASSERT(baked->FallbackAdvanceX == 0.0f);
|
||||
ImFont* font = baked->ContainerFont;
|
||||
ImFont* font = baked->OwnerFont;
|
||||
ImFontGlyph* fallback_glyph = NULL;
|
||||
if (font->FallbackChar != 0)
|
||||
fallback_glyph = baked->FindGlyphNoFallback(font->FallbackChar);
|
||||
|
|
@ -3699,7 +3699,7 @@ static void ImFontAtlasBuildSetupFontBakedFallback(ImFontBaked* baked)
|
|||
ImFontGlyph glyph;
|
||||
glyph.Codepoint = 0;
|
||||
glyph.AdvanceX = space_glyph ? space_glyph->AdvanceX : IM_ROUND(baked->Size * 0.40f);
|
||||
fallback_glyph = ImFontAtlasBakedAddFontGlyph(font->ContainerAtlas, baked, NULL, &glyph);
|
||||
fallback_glyph = ImFontAtlasBakedAddFontGlyph(font->OwnerAtlas, baked, NULL, &glyph);
|
||||
}
|
||||
baked->FallbackGlyphIndex = baked->Glyphs.index_from_ptr(fallback_glyph); // Storing index avoid need to update pointer on growth and simplify inner loop code
|
||||
baked->FallbackAdvanceX = fallback_glyph->AdvanceX;
|
||||
|
|
@ -3780,7 +3780,7 @@ ImFontBaked* ImFontAtlasBakedAdd(ImFontAtlas* atlas, ImFont* font, float font_si
|
|||
baked->Size = font_size;
|
||||
baked->RasterizerDensity = font_rasterizer_density;
|
||||
baked->BakedId = baked_id;
|
||||
baked->ContainerFont = font;
|
||||
baked->OwnerFont = font;
|
||||
baked->LastUsedFrame = atlas->Builder->FrameCount;
|
||||
|
||||
// Initialize backend data
|
||||
|
|
@ -3815,7 +3815,7 @@ ImFontBaked* ImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas, ImFont* font, f
|
|||
for (int baked_n = 0; baked_n < builder->BakedPool.Size; baked_n++)
|
||||
{
|
||||
ImFontBaked* baked = &builder->BakedPool[baked_n];
|
||||
if (baked->ContainerFont != font || baked->WantDestroy)
|
||||
if (baked->OwnerFont != font || baked->WantDestroy)
|
||||
continue;
|
||||
if (step_n == 0 && baked->RasterizerDensity != font_rasterizer_density) // First try with same density
|
||||
continue;
|
||||
|
|
@ -3871,7 +3871,7 @@ void ImFontAtlasFontDiscardBakes(ImFontAtlas* atlas, ImFont* font, int unused_fr
|
|||
ImFontBaked* baked = &builder->BakedPool[baked_n];
|
||||
if (baked->LastUsedFrame + unused_frames > atlas->Builder->FrameCount)
|
||||
continue;
|
||||
if (baked->ContainerFont != font || baked->WantDestroy)
|
||||
if (baked->OwnerFont != font || baked->WantDestroy)
|
||||
continue;
|
||||
ImFontAtlasBakedDiscard(atlas, font, baked);
|
||||
}
|
||||
|
|
@ -3886,9 +3886,9 @@ void ImFontAtlasBuildDiscardBakes(ImFontAtlas* atlas, int unused_frames)
|
|||
ImFontBaked* baked = &builder->BakedPool[baked_n];
|
||||
if (baked->LastUsedFrame + unused_frames > atlas->Builder->FrameCount)
|
||||
continue;
|
||||
if (baked->WantDestroy || (baked->ContainerFont->Flags & ImFontFlags_LockBakedSizes))
|
||||
if (baked->WantDestroy || (baked->OwnerFont->Flags & ImFontFlags_LockBakedSizes))
|
||||
continue;
|
||||
ImFontAtlasBakedDiscard(atlas, baked->ContainerFont, baked);
|
||||
ImFontAtlasBakedDiscard(atlas, baked->OwnerFont, baked);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4421,8 +4421,8 @@ static void ImFontAtlas_FontHookRemapCodepoint(ImFontAtlas* atlas, ImFont* font,
|
|||
|
||||
static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codepoint, float* only_load_advance_x)
|
||||
{
|
||||
ImFont* font = baked->ContainerFont;
|
||||
ImFontAtlas* atlas = font->ContainerAtlas;
|
||||
ImFont* font = baked->OwnerFont;
|
||||
ImFontAtlas* atlas = font->OwnerAtlas;
|
||||
if (atlas->Locked || (font->Flags & ImFontFlags_NoLoadGlyphs))
|
||||
{
|
||||
// Lazily load fallback glyph
|
||||
|
|
@ -4696,7 +4696,7 @@ static bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontC
|
|||
stbtt_MakeGlyphBitmapSubpixelPrefilter(&bd_font_data->FontInfo, bitmap_pixels, w, h, w,
|
||||
scale_for_raster_x, scale_for_raster_y, 0, 0, oversample_h, oversample_v, &sub_x, &sub_y, glyph_index);
|
||||
|
||||
const float ref_size = baked->ContainerFont->Sources[0]->SizePixels;
|
||||
const float ref_size = baked->OwnerFont->Sources[0]->SizePixels;
|
||||
const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f;
|
||||
float font_off_x = (src->GlyphOffset.x * offsets_scale);
|
||||
float font_off_y = (src->GlyphOffset.y * offsets_scale);
|
||||
|
|
@ -5093,7 +5093,7 @@ ImFont::~ImFont()
|
|||
|
||||
void ImFont::ClearOutputData()
|
||||
{
|
||||
if (ImFontAtlas* atlas = ContainerAtlas)
|
||||
if (ImFontAtlas* atlas = OwnerAtlas)
|
||||
ImFontAtlasFontDiscardBakes(atlas, this, 0);
|
||||
FallbackChar = EllipsisChar = 0;
|
||||
memset(Used8kPagesMap, 0, sizeof(Used8kPagesMap));
|
||||
|
|
@ -5138,7 +5138,7 @@ ImFontGlyph* ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked
|
|||
if (src != NULL)
|
||||
{
|
||||
// Clamp & recenter if needed
|
||||
const float ref_size = baked->ContainerFont->Sources[0]->SizePixels;
|
||||
const float ref_size = baked->OwnerFont->Sources[0]->SizePixels;
|
||||
const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f;
|
||||
float advance_x = ImClamp(glyph->AdvanceX, src->GlyphMinAdvanceX * offsets_scale, src->GlyphMaxAdvanceX * offsets_scale);
|
||||
if (advance_x != glyph->AdvanceX)
|
||||
|
|
@ -5164,7 +5164,7 @@ ImFontGlyph* ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked
|
|||
baked->IndexAdvanceX[codepoint] = glyph->AdvanceX;
|
||||
baked->IndexLookup[codepoint] = (ImU16)glyph_idx;
|
||||
const int page_n = codepoint / 8192;
|
||||
baked->ContainerFont->Used8kPagesMap[page_n >> 3] |= 1 << (page_n & 7);
|
||||
baked->OwnerFont->Used8kPagesMap[page_n >> 3] |= 1 << (page_n & 7);
|
||||
|
||||
return glyph;
|
||||
}
|
||||
|
|
@ -5176,7 +5176,7 @@ void ImFontAtlasBakedAddFontGlyphAdvancedX(ImFontAtlas* atlas, ImFontBaked* bake
|
|||
if (src != NULL)
|
||||
{
|
||||
// Clamp & recenter if needed
|
||||
const float ref_size = baked->ContainerFont->Sources[0]->SizePixels;
|
||||
const float ref_size = baked->OwnerFont->Sources[0]->SizePixels;
|
||||
const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f;
|
||||
advance_x = ImClamp(advance_x, src->GlyphMinAdvanceX * offsets_scale, src->GlyphMaxAdvanceX * offsets_scale);
|
||||
|
||||
|
|
@ -5198,7 +5198,7 @@ void ImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas, ImFontBaked* baked,
|
|||
ImTextureData* tex = atlas->TexData;
|
||||
IM_ASSERT(r->x + r->w <= tex->Width && r->y + r->h <= tex->Height);
|
||||
ImFontAtlasTextureBlockConvert(src_pixels, src_fmt, src_pitch, (unsigned char*)tex->GetPixelsAt(r->x, r->y), tex->Format, tex->GetPitch(), r->w, r->h);
|
||||
ImFontAtlasPostProcessData pp_data = { atlas, baked->ContainerFont, src, baked, glyph, tex->GetPixelsAt(r->x, r->y), tex->Format, tex->GetPitch(), r->w, r->h };
|
||||
ImFontAtlasPostProcessData pp_data = { atlas, baked->OwnerFont, src, baked, glyph, tex->GetPixelsAt(r->x, r->y), tex->Format, tex->GetPitch(), r->w, r->h };
|
||||
ImFontAtlasTextureBlockPostProcess(&pp_data);
|
||||
ImFontAtlasTextureBlockQueueUpload(atlas, tex, r->x, r->y, r->w, r->h);
|
||||
}
|
||||
|
|
@ -5256,7 +5256,7 @@ bool ImFontBaked::IsGlyphLoaded(ImWchar c)
|
|||
// This is not fast query
|
||||
bool ImFont::IsGlyphInFont(ImWchar c)
|
||||
{
|
||||
ImFontAtlas* atlas = ContainerAtlas;
|
||||
ImFontAtlas* atlas = OwnerAtlas;
|
||||
ImFontAtlas_FontHookRemapCodepoint(atlas, this, &c);
|
||||
for (ImFontConfig* src : Sources)
|
||||
{
|
||||
|
|
@ -5305,7 +5305,7 @@ ImFontBaked* ImFont::GetFontBaked(float size, float density)
|
|||
if (baked && baked->Size == size && baked->RasterizerDensity == density)
|
||||
return baked;
|
||||
|
||||
ImFontAtlas* atlas = ContainerAtlas;
|
||||
ImFontAtlas* atlas = OwnerAtlas;
|
||||
ImFontAtlasBuilder* builder = atlas->Builder;
|
||||
baked = ImFontAtlasBakedGetOrAdd(atlas, this, size, density);
|
||||
if (baked == NULL)
|
||||
|
|
@ -5326,7 +5326,7 @@ ImFontBaked* ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float fo
|
|||
ImFontBaked* baked = *p_baked_in_map;
|
||||
if (baked != NULL)
|
||||
{
|
||||
IM_ASSERT(baked->Size == font_size && baked->ContainerFont == font && baked->BakedId == baked_id);
|
||||
IM_ASSERT(baked->Size == font_size && baked->OwnerFont == font && baked->BakedId == baked_id);
|
||||
return baked;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -423,7 +423,7 @@ static bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* s
|
|||
IM_UNUSED(atlas);
|
||||
float size = baked->Size;
|
||||
if (src->MergeMode && src->SizePixels != 0.0f)
|
||||
size *= (src->SizePixels / baked->ContainerFont->Sources[0]->SizePixels);
|
||||
size *= (src->SizePixels / baked->OwnerFont->Sources[0]->SizePixels);
|
||||
|
||||
ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData;
|
||||
bd_font_data->BakedLastActivated = baked;
|
||||
|
|
@ -539,7 +539,7 @@ static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConf
|
|||
uint32_t* temp_buffer = (uint32_t*)atlas->Builder->TempBuffer.Data;
|
||||
ImGui_ImplFreeType_BlitGlyph(ft_bitmap, temp_buffer, w);
|
||||
|
||||
const float ref_size = baked->ContainerFont->Sources[0]->SizePixels;
|
||||
const float ref_size = baked->OwnerFont->Sources[0]->SizePixels;
|
||||
const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f;
|
||||
float font_off_x = (src->GlyphOffset.x * offsets_scale);
|
||||
float font_off_y = (src->GlyphOffset.y * offsets_scale) + baked->Ascent;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue