Faculty of Information Technology
Software Engineering Group

Ticket #126: libview_fix_1_of_1_rev_53572fb6382e_Issue__126__Removing_hack__correctly_calculating_masDisplayBuffer_and_having_fallback_maxDisplayBuffer_is_set_to_107_characters_.patch

File libview_fix_1_of_1_rev_53572fb6382e_Issue__126__Removing_hack__correctly_calculating_masDisplayBuffer_and_having_fallback_maxDisplayBuffer_is_set_to_107_characters_.patch, 14.0 KB (added by Patrik Svestka, 19 months ago)

maxDisplayBuffer calculation with test. Adding additional selector isDepthXXImage where XX is Screen depth

  • Depth16Image.st

    # HG changeset patch
    # User Patrik Svestka <patrik.svestka@gmail.com>
    # Date 1523348885 -7200
    #      Tue Apr 10 10:28:05 2018 +0200
    # Branch jv
    # Node ID 53572fb6382eb9633458c0a28d0633843407b7df
    # Parent  cc53a404d32d213dae19840d3876cecaa2560b27
    Issue #126: Removing hack, correctly calculating masDisplayBuffer and having fallback maxDisplayBuffer is set to 107 characters.
    
    On windows (7 and up) there is undocumented, you can't find this information anywhere, limit for raster size when using TextOutA or TextOutW.
    Experimentally, I have come to the conclusion that the limit is most likely set to 16384 points of the raster.
    
    To correctly calculate the maximum display buffer (maxDisplayBuffer) I have come to a formula:
    maxDisplayBuffer = 16384 / (maxWidth + tmet.tmOverhang);
    
    maxWidth is the maximum selected font width
    tmet.tmOverhang is the "extra width per string that may be added to some synthesized fonts." -> If you want to get the actual width you have to add it to maxWidth. You will get the whole extent then. (For most fonts tmOverhang will be 0.)
    
    The whole definition of tmOverhang from MSND - https://msdn.microsoft.com/en-us/library/windows/desktop/dd145132(v=vs.85).aspx
    
    diff -r cc53a404d32d -r 53572fb6382e Depth16Image.st
    a b  
    204204    "return the number of bytes in one scanline of the image"
    205205
    206206    ^ width * 2.
     207!
     208
     209isDepth16Image
     210    "return true if the image is instance of Depth16Image"
     211   (self bitsPerPixel == 16) ifTrue:[^ true]. 
     212    ^ false
     213
     214    "Created: / 16-04-2018 / 16:59:55 / svestkap"
    207215! !
    208216
    209217!Depth16Image class methodsFor:'documentation'!
     
    214222
    215223version_CVS
    216224    ^ '$Header$'
     225!
     226
     227version_HG
     228
     229    ^ '$Changeset: <not expanded> $'
    217230! !
    218231
  • Depth1Image.st

    diff -r cc53a404d32d -r 53572fb6382e Depth1Image.st
    a b  
    683683    ^ super colorFromValue:pixelValue.
    684684!
    685685
     686isDepth1Image
     687    "return true if the image is instance of Depth1Image"
     688   (self bitsPerPixel == 1) ifTrue:[^ true]. 
     689    ^ false
     690
     691    "Created: / 16-04-2018 / 17:02:34 / svestkap"
     692!
     693
    686694rgbFromValue:pixelValue
    687695    "given a pixel value, return the corresponding 24bit rgbValue (rrggbb, red is MSB).
    688696     Pixel values start with 0."
  • Depth24Image.st

    diff -r cc53a404d32d -r 53572fb6382e Depth24Image.st
    a b  
    1 "{ Encoding: utf8 }"
    2 
    31"
    42 COPYRIGHT (c) 1993 by Claus Gittinger
    53              All Rights Reserved
     
    33523350    ^ -8
    33533351!
    33543352
     3353isDepth24Image
     3354    "return true if the image is instance of Depth24Image"
     3355   (self bitsPerPixel == 24) ifTrue:[^ true]. 
     3356    ^ false
     3357
     3358    "Created: / 16-04-2018 / 16:53:00 / svestkap"
     3359!
     3360
    33553361redBitsOf:pixel
    33563362    "given a pixel-value, return the red component as byteValue (0..255)"
    33573363
  • Depth2Image.st

    diff -r cc53a404d32d -r 53572fb6382e Depth2Image.st
    a b  
    968968    ^ super colorFromValue:colorValue.
    969969!
    970970
     971isDepth2Image
     972    "return true if the image is instance of Depth2Image"
     973   (self bitsPerPixel == 2) ifTrue:[^ true]. 
     974    ^ false
     975
     976    "Created: / 16-04-2018 / 17:02:16 / svestkap"
     977!
     978
    971979usedColors
    972980    "return a collection of colors used in the receiver.
    973981     For depth2 images, we return the colorMap here, assuming all
  • Depth32Image.st

    diff -r cc53a404d32d -r 53572fb6382e Depth32Image.st
    a b  
    10781078    ^ true
    10791079!
    10801080
     1081isDepth32Image
     1082    "return true if the image is instance of Depth32Image"
     1083   (self bitsPerPixel == 32) ifTrue:[^ true]. 
     1084    ^ false
     1085
     1086    "Created: / 16-04-2018 / 17:00:39 / svestkap"
     1087!
     1088
    10811089redBitsOf:pixel
    10821090    "given a pixel-value, return the red component as byteValue (0..255)"
    10831091
  • Depth48Image.st

    diff -r cc53a404d32d -r 53572fb6382e Depth48Image.st
    a b  
    134134    "return the number of bytes in one scanline of the image"
    135135
    136136    ^ width * 6.
     137!
     138
     139isDepth48Image
     140    "return true if the image is instance of Depth48Image"
     141   (self bitsPerPixel == 48) ifTrue:[^ true]. 
     142    ^ false
     143
     144    "Created: / 16-04-2018 / 17:02:03 / svestkap"
    137145! !
    138146
    139147!Depth48Image class methodsFor:'documentation'!
    140148
    141149version
    142150    ^ '$Header$'
     151!
     152
     153version_HG
     154
     155    ^ '$Changeset: <not expanded> $'
    143156! !
    144157
  • Depth4Image.st

    diff -r cc53a404d32d -r 53572fb6382e Depth4Image.st
    a b  
    10241024    ^ super colorFromValue:pixelValue
    10251025!
    10261026
     1027isDepth4Image
     1028    "return true if the image is instance of Depth4Image"
     1029   (self bitsPerPixel == 4) ifTrue:[^ true]. 
     1030    ^ false
     1031
     1032    "Created: / 16-04-2018 / 17:01:45 / svestkap"
     1033!
     1034
    10271035usedValues
    10281036    "return a collection of color values used in the receiver."
    10291037
  • Depth64Image.st

    diff -r cc53a404d32d -r 53572fb6382e Depth64Image.st
    a b  
    140140
    141141hasAlphaChannel
    142142    ^ true
     143!
     144
     145isDepth64Image
     146    "return true if the image is instance of Depth64Image"
     147   (self bitsPerPixel == 64) ifTrue:[^ true]. 
     148    ^ false
     149
     150    "Created: / 16-04-2018 / 17:01:31 / svestkap"
    143151! !
    144152
    145153!Depth64Image class methodsFor:'documentation'!
  • Depth8Image.st

    diff -r cc53a404d32d -r 53572fb6382e Depth8Image.st
    a b  
    1 "{ Encoding: utf8 }"
    2 
    31"
    42 COPYRIGHT (c) 1993 by Claus Gittinger
    53              All Rights Reserved
     
    25862584    ^ super colorFromValue:pixelValue
    25872585!
    25882586
     2587isDepth8Image
     2588    "return true if the image is instance of Depth8Image"
     2589   (self bitsPerPixel == 8) ifTrue:[^ true]. 
     2590    ^ false
     2591
     2592    "Created: / 16-04-2018 / 17:01:10 / svestkap"
     2593!
     2594
    25892595nColorsUsed
    25902596    ^ colorMap size
    25912597!
  • WinWorkstation.st

    diff -r cc53a404d32d -r 53572fb6382e WinWorkstation.st
    a b  
    1077710777    SIZE size;
    1077810778    TEXTMETRICW tmet;
    1077910779    short maxDisplayBuffer;
    10780     int i1, i2, n, l, getMetricsResult, maxWidth, toDisplay;
     10780    int i1, i2, n, l;
     10781    int maxWidth, toDisplay;
    1078110782    int nInstBytes;
    1078210783
    1078310784    if (__isExternalAddress(aGCId)
     
    1081410815            }
    1081510816        }
    1081610817
    10817   /* Windows (as in 7 or newer) limits the string size for TextOut* changes
    10818   according to the font size!  There is apparently an undocumented limit to
    10819   the overall raster size.  The maximum value for the raster size is 16384. */
    10820 
    10821 
    10822   // GetTextMetricsW gives all needed font metrics
    10823   // If for any reason it does not work there is a fallback
     10818  /* The Windows (as in 7 or newer) limits the string size for TextOut*.  There is apparently
     10819   * an undocumented limit to the overall raster size.  It changes according to the font size (width)!
     10820   * The maximum value for the raster size is apparently 16384 (experimentally tested on Windows 7).
     10821   */
     10822
     10823
     10824  /* GetTextMetricsW gives all needed font metrics (has a fallback when fails)
     10825   * - a maxWidth parameter takes in account the worst case scenario - user prints
     10826   * with the widest possible characters from the selected font.
     10827   * - a overHang is a parameter which allows the font to grow outside the maxWidth size
     10828   */
     10829
    1082410830  if (GetTextMetricsW(hDC, &tmet)) {
    1082510831      maxWidth = tmet.tmMaxCharWidth;
     10832      // Dynamically calculate the maximum buffer size for the selected font and its size
     10833      maxDisplayBuffer = 16384 / (maxWidth + tmet.tmOverhang);
    1082610834  } else {
    10827       static char *s = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzコーヒーアイスクリームケーキビールすしかき";
    10828       static int len;
    10829 
    10830       if (len == 0) {
    10831           len = strlen(s);
    10832       }
    10833       assert(GetTextExtentPoint32(hDC, s, len, &size) != 0 && "GetTextExtentPoint32 != 0, fix me");
    10834       maxWidth = size.cx / (len / 2) + 1;
     10835      /* A fallback when GetTextMetricsW fails.  Should not normally happen!
     10836       * The works case scenario, experimentally tested from 1170 fonts, is Microsoft Uighur-build-italic-288
     10837       * with maxWidth = 2179.  That would mean only 7 characters at the maxDisplayBuffer (16384 / 2179).
     10838       * Such a buffer would be rediculously slow!. A compromise is needed (speed vs. functionality)
     10839       * Taking an avgWidth from all 1170 with fonts size 96pt -> avgWidth = 152,
     10840       * which should suffice 99.9% of time. The buffer would then be int(16384/152) = 107, which is reasonable.
     10841       */
     10842      maxDisplayBuffer = 107;
    1083510843  }
    1083610844
    10837   // Calculate the maximum buffer size for the selected font size
    10838   maxDisplayBuffer = 16384 / (maxWidth + tmet.tmOverhang);
    10839  
    10840   // Debug messages
    10841   // PRINTF(("MaxDisplayBuffer:%d\n", maxDisplayBuffer));
    10842   //PRINTF(("MaxWidth:%d; overHang:%d\n", maxWidth, tmet.tmOverhang));
    10843 
    1084410845#if 0
    1084510846        /* leftover code from tries to make TextOut honor the gc-mode,
    1084610847         * until I googled, that TextOut does not (by purpose, or backward-bug compatibility)
  • new file tests/FontTests.st

    diff -r cc53a404d32d -r 53572fb6382e tests/FontTests.st
    - +  
     1"{ Encoding: utf8 }"
     2
     3"{ Package: 'stx:libview/tests' }"
     4
     5"{ NameSpace: Smalltalk }"
     6
     7TestCase subclass:#FontTests
     8        instanceVariableNames:'testFontSize fontList testString startTimer topView textView'
     9        classVariableNames:''
     10        poolDictionaries:''
     11        category:'TestCases'
     12!
     13
     14!FontTests class methodsFor:'documentation'!
     15
     16documentation
     17"
     18    documentation to be added.
     19
     20    [author:]
     21        svestkap
     22
     23    [instance variables:]
     24
     25    [class variables:]
     26
     27    [see also:]
     28
     29"
     30! !
     31
     32!FontTests methodsFor:'initialize / release'!
     33
     34setUp
     35
     36    | numberOfTestedFonts windowWidthX windowHightY |
     37    self skipIf: Screen current isNil description: 'No display connection'.
     38   
     39    "/init
     40    startTimer := Time now.
     41    numberOfTestedFonts := 100.
     42   
     43    "/random font size to circumvent ascanding or descending font size bugs
     44    testFontSize := #(6 12 24 48 96 288) shuffled.
     45   
     46    "/pick random fonts available at system
     47    fontList := (Display listOfAvailableFonts) shuffled.
     48    fontList := ((fontList size) > numberOfTestedFonts) ifTrue: [fontList copyFrom:1 to: numberOfTestedFonts].
     49
     50    "/Japanese characters for UTF-16 testing
     51    testString := Unicode16String new.
     52    testString := 'コーヒーアイスクリームケーキビールすしかき空'.
     53
     54    "/creating a long string to be displayed at textView
     55    10 timesRepeat:[testString := testString,testString]. 
     56
     57    "/set testing window size -> large for large font testing (will probably fail for smaller)
     58    windowWidthX := 640.
     59    windowHightY := 400.
     60
     61    topView := StandardSystemView new.
     62    topView extent:windowWidthX @ windowHightY.
     63    topView label:'TextOut Raster testing with different font sizes and large strings'.
     64    textView := EditTextView origin:0.0 @ 0.0 extent:1.0 @ 1.0 in:topView.
     65    "/if styles are present, make sure we have correct setup
     66    textView backgroundColor: Color white;
     67             foregroundColor: Color black.
     68
     69    topView open.
     70    topView waitUntilVisible.
     71
     72    "Modified: / 06-04-2018 / 14:44:17 / svestkap"
     73    "Modified (comment): / 25-04-2018 / 12:09:18 / svestkap"
     74!
     75
     76tearDown
     77    "common cleanup - invoked after testing."
     78    | endTimer message |
     79    endTimer := Time now.
     80   
     81    "/left here for user overview when run manually
     82    "/Transcript showCR: 'FontTests time duration: '; showCR:(endTimer - startTimer) asString.
     83   
     84    message := 'FontTests time duration: ', (endTimer - startTimer) asString.
     85    Logger log: message severity: #info.
     86   
     87    topView isOpen ifTrue:[ topView close ].
     88
     89    "Modified (format): / 25-04-2018 / 12:05:24 / svestkap"
     90! !
     91
     92!FontTests methodsFor:'tests'!
     93
     94test_issue_126_TextOut_raster_size_24bit
     95    " Testing long strings display. 
     96      The test takes shown textView and tries to find out non-white pixels.
     97 
     98    For more visit: https://swing.fit.cvut.cz/projects/stx-jv/ticket/126"
     99    | font imageSnapshot |
     100 
     101    self skipIf: Screen current depth ~~ 24 description: 'Different than 24bit resulution'.
     102
     103    font := fontList atRandom.
     104
     105    fontList do:[:font |
     106        testFontSize do:[:preselectedFontSize |   | setFontSize fontAtSize bits |
     107               [   textView contents: testString.
     108                   (font size = 0.0) ifTrue:[setFontSize := preselectedFontSize]  "/TrueType font
     109                                     ifFalse:[setFontSize := font size].          "/Raster font
     110                   fontAtSize := (font copy asSize:setFontSize) onDevice:textView device.
     111                   textView font:fontAtSize.
     112
     113                   imageSnapshot := Image fromView:textView grab:false.
     114                   self assert:(imageSnapshot photometric == #rgb).
     115                   self assert:(imageSnapshot isDepth24Image).
     116
     117                   bits := imageSnapshot bits.
     118                   self assert:(bits contains:[:bit | bit ~~ 255 ]).
     119               ] ensure:[textView contents:nil].
     120        ]
     121    ].
     122
     123    "
     124     FontTests run: #test_issue_126_TextOut_raster_size_24bit 
     125     FontTests debug: #test_issue_126_TextOut_raster_size_24bit 
     126    "
     127
     128    "Created: / 05-04-2018 / 12:16:57 / svestkap"
     129    "Modified: / 25-04-2018 / 10:19:35 / svestkap"
     130    "Modified (format): / 25-04-2018 / 11:45:40 / svestkap"
     131! !
     132
     133!FontTests class methodsFor:'documentation'!
     134
     135version_HG
     136
     137    ^ '$Changeset: <not expanded> $'
     138! !
     139
  • tests/stx_libview_tests.st

    diff -r cc53a404d32d -r 53572fb6382e tests/stx_libview_tests.st
    a b  
    7575    ^ #(
    7676        "<className> or (<className> attributes...) in load order"
    7777        FcPatternTests
     78        FontTests
    7879        FormTests
    7980        ImageTests
    8081        ResourcePackTests