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