tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TestQcms.cpp (11544B)


      1 /* vim:set ts=2 sw=2 sts=2 et: */
      2 /* Any copyright is dedicated to the Public Domain.
      3 * http://creativecommons.org/publicdomain/zero/1.0/
      4 */
      5 
      6 #include "gtest/gtest.h"
      7 #include "gtest/MozGTestBench.h"
      8 #include "gmock/gmock.h"
      9 
     10 #include "mozilla/UniquePtr.h"
     11 #include "mozilla/SSE.h"
     12 #include "mozilla/arm.h"
     13 #include "qcms.h"
     14 #include "qcmsint.h"
     15 
     16 #include <cmath>
     17 
     18 /* SSEv1 is only included in non-Windows or non-x86-64-bit builds. */
     19 #if defined(MOZILLA_MAY_SUPPORT_SSE) && \
     20    (!(defined(_MSC_VER) && defined(_M_AMD64)))
     21 #  define QCMS_MAY_SUPPORT_SSE
     22 #endif
     23 
     24 using namespace mozilla;
     25 
     26 static bool CmpRgbChannel(const uint8_t* aRef, const uint8_t* aTest,
     27                          size_t aIndex) {
     28  return std::abs(static_cast<int8_t>(aRef[aIndex] - aTest[aIndex])) <= 1;
     29 }
     30 
     31 template <bool kSwapRB, bool kHasAlpha>
     32 static bool CmpRgbBufferImpl(const uint8_t* aRefBuffer,
     33                             const uint8_t* aTestBuffer, size_t aPixels) {
     34  const size_t pixelSize = kHasAlpha ? 4 : 3;
     35  if (memcmp(aRefBuffer, aTestBuffer, aPixels * pixelSize) == 0) {
     36    return true;
     37  }
     38 
     39  const size_t kRIndex = kSwapRB ? 2 : 0;
     40  const size_t kGIndex = 1;
     41  const size_t kBIndex = kSwapRB ? 0 : 2;
     42  const size_t kAIndex = 3;
     43 
     44  size_t remaining = aPixels;
     45  const uint8_t* ref = aRefBuffer;
     46  const uint8_t* test = aTestBuffer;
     47  while (remaining > 0) {
     48    if (!CmpRgbChannel(ref, test, kRIndex) ||
     49        !CmpRgbChannel(ref, test, kGIndex) ||
     50        !CmpRgbChannel(ref, test, kBIndex) ||
     51        (kHasAlpha && ref[kAIndex] != test[kAIndex])) {
     52      EXPECT_EQ(test[kRIndex], ref[kRIndex]);
     53      EXPECT_EQ(test[kGIndex], ref[kGIndex]);
     54      EXPECT_EQ(test[kBIndex], ref[kBIndex]);
     55      if (kHasAlpha) {
     56        EXPECT_EQ(test[kAIndex], ref[kAIndex]);
     57      }
     58      return false;
     59    }
     60 
     61    --remaining;
     62    ref += pixelSize;
     63    test += pixelSize;
     64  }
     65 
     66  return true;
     67 }
     68 
     69 template <bool kSwapRB, bool kHasAlpha>
     70 static size_t GetRgbInputBufferImpl(UniquePtr<uint8_t[]>& aOutBuffer) {
     71  const uint8_t colorSamples[] = {0, 5, 16, 43, 101, 127, 182, 255};
     72  const size_t colorSampleMax = sizeof(colorSamples) / sizeof(uint8_t);
     73  const size_t pixelSize = kHasAlpha ? 4 : 3;
     74  const size_t pixelCount = colorSampleMax * colorSampleMax * 256 * 3;
     75 
     76  aOutBuffer = MakeUnique<uint8_t[]>(pixelCount * pixelSize);
     77  if (!aOutBuffer) {
     78    return 0;
     79  }
     80 
     81  const size_t kRIndex = kSwapRB ? 2 : 0;
     82  const size_t kGIndex = 1;
     83  const size_t kBIndex = kSwapRB ? 0 : 2;
     84  const size_t kAIndex = 3;
     85 
     86  // Sample every red pixel value with a subset of green and blue.
     87  uint8_t* color = aOutBuffer.get();
     88  for (uint16_t r = 0; r < 256; ++r) {
     89    for (uint8_t g : colorSamples) {
     90      for (uint8_t b : colorSamples) {
     91        color[kRIndex] = r;
     92        color[kGIndex] = g;
     93        color[kBIndex] = b;
     94        if (kHasAlpha) {
     95          color[kAIndex] = 0x80;
     96        }
     97        color += pixelSize;
     98      }
     99    }
    100  }
    101 
    102  // Sample every green pixel value with a subset of red and blue.
    103  for (uint8_t r : colorSamples) {
    104    for (uint16_t g = 0; g < 256; ++g) {
    105      for (uint8_t b : colorSamples) {
    106        color[kRIndex] = r;
    107        color[kGIndex] = g;
    108        color[kBIndex] = b;
    109        if (kHasAlpha) {
    110          color[kAIndex] = 0x80;
    111        }
    112        color += pixelSize;
    113      }
    114    }
    115  }
    116 
    117  // Sample every blue pixel value with a subset of red and green.
    118  for (uint8_t r : colorSamples) {
    119    for (uint8_t g : colorSamples) {
    120      for (uint16_t b = 0; b < 256; ++b) {
    121        color[kRIndex] = r;
    122        color[kGIndex] = g;
    123        color[kBIndex] = b;
    124        if (kHasAlpha) {
    125          color[kAIndex] = 0x80;
    126        }
    127        color += pixelSize;
    128      }
    129    }
    130  }
    131 
    132  return pixelCount;
    133 }
    134 
    135 static size_t GetRgbInputBuffer(UniquePtr<uint8_t[]>& aOutBuffer) {
    136  return GetRgbInputBufferImpl<false, false>(aOutBuffer);
    137 }
    138 
    139 static size_t GetRgbaInputBuffer(UniquePtr<uint8_t[]>& aOutBuffer) {
    140  return GetRgbInputBufferImpl<false, true>(aOutBuffer);
    141 }
    142 
    143 static size_t GetBgraInputBuffer(UniquePtr<uint8_t[]>& aOutBuffer) {
    144  return GetRgbInputBufferImpl<true, true>(aOutBuffer);
    145 }
    146 
    147 static bool CmpRgbBuffer(const uint8_t* aRefBuffer, const uint8_t* aTestBuffer,
    148                         size_t aPixels) {
    149  return CmpRgbBufferImpl<false, false>(aRefBuffer, aTestBuffer, aPixels);
    150 }
    151 
    152 static bool CmpRgbaBuffer(const uint8_t* aRefBuffer, const uint8_t* aTestBuffer,
    153                          size_t aPixels) {
    154  return CmpRgbBufferImpl<false, true>(aRefBuffer, aTestBuffer, aPixels);
    155 }
    156 
    157 static bool CmpBgraBuffer(const uint8_t* aRefBuffer, const uint8_t* aTestBuffer,
    158                          size_t aPixels) {
    159  return CmpRgbBufferImpl<true, true>(aRefBuffer, aTestBuffer, aPixels);
    160 }
    161 
    162 static void ClearRgbBuffer(uint8_t* aBuffer, size_t aPixels) {
    163  if (aBuffer) {
    164    memset(aBuffer, 0, aPixels * 3);
    165  }
    166 }
    167 
    168 static void ClearRgbaBuffer(uint8_t* aBuffer, size_t aPixels) {
    169  if (aBuffer) {
    170    memset(aBuffer, 0, aPixels * 4);
    171  }
    172 }
    173 
    174 static UniquePtr<uint8_t[]> GetRgbOutputBuffer(size_t aPixels) {
    175  UniquePtr<uint8_t[]> buffer = MakeUnique<uint8_t[]>(aPixels * 3);
    176  ClearRgbBuffer(buffer.get(), aPixels);
    177  return buffer;
    178 }
    179 
    180 static UniquePtr<uint8_t[]> GetRgbaOutputBuffer(size_t aPixels) {
    181  UniquePtr<uint8_t[]> buffer = MakeUnique<uint8_t[]>(aPixels * 4);
    182  ClearRgbaBuffer(buffer.get(), aPixels);
    183  return buffer;
    184 }
    185 
    186 class GfxQcms_ProfilePairBase : public ::testing::Test {
    187 protected:
    188  GfxQcms_ProfilePairBase()
    189      : mInProfile(nullptr),
    190        mOutProfile(nullptr),
    191        mTransform(nullptr),
    192        mPixels(0),
    193        mStorageType(QCMS_DATA_RGB_8),
    194        mPrecache(false) {}
    195 
    196  void SetUp() override {
    197    // XXX: This means that we can't have qcms v2 unit test
    198    //      without changing the qcms API.
    199    qcms_enable_iccv4();
    200  }
    201 
    202  void TearDown() override {
    203    if (mInProfile) {
    204      qcms_profile_release(mInProfile);
    205    }
    206    if (mOutProfile) {
    207      qcms_profile_release(mOutProfile);
    208    }
    209    if (mTransform) {
    210      qcms_transform_release(mTransform);
    211    }
    212  }
    213 
    214  bool SetTransform(qcms_transform* aTransform) {
    215    if (mTransform) {
    216      qcms_transform_release(mTransform);
    217    }
    218    mTransform = aTransform;
    219    return !!mTransform;
    220  }
    221 
    222  bool SetTransform(qcms_data_type aType) {
    223    return SetTransform(qcms_transform_create(mInProfile, aType, mOutProfile,
    224                                              aType, QCMS_INTENT_DEFAULT));
    225  }
    226 
    227  bool SetBuffers(qcms_data_type aType) {
    228    switch (aType) {
    229      case QCMS_DATA_RGB_8:
    230        mPixels = GetRgbInputBuffer(mInput);
    231        mRef = GetRgbOutputBuffer(mPixels);
    232        mOutput = GetRgbOutputBuffer(mPixels);
    233        break;
    234      case QCMS_DATA_RGBA_8:
    235        mPixels = GetRgbaInputBuffer(mInput);
    236        mRef = GetRgbaOutputBuffer(mPixels);
    237        mOutput = GetRgbaOutputBuffer(mPixels);
    238        break;
    239      case QCMS_DATA_BGRA_8:
    240        mPixels = GetBgraInputBuffer(mInput);
    241        mRef = GetRgbaOutputBuffer(mPixels);
    242        mOutput = GetRgbaOutputBuffer(mPixels);
    243        break;
    244      default:
    245        MOZ_ASSERT_UNREACHABLE("Unknown type!");
    246        break;
    247    }
    248 
    249    mStorageType = aType;
    250    return mInput && mOutput && mRef && mPixels > 0u;
    251  }
    252 
    253  void ClearOutputBuffer() {
    254    switch (mStorageType) {
    255      case QCMS_DATA_RGB_8:
    256        ClearRgbBuffer(mOutput.get(), mPixels);
    257        break;
    258      case QCMS_DATA_RGBA_8:
    259      case QCMS_DATA_BGRA_8:
    260        ClearRgbaBuffer(mOutput.get(), mPixels);
    261        break;
    262      default:
    263        MOZ_ASSERT_UNREACHABLE("Unknown type!");
    264        break;
    265    }
    266  }
    267 
    268  void ProduceRef(transform_fn_t aFn) {
    269    aFn(mTransform, mInput.get(), mRef.get(), mPixels);
    270  }
    271 
    272  void CopyInputToRef() {
    273    size_t pixelSize = 0;
    274    switch (mStorageType) {
    275      case QCMS_DATA_RGB_8:
    276        pixelSize = 3;
    277        break;
    278      case QCMS_DATA_RGBA_8:
    279      case QCMS_DATA_BGRA_8:
    280        pixelSize = 4;
    281        break;
    282      default:
    283        MOZ_ASSERT_UNREACHABLE("Unknown type!");
    284        break;
    285    }
    286 
    287    memcpy(mRef.get(), mInput.get(), mPixels * pixelSize);
    288  }
    289 
    290  void ProduceOutput(transform_fn_t aFn) {
    291    ClearOutputBuffer();
    292    aFn(mTransform, mInput.get(), mOutput.get(), mPixels);
    293  }
    294 
    295  bool VerifyOutput(const UniquePtr<uint8_t[]>& aBuf) {
    296    switch (mStorageType) {
    297      case QCMS_DATA_RGB_8:
    298        return CmpRgbBuffer(aBuf.get(), mOutput.get(), mPixels);
    299      case QCMS_DATA_RGBA_8:
    300        return CmpRgbaBuffer(aBuf.get(), mOutput.get(), mPixels);
    301      case QCMS_DATA_BGRA_8:
    302        return CmpBgraBuffer(aBuf.get(), mOutput.get(), mPixels);
    303      default:
    304        MOZ_ASSERT_UNREACHABLE("Unknown type!");
    305        break;
    306    }
    307 
    308    return false;
    309  }
    310 
    311  bool ProduceVerifyOutput(transform_fn_t aFn) {
    312    ProduceOutput(aFn);
    313    return VerifyOutput(mRef);
    314  }
    315 
    316  void PrecacheOutput() {
    317    qcms_profile_precache_output_transform(mOutProfile);
    318    mPrecache = true;
    319  }
    320 
    321  qcms_profile* mInProfile;
    322  qcms_profile* mOutProfile;
    323  qcms_transform* mTransform;
    324 
    325  UniquePtr<uint8_t[]> mInput;
    326  UniquePtr<uint8_t[]> mOutput;
    327  UniquePtr<uint8_t[]> mRef;
    328  size_t mPixels;
    329  qcms_data_type mStorageType;
    330  bool mPrecache;
    331 };
    332 
    333 class GfxQcms_sRGB_To_sRGB : public GfxQcms_ProfilePairBase {
    334 protected:
    335  void SetUp() override {
    336    GfxQcms_ProfilePairBase::SetUp();
    337    mInProfile = qcms_profile_sRGB();
    338    mOutProfile = qcms_profile_sRGB();
    339  }
    340 };
    341 
    342 class GfxQcms_sRGB_To_SamsungSyncmaster : public GfxQcms_ProfilePairBase {
    343 protected:
    344  void SetUp() override {
    345    GfxQcms_ProfilePairBase::SetUp();
    346    mInProfile = qcms_profile_sRGB();
    347    mOutProfile = qcms_profile_from_path("lcms_samsung_syncmaster.icc");
    348  }
    349 };
    350 
    351 class GfxQcms_sRGB_To_ThinkpadW540 : public GfxQcms_ProfilePairBase {
    352 protected:
    353  void SetUp() override {
    354    GfxQcms_ProfilePairBase::SetUp();
    355    mInProfile = qcms_profile_sRGB();
    356    mOutProfile = qcms_profile_from_path("lcms_thinkpad_w540.icc");
    357  }
    358 };
    359 
    360 TEST_F(GfxQcms_sRGB_To_sRGB, TransformIdentity) {
    361  PrecacheOutput();
    362  SetBuffers(QCMS_DATA_RGB_8);
    363  SetTransform(QCMS_DATA_RGB_8);
    364  qcms_transform_data(mTransform, mInput.get(), mOutput.get(), mPixels);
    365  EXPECT_TRUE(VerifyOutput(mInput));
    366 }
    367 
    368 class GfxQcmsPerf_Base : public GfxQcms_sRGB_To_ThinkpadW540 {
    369 protected:
    370  explicit GfxQcmsPerf_Base(qcms_data_type aType) { mStorageType = aType; }
    371 
    372  void TransformPerf() { ProduceRef(qcms_transform_data_rgb_out_lut_precache); }
    373 
    374  void TransformPlatformPerf() {
    375    qcms_transform_data(mTransform, mInput.get(), mRef.get(), mPixels);
    376  }
    377 
    378  void SetUp() override {
    379    GfxQcms_sRGB_To_ThinkpadW540::SetUp();
    380    PrecacheOutput();
    381    SetBuffers(mStorageType);
    382    SetTransform(mStorageType);
    383  }
    384 };
    385 
    386 class GfxQcmsPerf_Rgb : public GfxQcmsPerf_Base {
    387 protected:
    388  GfxQcmsPerf_Rgb() : GfxQcmsPerf_Base(QCMS_DATA_RGB_8) {}
    389 };
    390 
    391 class GfxQcmsPerf_Rgba : public GfxQcmsPerf_Base {
    392 protected:
    393  GfxQcmsPerf_Rgba() : GfxQcmsPerf_Base(QCMS_DATA_RGBA_8) {}
    394 };
    395 
    396 class GfxQcmsPerf_Bgra : public GfxQcmsPerf_Base {
    397 protected:
    398  GfxQcmsPerf_Bgra() : GfxQcmsPerf_Base(QCMS_DATA_BGRA_8) {}
    399 };
    400 
    401 MOZ_GTEST_BENCH_F(GfxQcmsPerf_Rgb, TransformC, [this] { TransformPerf(); });
    402 MOZ_GTEST_BENCH_F(GfxQcmsPerf_Rgb, TransformPlatform,
    403                  [this] { TransformPlatformPerf(); });
    404 MOZ_GTEST_BENCH_F(GfxQcmsPerf_Rgba, TransformC, [this] { TransformPerf(); });
    405 MOZ_GTEST_BENCH_F(GfxQcmsPerf_Rgba, TransformPlatform,
    406                  [this] { TransformPlatformPerf(); });
    407 MOZ_GTEST_BENCH_F(GfxQcmsPerf_Bgra, TransformC, [this] { TransformPerf(); });
    408 MOZ_GTEST_BENCH_F(GfxQcmsPerf_Bgra, TransformPlatform,
    409                  [this] { TransformPlatformPerf(); });