From dc6bd89497fd68d4be015eb973cbf3ec5ff1a839 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Tue, 21 Dec 2021 12:01:48 +0900 Subject: [PATCH] expanding diacritics types to correctly implement thai diacritics --- CONTRIBUTING.md | 65 ++++++++---------- assets/ascii_variable.tga | 2 +- assets/diacritical_marks_variable.tga | Bin 143378 -> 131 bytes assets/ipa_ext_variable.tga | 2 +- assets/thai_variable.tga | 2 +- .../torvald/terrarumsansbitmap/GlyphProps.kt | 4 +- .../gdx/TerrarumSansBitmap.kt | 36 ++++++---- testing.PNG | Bin 19149 -> 14091 bytes testtext.txt | 16 +---- work_files/ascii_variable.psd | 4 +- work_files/diacritical_marks_variable.psd | Bin 132187 -> 131 bytes work_files/ipa_ext_variable.psd | 4 +- work_files/thai_variable.psd | 4 +- 13 files changed, 67 insertions(+), 72 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c846d38..c99529e 100755 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,7 +7,7 @@ Font Spritesheets are stored in ```assets/graphics/fonts``` directory. Image for #### Before getting started, you did read our design goals, right? Good. Now you may continue your awesome work. -### Ascenders, descenders, width informations (aka Glyph Tags) +## Ascenders, descenders, width informations (aka Glyph Tags) ![Alas, use more modern browser or get better internet connexion!](glyph_height_pos_annotation.png) @@ -26,11 +26,11 @@ Each cell is 16 px wide, and any glyph you draw **must be contained within lefts -### Font Metrics +## Font Metrics for variable-width font sheets Although the font is basically a Spritesheet, some of the sheet expects variable widths to be supported. Any sheets with ```_variable``` means it expects variable widths. Anything else expects fixed width (regular Spritesheet behaviour). ```cjkpunct``` has width of 10, ```kana``` and ```hangul_johab``` has width of 12, ```wenquanyi``` has width of 16. -### Parsing glyph widths for variable font sheets +### Parsing Glyph Tags ![Sample of Font Spritesheet with annotation](width_bit_encoding_annotated.png) @@ -61,26 +61,28 @@ Rightmost vertical column (should be 20 px tall) contains the tags. Tags are def D --Diacritics Type Bit (see below; not all diacritics are marked as one on the spritesheet) S -,_ 0 Stack 1 Stack 0 Before 1 Up & (MSB) S -' 0 up 0 down 1 &After 1 Down (e.g. U+0C48) +``` - -* Nudging Bits Encoding: +#### Nudging Bits Encoding SXXXXXXX SYYYYYYY 00000000 Each X and Y numbers are Signed 8-Bit Integer. -X-positive: nudges towards left -Y-positive: nudges towards up -* Diacritics Anchor Point Encoding: +X-positive: nudges towards left +Y-positive: nudges towards up + +#### Diacritics Anchor Point Encoding 4 Pixels are further divided as follows: -(LSB) -Y - Anchor point Y for undefined, undefined, undefined -X - Anchor point X for undefined, undefined, undefined -Y - Anchor point Y for (unused), undefined, undefined -X - Anchor point X for Type-0 (centre-aligned) diacritics, undefined, undefined -(MSB) +| LSB | | | | | +| ------------ | ------------ | ------------ | ------------ | ------------ | +| Y | Anchor point Y for: | undefined | undefined | undefined | +| X | Anchor point X for: | undefined | undefined | undefined | +| Y | Anchor point Y for: | (unused) | (unused) | (unused) | +| X | Anchor point X for: | Type-0 | Type-1 | Type-2 | +| MSB | | | | | 1Y1Y1Y1Y 1Y2Y2Y2Y 1Y3Y3Y3Y 1X1X1X1X 1X2X2X2X 1X3X3X3X @@ -89,38 +91,31 @@ where Red is first, Green is second, Blue is the third diacritics. MSB for each word must be set so that the pixel would appear brighter on the image editor. (the font program will only read low 7 bits for each RGB channel) -* Diacritics Type Bit Encoding: +#### Diacritics Type Bit Encoding FFFFFFFF FFFFFFFF FFFFFFFF (For Type-0) TTTT0000 00000000 00000000 (For Type-1 to Type-15) -Right now, only the type-0 diacritics and its anchor point is used by the font. +Certain types of diacritics have predefined meanings: -* Compiler Directives: +* Type-0: Above +* Type-1: Below (when it should be separated from being above) +* Type-2: Overlaid (will shift down 2 pixels for lowheight glyphs instead of the default of 4 pixels) + + +#### Compiler Directives [Opcode] [arg1] [arg2] Currently supported opcodes: - - 00000000: No-operation; does not use the Compiler Directive system. +*00000000: No-operation; does not use the Compiler Directive system. - - 10000xxx: Replace a character with xxx subchars (yes, number 0 can be used). - Replacement characters are encoded vertically from X-zero, bit by bit - (colour of the pixel doesn't matter) with LSB sitting on Y-zero. +*10000xxx: Replace a character with xxx subchars (yes, number 0 can be used). + Replacement characters are encoded vertically from X-zero, bit by bit + (colour of the pixel doesn't matter) with LSB sitting on Y-zero. - --= NOTE =- - -The code has remnants of one old HACK: using 0th diacritics' X-anchor pos as a type selector -This hack applied only when write-on-top bit is set. Relevant codes are currently commented out instead of -being completely removed for future debugging. -Interpretation: - DIA_OVERLAY = 1 - DIA_JOINER = 2 - -``` - #### Stack Up/Down When the tag is stack-up, it'll be drawn 4 px lower if the underlying @@ -157,13 +152,13 @@ Also note that the font compiler will not "stack" these diacritics. (fun fact: it was drawn on Rhodia memopad with Lamy 2000, then photographed and edited on my iPhone. Letter used is a Cherokee WE Ꮺ) -### Technical Limitations +## Technical Limitations - Each spritesheet is 4096x4096 maximum, which is a size of 4K Texture. However it is recommended to be smaller or equal to 1024x1024. - Glyphs exceeding 15px of width needs to be broken down with 2 or more characters. Wider sheets WILL NOT BE IMPLEMENTED, can't waste much pixels just for few superwide glyphs. - Due to how the compiler is coded, actual glyph must have alpha value of 255, the tags must have alpha values LESS THAN 255 (and obviously greater than zero). RGB plane of the TGA image doesn't do anything, keep it as #FFFFFF white. -### Implementing the Korean writing system +## Implementation of the Korean writing system On this font, Hangul letters are printed by assemblying two or three letter pieces. There are 10 sets of Hangul letter pieces on the font. Top 6 are initials, middle 2 are medials, and bottom 2 are finals. On the rightmost side, there's eight assembled glyphs to help you with (assuming you have basic knowledge on the writing system). Top 6 tells you how to use 6 initials, and bottom 2 tells you how to use 2 finals. diff --git a/assets/ascii_variable.tga b/assets/ascii_variable.tga index d7a0dfe..4b0d226 100755 --- a/assets/ascii_variable.tga +++ b/assets/ascii_variable.tga @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:677781e28cfb0892fda6902e47477fd7c5495e931010b0109079906e806d4232 +oid sha256:3491d4b10b0341e1a8b875162381694e587dff4d2e8f0cb6a95da479c8956d41 size 327698 diff --git a/assets/diacritical_marks_variable.tga b/assets/diacritical_marks_variable.tga index 36d06d9d34f848574438b4467e2ef0770c954411..c9542a30334dd666cea99659dee59c359a737d18 100755 GIT binary patch literal 131 zcmWN?%MrpL5CG6SRnUNe1$H6nhOaQAl5sHytJimVSAFxCFWJ{R`B3UU*6q=F`` Nis13B5}dU@h#%PTCUF1& literal 143378 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 T^inS7ApijgKmY>2mB9Z2?^6S= diff --git a/assets/ipa_ext_variable.tga b/assets/ipa_ext_variable.tga index caa8b61..4ce33de 100755 --- a/assets/ipa_ext_variable.tga +++ b/assets/ipa_ext_variable.tga @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3aee9600478b6d531ea5a43590d002bc1ecdec600ecc325e997032b51c4214da +oid sha256:4f76c223c431d3f8da64a76aa4d0b77e4f2b4c0895e593f52615baa2add6079f size 225298 diff --git a/assets/thai_variable.tga b/assets/thai_variable.tga index 065fbc9..4783b52 100755 --- a/assets/thai_variable.tga +++ b/assets/thai_variable.tga @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a4794221096551311bdddd5b11167c52401dec48ea1f629300ad39f40eccbc82 +oid sha256:134e9efce4e298b623bb3f977c5a8d65d05bdc3924e4be0564cebb4683ee5692 size 122898 diff --git a/src/net/torvald/terrarumsansbitmap/GlyphProps.kt b/src/net/torvald/terrarumsansbitmap/GlyphProps.kt index 14ce5cc..11a0b24 100755 --- a/src/net/torvald/terrarumsansbitmap/GlyphProps.kt +++ b/src/net/torvald/terrarumsansbitmap/GlyphProps.kt @@ -46,8 +46,8 @@ data class GlyphProps( const val STACK_BEFORE_N_AFTER = 2 const val STACK_UP_N_DOWN = 3 - const val DIA_OVERLAY = 1 - const val DIA_JOINER = 2 + const val DIA_OVERLAY = 2 +// const val DIA_JOINER = 2 private fun Boolean.toInt() = if (this) 1 else 0 } diff --git a/src/net/torvald/terrarumsansbitmap/gdx/TerrarumSansBitmap.kt b/src/net/torvald/terrarumsansbitmap/gdx/TerrarumSansBitmap.kt index 7010233..6aa5fbf 100755 --- a/src/net/torvald/terrarumsansbitmap/gdx/TerrarumSansBitmap.kt +++ b/src/net/torvald/terrarumsansbitmap/gdx/TerrarumSansBitmap.kt @@ -739,14 +739,14 @@ class TerrarumSansBitmap( val nudgeY = nudgingBits.ushr(16).toByte().toInt() // signed 8-bit int val diacriticsAnchors = (0..5).map { - val yPos = 11 + (it / 3) * 2 + val yPos = 13 - (it / 3) * 2 val shift = (3 - (it % 3)) * 8 val yPixel = pixmap.getPixel(codeStartX, codeStartY + yPos).tagify() val xPixel = pixmap.getPixel(codeStartX, codeStartY + yPos + 1).tagify() - val y = (yPixel ushr shift) and 127 - val x = (xPixel ushr shift) and 127 - val yUsed = (yPixel ushr shift) >= 128 - val xUsed = (yPixel ushr shift) >= 128 + val yUsed = (yPixel ushr shift) and 128 != 0 + val xUsed = (xPixel ushr shift) and 128 != 0 + val y = if (yUsed) (yPixel ushr shift) and 127 else 0 + val x = if (xUsed) (xPixel ushr shift) and 127 else 0 DiacriticsAnchor(it, x, y, xUsed, yUsed) }.toTypedArray() @@ -754,18 +754,22 @@ class TerrarumSansBitmap( val alignWhere = (0..1).fold(0) { acc, y -> acc or ((pixmap.getPixel(codeStartX, codeStartY + y + 15).and(255) != 0).toInt() shl y) } var writeOnTop = pixmap.getPixel(codeStartX, codeStartY + 17) // NO .tagify() - if (writeOnTop and 255 == 0) writeOnTop = -1 + if (writeOnTop and 255 == 0) writeOnTop = -1 // look for the alpha channel else { - writeOnTop = writeOnTop.ushr(8) - if (writeOnTop == 0xFFFFFF) writeOnTop = 0 + if (writeOnTop.ushr(8) == 0xFFFFFF) writeOnTop = 0 + else writeOnTop = writeOnTop.ushr(28) and 15 } val stackWhere = (0..1).fold(0) { acc, y -> acc or ((pixmap.getPixel(codeStartX, codeStartY + y + 18).and(255) != 0).toInt() shl y) } glyphProps[code] = GlyphProps(width, isLowHeight, nudgeX, nudgeY, diacriticsAnchors, alignWhere, writeOnTop, stackWhere, IntArray(15), hasKernData, isKernYtype, kerningMask, directiveOpcode, directiveArg1, directiveArg2) + + // Debug prints // // if (nudgingBits != 0) dbgprn("${code.charInfo()} nudgeX=$nudgeX, nudgeY=$nudgeY, nudgingBits=0x${nudgingBits.toString(16)}") // if (writeOnTop >= 0) dbgprn("WriteOnTop: ${code.charInfo()} (Type-${writeOnTop})") +// if (diacriticsAnchors.any { it.xUsed || it.yUsed }) dbgprn("${code.charInfo()} ${diacriticsAnchors.filter { it.xUsed || it.yUsed }.joinToString()}") + // extra info val extCount = glyphProps[code]?.requiredExtInfoCount() ?: 0 @@ -834,7 +838,10 @@ class TerrarumSansBitmap( /** - * posXbuffer's size is greater than the string, last element marks the width of entire string. + * THE function to typeset all the letters and their diacritics + * + * @return Pair of X-positions and Y-positions, of which the X-position's size is greater than the string + * and the last element marks the width of entire string. */ private fun buildPosMap(str: List): Pair { val posXbuffer = IntArray(str.size + 1) { 0 } @@ -900,6 +907,7 @@ class TerrarumSansBitmap( if (isHangul(thisChar) && !isHangulChoseong(thisChar) && !isHangulCompat(thisChar)) { posXbuffer[charIndex] = posXbuffer[nonDiacriticCounter] } + // is this glyph NOT a diacritic? else if (thisProp.writeOnTop < 0) { posXbuffer[charIndex] = -thisProp.nudgeX + when (itsProp.alignWhere) { @@ -929,15 +937,19 @@ class TerrarumSansBitmap( } }*/ + // is this glyph a diacritic? else { + val diacriticsType = thisProp.writeOnTop // set X pos according to alignment information posXbuffer[charIndex] = when (thisProp.alignWhere) { GlyphProps.ALIGN_LEFT, GlyphProps.ALIGN_BEFORE -> posXbuffer[nonDiacriticCounter] GlyphProps.ALIGN_RIGHT -> { - posXbuffer[nonDiacriticCounter] - (W_VAR_INIT - itsProp.width) + val alignXPos = if (!itsProp.diacriticsAnchors[diacriticsType].xUsed) itsProp.width else itsProp.diacriticsAnchors[diacriticsType].x + + posXbuffer[nonDiacriticCounter] - W_VAR_INIT + alignXPos } GlyphProps.ALIGN_CENTRE -> { - val alignXPos = if (itsProp.diacriticsAnchors[0].x == 0) itsProp.width.div(2) else itsProp.diacriticsAnchors[0].x + val alignXPos = if (!itsProp.diacriticsAnchors[diacriticsType].xUsed) itsProp.width.div(2) else itsProp.diacriticsAnchors[diacriticsType].x if (itsProp.alignWhere == GlyphProps.ALIGN_RIGHT) { posXbuffer[nonDiacriticCounter] + alignXPos + (itsProp.width + 1).div(2) @@ -966,7 +978,7 @@ class TerrarumSansBitmap( //dbgprn("AAARRRRHHHH for character ${thisChar.toHex()}") //dbgprn("lastNonDiacriticChar: ${lastNonDiacriticChar.toHex()}") //dbgprn("cond: ${thisProp.alignXPos == GlyphProps.DIA_OVERLAY}, charIndex: $charIndex") - if (thisProp.diacriticsAnchors[0].x == GlyphProps.DIA_OVERLAY) + if (diacriticsType == GlyphProps.DIA_OVERLAY) posYbuffer[charIndex] -= H_OVERLAY_LOWERCASE_SHIFTDOWN * (!flipY).toSign() // if minus-assign doesn't work, try plus-assign else posYbuffer[charIndex] -= H_STACKUP_LOWERCASE_SHIFTDOWN * (!flipY).toSign() // if minus-assign doesn't work, try plus-assign diff --git a/testing.PNG b/testing.PNG index 1df16e7c98f74e14a2315c7055910a8d66384202..5dd3bb0794cea7162d53106575b82b6f30327363 100755 GIT binary patch literal 14091 zcmeI3eKea_9LKNrEY)sQ*=U8fIX3>8W6UydX%tmfBy{WP7H?H=4M~w26Q-?I%UVyz zIWl3)dWtiP)H@?$hb4`-tQ9g8wR8=82(76`blJ_H`*VLaJG*~9&$-X+oG9?hsc{km`X z-j=cvpS8F)x{IXXdM{6NDl_~_QT!#}#Se}o#W`7jd$Z`gNUjvli}UsCI{@&N2)SYt z*j=9`>>9ZGnE`M(umQFVn!X3X#r?JLSr!focrES=TNr z9_1zNt`UnwBJ6U^lhz9r~5FqNY@qB%0%I?gk)twW*f89YNjxY|NdVJMv^hwR)Z+vm(`Zy8v)U5bDRW zqj7lD66`>9mQNx(&5W23BHjf+_JztYMPb(`mZ7S{4BJR9JJB_duqqb2QF78_U;?b+ z$ewg)xXlW47#!H>&)TY*ip7#7&)hz@X7^r_(bRna^s`dQ;oj}K`K+edq5NSCc-T~^ zBU{_n`FN;4@5H=%PRZcH?k);79k^<3VQ8RK*4;yh=0334r+SM&J{mGs5jb46bZ;7W zrm$D>8!_)wA|K(3bDy)a(3w9(l&_viE5_u7;j<#RT0 zpe+-%pK5Hjx`!?6g+%IR+oLYe8SUPA%qg8$m!bSi*A!K*U6hW#&_lhA_29dr;c=}q zvSG#b#sO+s!d9#ErWF8_!&jyc&S+^hw{r?Hi-t586A&wcLC5HDEFV$H2H}4Ha)kSX z`9BmoHg9+VQDumt(Q(VJdrxX3~l@(TcvYC|qq8bkQplsHWsdh|~ z_FJXESX#%q20_I&LB${xmrt3YkCSf1Vi*{i)JmU;<9AGO;g#(8N}6JFc{14&3gu3C zgJ22_y2FBg*f>#3o&)h;*Nqklmn5XRbMQ_*!=5?dzVbZNR6x4l#~^r`w=SISIRZT) z)jgb;Z$FZEij`B9aMSSkPA^5%tWSKePsS(#RuqswcC5KYz8VDI$YX_F>-Y<73+rJR zY$J_t90p+|K81#1go_876f`MD&NoP+NTNuh|Dhiws9;gSqJl*QYdl*d$dQmEAxA=v zgdE8@x3G~vBY#HzjQsgu`m??zMhZ9S0VvoO)HmOHdq-jb+~SW7G<@kmrzj6d1gIF0 z6Cg)K%K=&z5fdObLSTrf7*R2zVnoG=iV+nfDn?X{s2EW(qGCkFh>HI&RLm5YyaHE# wK+rO1XODmE?qK-)(!1Zm@51k(Q)A16N#0XV1iq+FHq2P?v9N$9zu3!v1C=9P&j0`b literal 19149 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 JHM>rn|1aN|z2E=< diff --git a/testtext.txt b/testtext.txt index f9588b9..481a614 100755 --- a/testtext.txt +++ b/testtext.txt @@ -1,16 +1,4 @@ -ถูกตำรว -ถูกตรว -aำ -แป้นพิมพ์เกษมณี แป้นพิมพ์ปัตตะโชติ -คำเตือน-อนามัยและความปลอดภัย -UIJTuijt + A ดุ ตี ปู่ พี่ ป่ ม่ ปั มั พีุ -দৌ ষৌ - -Mℂ℄ℇ℀℈jℊ№℗CKℵ℡Z℥ -ℵ₀ - -🄅🄵🅅🄕🄩🄪🄭🅭🄏🄎🅯🅮🆞🆠🆐🄯🆒🆓 - -도ᄋ해 무ᄅ과 배ᄀ두사ᄂ 하느니ᄆ이 보우하사 \ No newline at end of file + U̸ u̸ \ No newline at end of file diff --git a/work_files/ascii_variable.psd b/work_files/ascii_variable.psd index 3a3048a..de9b286 100644 --- a/work_files/ascii_variable.psd +++ b/work_files/ascii_variable.psd @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f2a33657d6df816821ad8991515c4da297b1053966cdb0fc23e09168c70c9299 -size 317663 +oid sha256:1b80ce3760208ed5fd3ccd2dadbd2c6e6ab433c4c7058648e87df89dbe08b7b3 +size 317679 diff --git a/work_files/diacritical_marks_variable.psd b/work_files/diacritical_marks_variable.psd index 4bec8eef876515cc0d10eeb209b8458419dae9bd..b0f1aeb573e18cdcdd82967324778bc12379ddae 100644 GIT binary patch literal 131 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