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