| OLD | NEW | 
 | (Empty) | 
|    1 #include <cmath> |  | 
|    2  |  | 
|    3 #include "SkBitmap.h" |  | 
|    4 #include "skpdiff_util.h" |  | 
|    5 #include "SkPMetric.h" |  | 
|    6 #include "SkPMetricUtil_generated.h" |  | 
|    7  |  | 
|    8 struct RGB { |  | 
|    9     float r, g, b; |  | 
|   10 }; |  | 
|   11  |  | 
|   12 struct LAB { |  | 
|   13     float l, a, b; |  | 
|   14 }; |  | 
|   15  |  | 
|   16 template<class T> |  | 
|   17 struct Image2D { |  | 
|   18     int width; |  | 
|   19     int height; |  | 
|   20     T* image; |  | 
|   21  |  | 
|   22     Image2D(int w, int h) |  | 
|   23         : width(w), |  | 
|   24           height(h) { |  | 
|   25         SkASSERT(w > 0); |  | 
|   26         SkASSERT(h > 0); |  | 
|   27         image = SkNEW_ARRAY(T, w * h); |  | 
|   28     } |  | 
|   29  |  | 
|   30     ~Image2D() { |  | 
|   31         SkDELETE_ARRAY(image); |  | 
|   32     } |  | 
|   33  |  | 
|   34     void readPixel(int x, int y, T* pixel) const { |  | 
|   35         SkASSERT(x >= 0); |  | 
|   36         SkASSERT(y >= 0); |  | 
|   37         SkASSERT(x < width); |  | 
|   38         SkASSERT(y < height); |  | 
|   39         *pixel = image[y * width + x]; |  | 
|   40     } |  | 
|   41  |  | 
|   42     T* getRow(int y) const { |  | 
|   43         return &image[y * width]; |  | 
|   44     } |  | 
|   45  |  | 
|   46     void writePixel(int x, int y, const T& pixel) { |  | 
|   47         SkASSERT(x >= 0); |  | 
|   48         SkASSERT(y >= 0); |  | 
|   49         SkASSERT(x < width); |  | 
|   50         SkASSERT(y < height); |  | 
|   51         image[y * width + x] = pixel; |  | 
|   52     } |  | 
|   53 }; |  | 
|   54  |  | 
|   55 typedef Image2D<float> ImageL; |  | 
|   56 typedef Image2D<RGB> ImageRGB; |  | 
|   57 typedef Image2D<LAB> ImageLAB; |  | 
|   58  |  | 
|   59 template<class T> |  | 
|   60 struct ImageArray |  | 
|   61 { |  | 
|   62     int slices; |  | 
|   63     Image2D<T>** image; |  | 
|   64  |  | 
|   65     ImageArray(int w, int h, int s) |  | 
|   66         : slices(s) { |  | 
|   67         SkASSERT(s > 0); |  | 
|   68         image = SkNEW_ARRAY(Image2D<T>*, s); |  | 
|   69         for (int sliceIndex = 0; sliceIndex < slices; sliceIndex++) { |  | 
|   70             image[sliceIndex] = SkNEW_ARGS(Image2D<T>, (w, h)); |  | 
|   71         } |  | 
|   72     } |  | 
|   73  |  | 
|   74     ~ImageArray() { |  | 
|   75         for (int sliceIndex = 0; sliceIndex < slices; sliceIndex++) { |  | 
|   76             SkDELETE(image[sliceIndex]); |  | 
|   77         } |  | 
|   78         SkDELETE_ARRAY(image); |  | 
|   79     } |  | 
|   80  |  | 
|   81     Image2D<T>* getLayer(int z) const { |  | 
|   82         SkASSERT(z >= 0); |  | 
|   83         SkASSERT(z < slices); |  | 
|   84         return image[z]; |  | 
|   85     } |  | 
|   86 }; |  | 
|   87  |  | 
|   88 typedef ImageArray<float> ImageL3D; |  | 
|   89  |  | 
|   90  |  | 
|   91 #define MAT_ROW_MULT(rc,gc,bc) r*rc + g*gc + b*bc |  | 
|   92  |  | 
|   93  |  | 
|   94 void adobergb_to_cielab(float r, float g, float b, LAB* lab) { |  | 
|   95     // Conversion of Adobe RGB to XYZ taken from from "Adobe RGB (1998) ColorIma
     ge Encoding" |  | 
|   96     // URL:http://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf |  | 
|   97     // Section: 4.3.5.3 |  | 
|   98     // See Also: http://en.wikipedia.org/wiki/Adobe_rgb |  | 
|   99     float x = MAT_ROW_MULT(0.57667f, 0.18556f, 0.18823f); |  | 
|  100     float y = MAT_ROW_MULT(0.29734f, 0.62736f, 0.07529f); |  | 
|  101     float z = MAT_ROW_MULT(0.02703f, 0.07069f, 0.99134f); |  | 
|  102  |  | 
|  103     // The following is the white point in XYZ, so it's simply the row wise addi
     tion of the above |  | 
|  104     // matrix. |  | 
|  105     const float xw = 0.5767f + 0.185556f + 0.188212f; |  | 
|  106     const float yw = 0.297361f + 0.627355f + 0.0752847f; |  | 
|  107     const float zw = 0.0270328f + 0.0706879f + 0.991248f; |  | 
|  108  |  | 
|  109     // This is the XYZ color point relative to the white point |  | 
|  110     float f[3] = { x / xw, y / yw, z / zw }; |  | 
|  111  |  | 
|  112     // Conversion from XYZ to LAB taken from |  | 
|  113     // http://en.wikipedia.org/wiki/CIELAB#Forward_transformation |  | 
|  114     for (int i = 0; i < 3; i++) { |  | 
|  115         if (f[i] >= 0.008856f) { |  | 
|  116             f[i] = SkPMetricUtil::get_cube_root(f[i]); |  | 
|  117         } else { |  | 
|  118             f[i] = 7.787f * f[i] + 4.0f / 29.0f; |  | 
|  119         } |  | 
|  120     } |  | 
|  121     lab->l = 116.0f * f[1] - 16.0f; |  | 
|  122     lab->a = 500.0f * (f[0] - f[1]); |  | 
|  123     lab->b = 200.0f * (f[1] - f[2]); |  | 
|  124 } |  | 
|  125  |  | 
|  126 /// Converts a 8888 bitmap to LAB color space and puts it into the output |  | 
|  127 static void bitmap_to_cielab(const SkBitmap* bitmap, ImageLAB* outImageLAB) { |  | 
|  128     SkASSERT(bitmap->config() == SkBitmap::kARGB_8888_Config); |  | 
|  129  |  | 
|  130     int width = bitmap->width(); |  | 
|  131     int height = bitmap->height(); |  | 
|  132     SkASSERT(outImageLAB->width == width); |  | 
|  133     SkASSERT(outImageLAB->height == height); |  | 
|  134  |  | 
|  135     bitmap->lockPixels(); |  | 
|  136     RGB rgb; |  | 
|  137     LAB lab; |  | 
|  138     for (int y = 0; y < height; y++) { |  | 
|  139         unsigned char* row = (unsigned char*)bitmap->getAddr(0, y); |  | 
|  140         for (int x = 0; x < width; x++) { |  | 
|  141             // Perform gamma correction which is assumed to be 2.2 |  | 
|  142             rgb.r = SkPMetricUtil::get_gamma(row[x * 4 + 2]); |  | 
|  143             rgb.g = SkPMetricUtil::get_gamma(row[x * 4 + 1]); |  | 
|  144             rgb.b = SkPMetricUtil::get_gamma(row[x * 4 + 0]); |  | 
|  145             adobergb_to_cielab(rgb.r, rgb.g, rgb.b, &lab); |  | 
|  146             outImageLAB->writePixel(x, y, lab); |  | 
|  147         } |  | 
|  148     } |  | 
|  149     bitmap->unlockPixels(); |  | 
|  150 } |  | 
|  151  |  | 
|  152 // From Barten SPIE 1989 |  | 
|  153 static float contrast_sensitivity(float cyclesPerDegree, float luminance) { |  | 
|  154     float a = 440.0f * powf(1.0f + 0.7f / luminance, -0.2f); |  | 
|  155     float b = 0.3f * powf(1 + 100.0 / luminance, 0.15f); |  | 
|  156     return a * |  | 
|  157            cyclesPerDegree * |  | 
|  158            expf(-b * cyclesPerDegree) * |  | 
|  159            sqrtf(1.0f + 0.06f * expf(b * cyclesPerDegree)); |  | 
|  160 } |  | 
|  161  |  | 
|  162 #if 0 |  | 
|  163 // We're keeping these around for reference and in case the lookup tables are no
      longer desired. |  | 
|  164 // They are no longer called by any code in this file. |  | 
|  165  |  | 
|  166 // From Daly 1993 |  | 
|  167  static float visual_mask(float contrast) { |  | 
|  168     float x = powf(392.498f * contrast, 0.7f); |  | 
|  169     x = powf(0.0153f * x, 4.0f); |  | 
|  170     return powf(1.0f + x, 0.25f); |  | 
|  171 } |  | 
|  172  |  | 
|  173 // From Ward Larson Siggraph 1997 |  | 
|  174 static float threshold_vs_intensity(float adaptationLuminance) { |  | 
|  175     float logLum = log10f(adaptationLuminance); |  | 
|  176     float x; |  | 
|  177     if (logLum < -3.94f) { |  | 
|  178         x = -2.86f; |  | 
|  179     } else if (logLum < -1.44f) { |  | 
|  180         x = powf(0.405f * logLum + 1.6f, 2.18) - 2.86f; |  | 
|  181     } else if (logLum < -0.0184f) { |  | 
|  182         x = logLum - 0.395f; |  | 
|  183     } else if (logLum < 1.9f) { |  | 
|  184         x = powf(0.249f * logLum + 0.65f, 2.7f) - 0.72f; |  | 
|  185     } else { |  | 
|  186         x = logLum - 1.255f; |  | 
|  187     } |  | 
|  188     return powf(10.0f, x); |  | 
|  189 } |  | 
|  190  |  | 
|  191 #endif |  | 
|  192  |  | 
|  193 /// Simply takes the L channel from the input and puts it into the output |  | 
|  194 static void lab_to_l(const ImageLAB* imageLAB, ImageL* outImageL) { |  | 
|  195     for (int y = 0; y < imageLAB->height; y++) { |  | 
|  196         for (int x = 0; x < imageLAB->width; x++) { |  | 
|  197             LAB lab; |  | 
|  198             imageLAB->readPixel(x, y, &lab); |  | 
|  199             outImageL->writePixel(x, y, lab.l); |  | 
|  200         } |  | 
|  201     } |  | 
|  202 } |  | 
|  203  |  | 
|  204 /// Convolves an image with the given filter in one direction and saves it to th
     e output image |  | 
|  205 static void convolve(const ImageL* imageL, bool vertical, ImageL* outImageL) { |  | 
|  206     SkASSERT(imageL->width == outImageL->width); |  | 
|  207     SkASSERT(imageL->height == outImageL->height); |  | 
|  208  |  | 
|  209     const float matrix[] = { 0.05f, 0.25f, 0.4f, 0.25f, 0.05f }; |  | 
|  210     const int matrixCount = sizeof(matrix) / sizeof(float); |  | 
|  211     const int radius = matrixCount / 2; |  | 
|  212  |  | 
|  213     // Keep track of what rows are being operated on for quick access. |  | 
|  214     float* rowPtrs[matrixCount]; // Because matrixCount is constant, this won't 
     create a VLA |  | 
|  215     for (int y = radius; y < matrixCount; y++) { |  | 
|  216         rowPtrs[y] = imageL->getRow(y - radius); |  | 
|  217     } |  | 
|  218     float* writeRow = outImageL->getRow(0); |  | 
|  219  |  | 
|  220     for (int y = 0; y < imageL->height; y++) { |  | 
|  221         for (int x = 0; x < imageL->width; x++) { |  | 
|  222             float lSum = 0.0f; |  | 
|  223             for (int xx = -radius; xx <= radius; xx++) { |  | 
|  224                 int nx = x; |  | 
|  225                 int ny = y; |  | 
|  226  |  | 
|  227                 // We mirror at edges so that edge pixels that the filter weight
     ing still makes |  | 
|  228                 // sense. |  | 
|  229                 if (vertical) { |  | 
|  230                     ny += xx; |  | 
|  231                     if (ny < 0) { |  | 
|  232                         ny = -ny; |  | 
|  233                     } |  | 
|  234                     if (ny >= imageL->height) { |  | 
|  235                         ny = imageL->height + (imageL->height - ny - 1); |  | 
|  236                     } |  | 
|  237                 } else { |  | 
|  238                     nx += xx; |  | 
|  239                     if (nx < 0) { |  | 
|  240                         nx = -nx; |  | 
|  241                     } |  | 
|  242                     if (nx >= imageL->width) { |  | 
|  243                         nx = imageL->width + (imageL->width - nx - 1); |  | 
|  244                     } |  | 
|  245                 } |  | 
|  246  |  | 
|  247                 float weight = matrix[xx + radius]; |  | 
|  248                 lSum += rowPtrs[ny - y + radius][nx] * weight; |  | 
|  249             } |  | 
|  250             writeRow[x] = lSum; |  | 
|  251         } |  | 
|  252         // As we move down, scroll the row pointers down with us |  | 
|  253         for (int y = 0; y < matrixCount - 1; y++) |  | 
|  254         { |  | 
|  255             rowPtrs[y] = rowPtrs[y + 1]; |  | 
|  256         } |  | 
|  257         rowPtrs[matrixCount - 1] += imageL->width; |  | 
|  258         writeRow += imageL->width; |  | 
|  259     } |  | 
|  260 } |  | 
|  261  |  | 
|  262 float pmetric(const ImageLAB* baselineLAB, const ImageLAB* testLAB, SkTDArray<Sk
     IPoint>* poi) { |  | 
|  263     int width = baselineLAB->width; |  | 
|  264     int height = baselineLAB->height; |  | 
|  265     int maxLevels = (int)log2(width < height ? width : height); |  | 
|  266  |  | 
|  267     const float fov = M_PI / 180.0f * 45.0f; |  | 
|  268     float contrastSensitivityMax = contrast_sensitivity(3.248f, 100.0f); |  | 
|  269     float pixelsPerDegree = width / (2.0f * tanf(fov * 0.5f) * 180.0f / M_PI); |  | 
|  270  |  | 
|  271     ImageL3D baselineL(width, height, maxLevels); |  | 
|  272     ImageL3D testL(width, height, maxLevels); |  | 
|  273     ImageL scratchImageL(width, height); |  | 
|  274     float* cyclesPerDegree = SkNEW_ARRAY(float, maxLevels); |  | 
|  275     float* thresholdFactorFrequency = SkNEW_ARRAY(float, maxLevels - 2); |  | 
|  276     float* contrast = SkNEW_ARRAY(float, maxLevels - 2); |  | 
|  277  |  | 
|  278     lab_to_l(baselineLAB, baselineL.getLayer(0)); |  | 
|  279     lab_to_l(testLAB, testL.getLayer(0)); |  | 
|  280  |  | 
|  281     // Compute cpd - Cycles per degree on the pyramid |  | 
|  282     cyclesPerDegree[0] = 0.5f * pixelsPerDegree; |  | 
|  283     for (int levelIndex = 1; levelIndex < maxLevels; levelIndex++) { |  | 
|  284         cyclesPerDegree[levelIndex] = cyclesPerDegree[levelIndex - 1] * 0.5f; |  | 
|  285     } |  | 
|  286  |  | 
|  287     // Contrast sensitivity is based on image dimensions. Therefore it cannot be
      statically |  | 
|  288     // generated. |  | 
|  289     float* contrastSensitivityTable = SkNEW_ARRAY(float, maxLevels * 1000); |  | 
|  290     for (int levelIndex = 0; levelIndex < maxLevels; levelIndex++) { |  | 
|  291         for (int csLum = 0; csLum < 1000; csLum++) { |  | 
|  292            contrastSensitivityTable[levelIndex * 1000 + csLum] = |  | 
|  293            contrast_sensitivity(cyclesPerDegree[levelIndex], (float)csLum / 10.0
     f + 1e-5f); |  | 
|  294        } |  | 
|  295     } |  | 
|  296  |  | 
|  297     // Compute G - The convolved lum for the baseline |  | 
|  298     for (int levelIndex = 1; levelIndex < maxLevels; levelIndex++) { |  | 
|  299         convolve(baselineL.getLayer(levelIndex - 1), false, &scratchImageL); |  | 
|  300         convolve(&scratchImageL, true, baselineL.getLayer(levelIndex)); |  | 
|  301     } |  | 
|  302     for (int levelIndex = 1; levelIndex < maxLevels; levelIndex++) { |  | 
|  303         convolve(testL.getLayer(levelIndex - 1), false, &scratchImageL); |  | 
|  304         convolve(&scratchImageL, true, testL.getLayer(levelIndex)); |  | 
|  305     } |  | 
|  306  |  | 
|  307     // Compute F_freq - The elevation f |  | 
|  308     for (int levelIndex = 0; levelIndex < maxLevels - 2; levelIndex++) { |  | 
|  309         float cpd = cyclesPerDegree[levelIndex]; |  | 
|  310         thresholdFactorFrequency[levelIndex] = contrastSensitivityMax / |  | 
|  311                                                contrast_sensitivity(cpd, 100.0f)
     ; |  | 
|  312     } |  | 
|  313  |  | 
|  314     int failures = 0; |  | 
|  315     // Calculate F |  | 
|  316     for (int y = 0; y < height; y++) { |  | 
|  317         for (int x = 0; x < width; x++) { |  | 
|  318             float lBaseline; |  | 
|  319             float lTest; |  | 
|  320             baselineL.getLayer(0)->readPixel(x, y, &lBaseline); |  | 
|  321             testL.getLayer(0)->readPixel(x, y, &lTest); |  | 
|  322  |  | 
|  323             float avgLBaseline; |  | 
|  324             float avgLTest; |  | 
|  325             baselineL.getLayer(maxLevels - 1)->readPixel(x, y, &avgLBaseline); |  | 
|  326             testL.getLayer(maxLevels - 1)->readPixel(x, y, &avgLTest); |  | 
|  327  |  | 
|  328             float lAdapt = 0.5f * (avgLBaseline + avgLTest); |  | 
|  329             if (lAdapt < 1e-5) { |  | 
|  330                 lAdapt = 1e-5; |  | 
|  331             } |  | 
|  332  |  | 
|  333             float contrastSum = 0.0f; |  | 
|  334             for (int levelIndex = 0; levelIndex < maxLevels - 2; levelIndex++) { |  | 
|  335                 float baselineL0, baselineL1, baselineL2; |  | 
|  336                 float testL0, testL1, testL2; |  | 
|  337                 baselineL.getLayer(levelIndex + 0)->readPixel(x, y, &baselineL0)
     ; |  | 
|  338                 testL.    getLayer(levelIndex + 0)->readPixel(x, y, &testL0); |  | 
|  339                 baselineL.getLayer(levelIndex + 1)->readPixel(x, y, &baselineL1)
     ; |  | 
|  340                 testL.    getLayer(levelIndex + 1)->readPixel(x, y, &testL1); |  | 
|  341                 baselineL.getLayer(levelIndex + 2)->readPixel(x, y, &baselineL2)
     ; |  | 
|  342                 testL.    getLayer(levelIndex + 2)->readPixel(x, y, &testL2); |  | 
|  343  |  | 
|  344                 float baselineContrast1 = fabsf(baselineL0 - baselineL1); |  | 
|  345                 float testContrast1     = fabsf(testL0 - testL1); |  | 
|  346                 float numerator = (baselineContrast1 > testContrast1) ? |  | 
|  347                                    baselineContrast1 : testContrast1; |  | 
|  348  |  | 
|  349                 float baselineContrast2 = fabsf(baselineL2); |  | 
|  350                 float testContrast2     = fabsf(testL2); |  | 
|  351                 float denominator = (baselineContrast2 > testContrast2) ? |  | 
|  352                                     baselineContrast2 : testContrast2; |  | 
|  353  |  | 
|  354                 // Avoid divides by close to zero |  | 
|  355                 if (denominator < 1e-5) { |  | 
|  356                     denominator = 1e-5; |  | 
|  357                 } |  | 
|  358                 contrast[levelIndex] = numerator / denominator; |  | 
|  359                 contrastSum += contrast[levelIndex]; |  | 
|  360             } |  | 
|  361  |  | 
|  362             if (contrastSum < 1e-5) { |  | 
|  363                 contrastSum = 1e-5; |  | 
|  364             } |  | 
|  365  |  | 
|  366             float F = 0.0f; |  | 
|  367             for (int levelIndex = 0; levelIndex < maxLevels - 2; levelIndex++) { |  | 
|  368                 float contrastSensitivity = contrastSensitivityTable[levelIndex 
     * 1000 + |  | 
|  369                                                                      (int)(lAdap
     t * 10.0)]; |  | 
|  370                 float mask = SkPMetricUtil::get_visual_mask(contrast[levelIndex]
      * |  | 
|  371                                                             contrastSensitivity)
     ; |  | 
|  372  |  | 
|  373                 F += contrast[levelIndex] + |  | 
|  374                      thresholdFactorFrequency[levelIndex] * mask / contrastSum; |  | 
|  375             } |  | 
|  376  |  | 
|  377             if (F < 1.0f) { |  | 
|  378                 F = 1.0f; |  | 
|  379             } |  | 
|  380  |  | 
|  381             if (F > 10.0f) { |  | 
|  382                 F = 10.0f; |  | 
|  383             } |  | 
|  384  |  | 
|  385  |  | 
|  386             bool isFailure = false; |  | 
|  387             if (fabsf(lBaseline - lTest) > F * SkPMetricUtil::get_threshold_vs_i
     ntensity(lAdapt)) { |  | 
|  388                 isFailure = true; |  | 
|  389             } else { |  | 
|  390                 LAB baselineColor; |  | 
|  391                 LAB testColor; |  | 
|  392                 baselineLAB->readPixel(x, y, &baselineColor); |  | 
|  393                 testLAB->readPixel(x, y, &testColor); |  | 
|  394                 float contrastA = baselineColor.a - testColor.a; |  | 
|  395                 float contrastB = baselineColor.b - testColor.b; |  | 
|  396                 float colorScale = 1.0f; |  | 
|  397                 if (lAdapt < 10.0f) { |  | 
|  398                     colorScale = lAdapt / 10.0f; |  | 
|  399                 } |  | 
|  400                 colorScale *= colorScale; |  | 
|  401  |  | 
|  402                 if ((contrastA * contrastA + contrastB * contrastB) * colorScale
      > F) |  | 
|  403                 { |  | 
|  404                     isFailure = true; |  | 
|  405                 } |  | 
|  406             } |  | 
|  407  |  | 
|  408             if (isFailure) { |  | 
|  409                 failures++; |  | 
|  410                 poi->push()->set(x, y); |  | 
|  411             } |  | 
|  412         } |  | 
|  413     } |  | 
|  414  |  | 
|  415     SkDELETE_ARRAY(cyclesPerDegree); |  | 
|  416     SkDELETE_ARRAY(contrast); |  | 
|  417     SkDELETE_ARRAY(thresholdFactorFrequency); |  | 
|  418     SkDELETE_ARRAY(contrastSensitivityTable); |  | 
|  419     return 1.0 - (double)failures / (width * height); |  | 
|  420 } |  | 
|  421  |  | 
|  422 const char* SkPMetric::getName() { |  | 
|  423     return "perceptual"; |  | 
|  424 } |  | 
|  425  |  | 
|  426 int SkPMetric::queueDiff(SkBitmap* baseline, SkBitmap* test) { |  | 
|  427     double startTime = get_seconds(); |  | 
|  428     int diffID = fQueuedDiffs.count(); |  | 
|  429     QueuedDiff& diff = fQueuedDiffs.push_back(); |  | 
|  430     diff.result = 0.0; |  | 
|  431  |  | 
|  432     // Ensure the images are comparable |  | 
|  433     if (baseline->width() != test->width() || baseline->height() != test->height
     () || |  | 
|  434                     baseline->width() <= 0 || baseline->height() <= 0) { |  | 
|  435         diff.finished = true; |  | 
|  436         return diffID; |  | 
|  437     } |  | 
|  438  |  | 
|  439     ImageLAB baselineLAB(baseline->width(), baseline->height()); |  | 
|  440     ImageLAB testLAB(baseline->width(), baseline->height()); |  | 
|  441  |  | 
|  442     bitmap_to_cielab(baseline, &baselineLAB); |  | 
|  443     bitmap_to_cielab(test, &testLAB); |  | 
|  444  |  | 
|  445     diff.result = pmetric(&baselineLAB, &testLAB, &diff.poi); |  | 
|  446  |  | 
|  447     SkDebugf("Time: %f\n", (get_seconds() - startTime)); |  | 
|  448  |  | 
|  449     return diffID; |  | 
|  450 } |  | 
|  451  |  | 
|  452  |  | 
|  453 void SkPMetric::deleteDiff(int id) { |  | 
|  454  |  | 
|  455 } |  | 
|  456  |  | 
|  457 bool SkPMetric::isFinished(int id) { |  | 
|  458     return fQueuedDiffs[id].finished; |  | 
|  459 } |  | 
|  460  |  | 
|  461 double SkPMetric::getResult(int id) { |  | 
|  462     return fQueuedDiffs[id].result; |  | 
|  463 } |  | 
|  464  |  | 
|  465 int SkPMetric::getPointsOfInterestCount(int id) { |  | 
|  466     return fQueuedDiffs[id].poi.count(); |  | 
|  467 } |  | 
|  468  |  | 
|  469 SkIPoint* SkPMetric::getPointsOfInterest(int id) { |  | 
|  470     return fQueuedDiffs[id].poi.begin(); |  | 
|  471 } |  | 
| OLD | NEW |