tor-browser

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

ImageScaling.cpp (8022B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "ImageScaling.h"
      8 #include "2D.h"
      9 #include "DataSurfaceHelpers.h"
     10 
     11 #include <math.h>
     12 #include <algorithm>
     13 
     14 namespace mozilla {
     15 namespace gfx {
     16 
     17 inline uint32_t Avg2x2(uint32_t a, uint32_t b, uint32_t c, uint32_t d) {
     18  // Prepare half-adder work
     19  uint32_t sum = a ^ b ^ c;
     20  uint32_t carry = (a & b) | (a & c) | (b & c);
     21 
     22  // Before shifting, mask lower order bits of each byte to avoid underflow.
     23  uint32_t mask = 0xfefefefe;
     24 
     25  // Add d to sum and divide by 2.
     26  sum = (((sum ^ d) & mask) >> 1) + (sum & d);
     27 
     28  // Sum is now shifted into place relative to carry, add them together.
     29  return (((sum ^ carry) & mask) >> 1) + (sum & carry);
     30 }
     31 
     32 inline uint32_t Avg2(uint32_t a, uint32_t b) {
     33  // Prepare half-adder work
     34  uint32_t sum = a ^ b;
     35  uint32_t carry = (a & b);
     36 
     37  // Before shifting, mask lower order bits of each byte to avoid underflow.
     38  uint32_t mask = 0xfefefefe;
     39 
     40  // Add d to sum and divide by 2.
     41  return ((sum & mask) >> 1) + carry;
     42 }
     43 
     44 void ImageHalfScaler::ScaleForSize(const IntSize& aSize) {
     45  uint32_t horizontalDownscales = 0;
     46  uint32_t verticalDownscales = 0;
     47 
     48  IntSize scaleSize = mOrigSize;
     49  while ((scaleSize.height / 2) > aSize.height) {
     50    verticalDownscales++;
     51    scaleSize.height /= 2;
     52  }
     53 
     54  while ((scaleSize.width / 2) > aSize.width) {
     55    horizontalDownscales++;
     56    scaleSize.width /= 2;
     57  }
     58 
     59  if (scaleSize == mOrigSize) {
     60    return;
     61  }
     62 
     63  delete[] mDataStorage;
     64 
     65  IntSize internalSurfSize;
     66  internalSurfSize.width = std::max(scaleSize.width, mOrigSize.width / 2);
     67  internalSurfSize.height = std::max(scaleSize.height, mOrigSize.height / 2);
     68 
     69  size_t bufLen = 0;
     70  mStride = GetAlignedStride<16>(internalSurfSize.width, 4);
     71  if (mStride > 0) {
     72    // Allocate 15 bytes extra to make sure we can get 16 byte alignment. We
     73    // should add tools for this, see bug 751696.
     74    bufLen =
     75        BufferSizeFromStrideAndHeight(mStride, internalSurfSize.height, 15);
     76  }
     77 
     78  if (bufLen == 0) {
     79    mSize.SizeTo(0, 0);
     80    mDataStorage = nullptr;
     81    return;
     82  }
     83  mDataStorage = new uint8_t[bufLen];
     84 
     85  if (uintptr_t(mDataStorage) % 16) {
     86    // Our storage does not start at a 16-byte boundary. Make sure mData does!
     87    mData = (uint8_t*)(uintptr_t(mDataStorage) +
     88                       (16 - (uintptr_t(mDataStorage) % 16)));
     89  } else {
     90    mData = mDataStorage;
     91  }
     92 
     93  mSize = scaleSize;
     94 
     95  /* The surface we sample from might not be even sized, if it's not we will
     96   * ignore the last row/column. This means we lose some data but it keeps the
     97   * code very simple. There's also no perfect answer that provides a better
     98   * solution.
     99   */
    100  IntSize currentSampledSize = mOrigSize;
    101  uint32_t currentSampledStride = mOrigStride;
    102  uint8_t* currentSampledData = mOrigData;
    103 
    104  while (verticalDownscales && horizontalDownscales) {
    105    if (currentSampledSize.width % 2) {
    106      currentSampledSize.width -= 1;
    107    }
    108    if (currentSampledSize.height % 2) {
    109      currentSampledSize.height -= 1;
    110    }
    111 
    112    HalfImage2D(currentSampledData, currentSampledStride, currentSampledSize,
    113                mData, mStride);
    114 
    115    verticalDownscales--;
    116    horizontalDownscales--;
    117    currentSampledSize.width /= 2;
    118    currentSampledSize.height /= 2;
    119    currentSampledData = mData;
    120    currentSampledStride = mStride;
    121  }
    122 
    123  while (verticalDownscales) {
    124    if (currentSampledSize.height % 2) {
    125      currentSampledSize.height -= 1;
    126    }
    127 
    128    HalfImageVertical(currentSampledData, currentSampledStride,
    129                      currentSampledSize, mData, mStride);
    130 
    131    verticalDownscales--;
    132    currentSampledSize.height /= 2;
    133    currentSampledData = mData;
    134    currentSampledStride = mStride;
    135  }
    136 
    137  while (horizontalDownscales) {
    138    if (currentSampledSize.width % 2) {
    139      currentSampledSize.width -= 1;
    140    }
    141 
    142    HalfImageHorizontal(currentSampledData, currentSampledStride,
    143                        currentSampledSize, mData, mStride);
    144 
    145    horizontalDownscales--;
    146    currentSampledSize.width /= 2;
    147    currentSampledData = mData;
    148    currentSampledStride = mStride;
    149  }
    150 }
    151 
    152 void ImageHalfScaler::HalfImage2D(uint8_t* aSource, int32_t aSourceStride,
    153                                  const IntSize& aSourceSize, uint8_t* aDest,
    154                                  uint32_t aDestStride) {
    155 #ifdef USE_SSE2
    156  if (Factory::HasSSE2()) {
    157    HalfImage2D_SSE2(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
    158  } else
    159 #endif
    160  {
    161    HalfImage2D_C(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
    162  }
    163 }
    164 
    165 void ImageHalfScaler::HalfImageVertical(uint8_t* aSource, int32_t aSourceStride,
    166                                        const IntSize& aSourceSize,
    167                                        uint8_t* aDest, uint32_t aDestStride) {
    168 #ifdef USE_SSE2
    169  if (Factory::HasSSE2()) {
    170    HalfImageVertical_SSE2(aSource, aSourceStride, aSourceSize, aDest,
    171                           aDestStride);
    172  } else
    173 #endif
    174  {
    175    HalfImageVertical_C(aSource, aSourceStride, aSourceSize, aDest,
    176                        aDestStride);
    177  }
    178 }
    179 
    180 void ImageHalfScaler::HalfImageHorizontal(uint8_t* aSource,
    181                                          int32_t aSourceStride,
    182                                          const IntSize& aSourceSize,
    183                                          uint8_t* aDest,
    184                                          uint32_t aDestStride) {
    185 #ifdef USE_SSE2
    186  if (Factory::HasSSE2()) {
    187    HalfImageHorizontal_SSE2(aSource, aSourceStride, aSourceSize, aDest,
    188                             aDestStride);
    189  } else
    190 #endif
    191  {
    192    HalfImageHorizontal_C(aSource, aSourceStride, aSourceSize, aDest,
    193                          aDestStride);
    194  }
    195 }
    196 
    197 void ImageHalfScaler::HalfImage2D_C(uint8_t* aSource, int32_t aSourceStride,
    198                                    const IntSize& aSourceSize, uint8_t* aDest,
    199                                    uint32_t aDestStride) {
    200  for (int y = 0; y < aSourceSize.height; y += 2) {
    201    uint32_t* storage = (uint32_t*)(aDest + (y / 2) * aDestStride);
    202    for (int x = 0; x < aSourceSize.width; x += 2) {
    203      uint8_t* upperRow = aSource + (y * aSourceStride + x * 4);
    204      uint8_t* lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
    205 
    206      *storage++ = Avg2x2(*(uint32_t*)upperRow, *((uint32_t*)upperRow + 1),
    207                          *(uint32_t*)lowerRow, *((uint32_t*)lowerRow + 1));
    208    }
    209  }
    210 }
    211 
    212 void ImageHalfScaler::HalfImageVertical_C(uint8_t* aSource,
    213                                          int32_t aSourceStride,
    214                                          const IntSize& aSourceSize,
    215                                          uint8_t* aDest,
    216                                          uint32_t aDestStride) {
    217  for (int y = 0; y < aSourceSize.height; y += 2) {
    218    uint32_t* storage = (uint32_t*)(aDest + (y / 2) * aDestStride);
    219    for (int x = 0; x < aSourceSize.width; x++) {
    220      uint32_t* upperRow = (uint32_t*)(aSource + (y * aSourceStride + x * 4));
    221      uint32_t* lowerRow =
    222          (uint32_t*)(aSource + ((y + 1) * aSourceStride + x * 4));
    223 
    224      *storage++ = Avg2(*upperRow, *lowerRow);
    225    }
    226  }
    227 }
    228 
    229 void ImageHalfScaler::HalfImageHorizontal_C(uint8_t* aSource,
    230                                            int32_t aSourceStride,
    231                                            const IntSize& aSourceSize,
    232                                            uint8_t* aDest,
    233                                            uint32_t aDestStride) {
    234  for (int y = 0; y < aSourceSize.height; y++) {
    235    uint32_t* storage = (uint32_t*)(aDest + y * aDestStride);
    236    for (int x = 0; x < aSourceSize.width; x += 2) {
    237      uint32_t* pixels = (uint32_t*)(aSource + (y * aSourceStride + x * 4));
    238 
    239      *storage++ = Avg2(*pixels, *(pixels + 1));
    240    }
    241  }
    242 }
    243 
    244 }  // namespace gfx
    245 }  // namespace mozilla