gfx/2d/ImageScaling.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/2d/ImageScaling.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,251 @@
     1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "ImageScaling.h"
    1.10 +#include "2D.h"
    1.11 +#include "DataSurfaceHelpers.h"
    1.12 +
    1.13 +#include <math.h>
    1.14 +#include <algorithm>
    1.15 +
    1.16 +using namespace std;
    1.17 +
    1.18 +namespace mozilla {
    1.19 +namespace gfx {
    1.20 +
    1.21 +inline uint32_t Avg2x2(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
    1.22 +{
    1.23 +  // Prepare half-adder work
    1.24 +  uint32_t sum = a ^ b ^ c;
    1.25 +  uint32_t carry = (a & b) | (a & c) | (b & c);
    1.26 +
    1.27 +  // Before shifting, mask lower order bits of each byte to avoid underflow.
    1.28 +  uint32_t mask = 0xfefefefe;
    1.29 +
    1.30 +  // Add d to sum and divide by 2.
    1.31 +  sum = (((sum ^ d) & mask) >> 1) + (sum & d);
    1.32 +
    1.33 +  // Sum is now shifted into place relative to carry, add them together.
    1.34 +  return (((sum ^ carry) & mask) >> 1) + (sum & carry);
    1.35 +}
    1.36 +
    1.37 +inline uint32_t Avg2(uint32_t a, uint32_t b)
    1.38 +{
    1.39 +  // Prepare half-adder work
    1.40 +  uint32_t sum = a ^ b;
    1.41 +  uint32_t carry = (a & b);
    1.42 +
    1.43 +  // Before shifting, mask lower order bits of each byte to avoid underflow.
    1.44 +  uint32_t mask = 0xfefefefe;
    1.45 +
    1.46 +  // Add d to sum and divide by 2.
    1.47 +  return ((sum & mask) >> 1) + carry;
    1.48 +}
    1.49 +
    1.50 +void
    1.51 +ImageHalfScaler::ScaleForSize(const IntSize &aSize)
    1.52 +{
    1.53 +  uint32_t horizontalDownscales = 0;
    1.54 +  uint32_t verticalDownscales = 0;
    1.55 +
    1.56 +  IntSize scaleSize = mOrigSize;
    1.57 +  while ((scaleSize.height / 2) > aSize.height) {
    1.58 +    verticalDownscales++;
    1.59 +    scaleSize.height /= 2;
    1.60 +  }
    1.61 +
    1.62 +  while ((scaleSize.width / 2) > aSize.width) {
    1.63 +    horizontalDownscales++;
    1.64 +    scaleSize.width /= 2;
    1.65 +  }
    1.66 +
    1.67 +  if (scaleSize == mOrigSize) {
    1.68 +    return;
    1.69 +  }
    1.70 +
    1.71 +  IntSize internalSurfSize;
    1.72 +
    1.73 +  internalSurfSize.width = max(scaleSize.width, mOrigSize.width / 2);
    1.74 +  internalSurfSize.height = max(scaleSize.height, mOrigSize.height / 2);
    1.75 +
    1.76 +  mStride = internalSurfSize.width * 4;
    1.77 +  if (mStride % 16) {
    1.78 +    mStride += 16 - (mStride % 16);
    1.79 +  }
    1.80 +
    1.81 +  delete [] mDataStorage;
    1.82 +  // Allocate 15 bytes extra to make sure we can get 16 byte alignment. We
    1.83 +  // should add tools for this, see bug 751696.
    1.84 +  size_t bufLen = BufferSizeFromStrideAndHeight(mStride, internalSurfSize.height, 15);
    1.85 +  if (bufLen == 0) {
    1.86 +    mSize.SizeTo(0, 0);
    1.87 +    mDataStorage = nullptr;
    1.88 +    return;
    1.89 +  }
    1.90 +  mDataStorage = new uint8_t[bufLen];
    1.91 +
    1.92 +  if (uintptr_t(mDataStorage) % 16) {
    1.93 +    // Our storage does not start at a 16-byte boundary. Make sure mData does!
    1.94 +    mData = (uint8_t*)(uintptr_t(mDataStorage) +
    1.95 +      (16 - (uintptr_t(mDataStorage) % 16)));
    1.96 +  } else {
    1.97 +    mData = mDataStorage;
    1.98 +  }
    1.99 +
   1.100 +  mSize = scaleSize;
   1.101 +
   1.102 +  /* The surface we sample from might not be even sized, if it's not we will
   1.103 +   * ignore the last row/column. This means we lose some data but it keeps the
   1.104 +   * code very simple. There's also no perfect answer that provides a better
   1.105 +   * solution.
   1.106 +   */
   1.107 +  IntSize currentSampledSize = mOrigSize;
   1.108 +  uint32_t currentSampledStride = mOrigStride;
   1.109 +  uint8_t *currentSampledData = mOrigData;
   1.110 +  
   1.111 +  while (verticalDownscales && horizontalDownscales) {
   1.112 +    if (currentSampledSize.width % 2) {
   1.113 +      currentSampledSize.width -= 1;
   1.114 +    }
   1.115 +    if (currentSampledSize.height % 2) {
   1.116 +      currentSampledSize.height -= 1;
   1.117 +    }
   1.118 +
   1.119 +    HalfImage2D(currentSampledData, currentSampledStride, currentSampledSize,
   1.120 +                mData, mStride);
   1.121 +
   1.122 +    verticalDownscales--;
   1.123 +    horizontalDownscales--;
   1.124 +    currentSampledSize.width /= 2;
   1.125 +    currentSampledSize.height /= 2;
   1.126 +    currentSampledData = mData;
   1.127 +    currentSampledStride = mStride;
   1.128 +  }
   1.129 +
   1.130 +  while (verticalDownscales) {
   1.131 +    if (currentSampledSize.height % 2) {
   1.132 +      currentSampledSize.height -= 1;
   1.133 +    }
   1.134 +
   1.135 +    HalfImageVertical(currentSampledData, currentSampledStride, currentSampledSize,
   1.136 +                      mData, mStride);
   1.137 +
   1.138 +    verticalDownscales--;
   1.139 +    currentSampledSize.height /= 2;
   1.140 +    currentSampledData = mData;
   1.141 +    currentSampledStride = mStride;
   1.142 +  }
   1.143 +
   1.144 +
   1.145 +  while (horizontalDownscales) {
   1.146 +    if (currentSampledSize.width % 2) {
   1.147 +      currentSampledSize.width -= 1;
   1.148 +    }
   1.149 +
   1.150 +    HalfImageHorizontal(currentSampledData, currentSampledStride, currentSampledSize,
   1.151 +                        mData, mStride);
   1.152 +
   1.153 +    horizontalDownscales--;
   1.154 +    currentSampledSize.width /= 2;
   1.155 +    currentSampledData = mData;
   1.156 +    currentSampledStride = mStride;
   1.157 +  }
   1.158 +}
   1.159 +
   1.160 +void
   1.161 +ImageHalfScaler::HalfImage2D(uint8_t *aSource, int32_t aSourceStride,
   1.162 +                             const IntSize &aSourceSize, uint8_t *aDest,
   1.163 +                             uint32_t aDestStride)
   1.164 +{
   1.165 +#ifdef USE_SSE2
   1.166 +  if (Factory::HasSSE2()) {
   1.167 +    HalfImage2D_SSE2(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
   1.168 +  } else
   1.169 +#endif
   1.170 +  {
   1.171 +    HalfImage2D_C(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
   1.172 +  }
   1.173 +}
   1.174 +
   1.175 +void
   1.176 +ImageHalfScaler::HalfImageVertical(uint8_t *aSource, int32_t aSourceStride,
   1.177 +                                   const IntSize &aSourceSize, uint8_t *aDest,
   1.178 +                                   uint32_t aDestStride)
   1.179 +{
   1.180 +#ifdef USE_SSE2
   1.181 +  if (Factory::HasSSE2()) {
   1.182 +    HalfImageVertical_SSE2(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
   1.183 +  } else
   1.184 +#endif
   1.185 +  {
   1.186 +    HalfImageVertical_C(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
   1.187 +  }
   1.188 +}
   1.189 +
   1.190 +void
   1.191 +ImageHalfScaler::HalfImageHorizontal(uint8_t *aSource, int32_t aSourceStride,
   1.192 +                                     const IntSize &aSourceSize, uint8_t *aDest,
   1.193 +                                     uint32_t aDestStride)
   1.194 +{
   1.195 +#ifdef USE_SSE2
   1.196 +  if (Factory::HasSSE2()) {
   1.197 +    HalfImageHorizontal_SSE2(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
   1.198 +  } else
   1.199 +#endif
   1.200 +  {
   1.201 +    HalfImageHorizontal_C(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
   1.202 +  }
   1.203 +}
   1.204 +
   1.205 +void
   1.206 +ImageHalfScaler::HalfImage2D_C(uint8_t *aSource, int32_t aSourceStride,
   1.207 +                               const IntSize &aSourceSize, uint8_t *aDest,
   1.208 +                               uint32_t aDestStride)
   1.209 +{
   1.210 +  for (int y = 0; y < aSourceSize.height; y += 2) {
   1.211 +    uint32_t *storage = (uint32_t*)(aDest + (y / 2) * aDestStride);
   1.212 +    for (int x = 0; x < aSourceSize.width; x += 2) {
   1.213 +      uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
   1.214 +      uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
   1.215 +
   1.216 +      *storage++ = Avg2x2(*(uint32_t*)upperRow, *((uint32_t*)upperRow + 1),
   1.217 +                          *(uint32_t*)lowerRow, *((uint32_t*)lowerRow + 1));
   1.218 +    }
   1.219 +  }
   1.220 +}
   1.221 +
   1.222 +void
   1.223 +ImageHalfScaler::HalfImageVertical_C(uint8_t *aSource, int32_t aSourceStride,
   1.224 +                                     const IntSize &aSourceSize, uint8_t *aDest,
   1.225 +                                     uint32_t aDestStride)
   1.226 +{
   1.227 +  for (int y = 0; y < aSourceSize.height; y += 2) {
   1.228 +    uint32_t *storage = (uint32_t*)(aDest + (y / 2) * aDestStride);
   1.229 +    for (int x = 0; x < aSourceSize.width; x++) {
   1.230 +      uint32_t *upperRow = (uint32_t*)(aSource + (y * aSourceStride + x * 4));
   1.231 +      uint32_t *lowerRow = (uint32_t*)(aSource + ((y + 1) * aSourceStride + x * 4));
   1.232 +
   1.233 +      *storage++ = Avg2(*upperRow, *lowerRow);
   1.234 +    }
   1.235 +  }
   1.236 +}
   1.237 +
   1.238 +void
   1.239 +ImageHalfScaler::HalfImageHorizontal_C(uint8_t *aSource, int32_t aSourceStride,
   1.240 +                                       const IntSize &aSourceSize, uint8_t *aDest,
   1.241 +                                       uint32_t aDestStride)
   1.242 +{
   1.243 +  for (int y = 0; y < aSourceSize.height; y++) {
   1.244 +    uint32_t *storage = (uint32_t*)(aDest + y * aDestStride);
   1.245 +    for (int x = 0; x < aSourceSize.width;  x+= 2) {
   1.246 +      uint32_t *pixels = (uint32_t*)(aSource + (y * aSourceStride + x * 4));
   1.247 +
   1.248 +      *storage++ = Avg2(*pixels, *(pixels + 1));
   1.249 +    }
   1.250 +  }
   1.251 +}
   1.252 +
   1.253 +}
   1.254 +}

mercurial