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 +}