Home | History | Annotate | Download | only in text
      1 /*
      2  * Copyright 2015 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "GrDistanceFieldAdjustTable.h"
      9 
     10 #include "SkScalerContext.h"
     11 
     12 SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;)
     13 
     14 void GrDistanceFieldAdjustTable::buildDistanceAdjustTable() {
     15     // This is used for an approximation of the mask gamma hack, used by raster and bitmap
     16     // text. The mask gamma hack is based off of guessing what the blend color is going to
     17     // be, and adjusting the mask so that when run through the linear blend will
     18     // produce the value closest to the desired result. However, in practice this means
     19     // that the 'adjusted' mask is just increasing or decreasing the coverage of
     20     // the mask depending on what it is thought it will blit against. For black (on
     21     // assumed white) this means that coverages are decreased (on a curve). For white (on
     22     // assumed black) this means that coverages are increased (on a a curve). At
     23     // middle (perceptual) gray (which could be blit against anything) the coverages
     24     // remain the same.
     25     //
     26     // The idea here is that instead of determining the initial (real) coverage and
     27     // then adjusting that coverage, we determine an adjusted coverage directly by
     28     // essentially manipulating the geometry (in this case, the distance to the glyph
     29     // edge). So for black (on assumed white) this thins a bit; for white (on
     30     // assumed black) this fake bolds the geometry a bit.
     31     //
     32     // The distance adjustment is calculated by determining the actual coverage value which
     33     // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This
     34     // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the
     35     // actual edge. So by subtracting this distance adjustment and computing without the
     36     // the coverage adjustment we should get 0.5 coverage at the same point.
     37     //
     38     // This has several implications:
     39     //     For non-gray lcd smoothed text, each subpixel essentially is using a
     40     //     slightly different geometry.
     41     //
     42     //     For black (on assumed white) this may not cover some pixels which were
     43     //     previously covered; however those pixels would have been only slightly
     44     //     covered and that slight coverage would have been decreased anyway. Also, some pixels
     45     //     which were previously fully covered may no longer be fully covered.
     46     //
     47     //     For white (on assumed black) this may cover some pixels which weren't
     48     //     previously covered at all.
     49 
     50     int width, height;
     51     size_t size;
     52 
     53 #ifdef SK_GAMMA_CONTRAST
     54     SkScalar contrast = SK_GAMMA_CONTRAST;
     55 #else
     56     SkScalar contrast = 0.5f;
     57 #endif
     58     SkScalar paintGamma = SK_GAMMA_EXPONENT;
     59     SkScalar deviceGamma = SK_GAMMA_EXPONENT;
     60 
     61     size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
     62         &width, &height);
     63 
     64     SkASSERT(kExpectedDistanceAdjustTableSize == height);
     65     fTable = new SkScalar[height];
     66 
     67     SkAutoTArray<uint8_t> data((int)size);
     68     SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
     69 
     70     // find the inverse points where we cross 0.5
     71     // binsearch might be better, but we only need to do this once on creation
     72     for (int row = 0; row < height; ++row) {
     73         uint8_t* rowPtr = data.get() + row*width;
     74         for (int col = 0; col < width - 1; ++col) {
     75             if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) {
     76                 // compute point where a mask value will give us a result of 0.5
     77                 float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]);
     78                 float borderAlpha = (col + interp) / 255.f;
     79 
     80                 // compute t value for that alpha
     81                 // this is an approximate inverse for smoothstep()
     82                 float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f;
     83 
     84                 // compute distance which gives us that t value
     85                 const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor
     86                 float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor;
     87 
     88                 fTable[row] = d;
     89                 break;
     90             }
     91         }
     92     }
     93 }
     94