|
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 inline uint32_t Avg2x2(uint32_t a, uint32_t b, uint32_t c, uint32_t d) |
|
19 { |
|
20 // Prepare half-adder work |
|
21 uint32_t sum = a ^ b ^ c; |
|
22 uint32_t carry = (a & b) | (a & c) | (b & c); |
|
23 |
|
24 // Before shifting, mask lower order bits of each byte to avoid underflow. |
|
25 uint32_t mask = 0xfefefefe; |
|
26 |
|
27 // Add d to sum and divide by 2. |
|
28 sum = (((sum ^ d) & mask) >> 1) + (sum & d); |
|
29 |
|
30 // Sum is now shifted into place relative to carry, add them together. |
|
31 return (((sum ^ carry) & mask) >> 1) + (sum & carry); |
|
32 } |
|
33 |
|
34 inline uint32_t Avg2(uint32_t a, uint32_t b) |
|
35 { |
|
36 // Prepare half-adder work |
|
37 uint32_t sum = a ^ b; |
|
38 uint32_t carry = (a & b); |
|
39 |
|
40 // Before shifting, mask lower order bits of each byte to avoid underflow. |
|
41 uint32_t mask = 0xfefefefe; |
|
42 |
|
43 // Add d to sum and divide by 2. |
|
44 return ((sum & mask) >> 1) + carry; |
|
45 } |
|
46 |
|
47 void |
|
48 ImageHalfScaler::ScaleForSize(const IntSize &aSize) |
|
49 { |
|
50 uint32_t horizontalDownscales = 0; |
|
51 uint32_t verticalDownscales = 0; |
|
52 |
|
53 IntSize scaleSize = mOrigSize; |
|
54 while ((scaleSize.height / 2) > aSize.height) { |
|
55 verticalDownscales++; |
|
56 scaleSize.height /= 2; |
|
57 } |
|
58 |
|
59 while ((scaleSize.width / 2) > aSize.width) { |
|
60 horizontalDownscales++; |
|
61 scaleSize.width /= 2; |
|
62 } |
|
63 |
|
64 if (scaleSize == mOrigSize) { |
|
65 return; |
|
66 } |
|
67 |
|
68 IntSize internalSurfSize; |
|
69 |
|
70 internalSurfSize.width = max(scaleSize.width, mOrigSize.width / 2); |
|
71 internalSurfSize.height = max(scaleSize.height, mOrigSize.height / 2); |
|
72 |
|
73 mStride = internalSurfSize.width * 4; |
|
74 if (mStride % 16) { |
|
75 mStride += 16 - (mStride % 16); |
|
76 } |
|
77 |
|
78 delete [] mDataStorage; |
|
79 // Allocate 15 bytes extra to make sure we can get 16 byte alignment. We |
|
80 // should add tools for this, see bug 751696. |
|
81 size_t bufLen = BufferSizeFromStrideAndHeight(mStride, internalSurfSize.height, 15); |
|
82 if (bufLen == 0) { |
|
83 mSize.SizeTo(0, 0); |
|
84 mDataStorage = nullptr; |
|
85 return; |
|
86 } |
|
87 mDataStorage = new uint8_t[bufLen]; |
|
88 |
|
89 if (uintptr_t(mDataStorage) % 16) { |
|
90 // Our storage does not start at a 16-byte boundary. Make sure mData does! |
|
91 mData = (uint8_t*)(uintptr_t(mDataStorage) + |
|
92 (16 - (uintptr_t(mDataStorage) % 16))); |
|
93 } else { |
|
94 mData = mDataStorage; |
|
95 } |
|
96 |
|
97 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 IntSize currentSampledSize = mOrigSize; |
|
105 uint32_t currentSampledStride = mOrigStride; |
|
106 uint8_t *currentSampledData = mOrigData; |
|
107 |
|
108 while (verticalDownscales && horizontalDownscales) { |
|
109 if (currentSampledSize.width % 2) { |
|
110 currentSampledSize.width -= 1; |
|
111 } |
|
112 if (currentSampledSize.height % 2) { |
|
113 currentSampledSize.height -= 1; |
|
114 } |
|
115 |
|
116 HalfImage2D(currentSampledData, currentSampledStride, currentSampledSize, |
|
117 mData, mStride); |
|
118 |
|
119 verticalDownscales--; |
|
120 horizontalDownscales--; |
|
121 currentSampledSize.width /= 2; |
|
122 currentSampledSize.height /= 2; |
|
123 currentSampledData = mData; |
|
124 currentSampledStride = mStride; |
|
125 } |
|
126 |
|
127 while (verticalDownscales) { |
|
128 if (currentSampledSize.height % 2) { |
|
129 currentSampledSize.height -= 1; |
|
130 } |
|
131 |
|
132 HalfImageVertical(currentSampledData, currentSampledStride, currentSampledSize, |
|
133 mData, mStride); |
|
134 |
|
135 verticalDownscales--; |
|
136 currentSampledSize.height /= 2; |
|
137 currentSampledData = mData; |
|
138 currentSampledStride = mStride; |
|
139 } |
|
140 |
|
141 |
|
142 while (horizontalDownscales) { |
|
143 if (currentSampledSize.width % 2) { |
|
144 currentSampledSize.width -= 1; |
|
145 } |
|
146 |
|
147 HalfImageHorizontal(currentSampledData, currentSampledStride, currentSampledSize, |
|
148 mData, mStride); |
|
149 |
|
150 horizontalDownscales--; |
|
151 currentSampledSize.width /= 2; |
|
152 currentSampledData = mData; |
|
153 currentSampledStride = mStride; |
|
154 } |
|
155 } |
|
156 |
|
157 void |
|
158 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 if (Factory::HasSSE2()) { |
|
164 HalfImage2D_SSE2(aSource, aSourceStride, aSourceSize, aDest, aDestStride); |
|
165 } else |
|
166 #endif |
|
167 { |
|
168 HalfImage2D_C(aSource, aSourceStride, aSourceSize, aDest, aDestStride); |
|
169 } |
|
170 } |
|
171 |
|
172 void |
|
173 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 if (Factory::HasSSE2()) { |
|
179 HalfImageVertical_SSE2(aSource, aSourceStride, aSourceSize, aDest, aDestStride); |
|
180 } else |
|
181 #endif |
|
182 { |
|
183 HalfImageVertical_C(aSource, aSourceStride, aSourceSize, aDest, aDestStride); |
|
184 } |
|
185 } |
|
186 |
|
187 void |
|
188 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 if (Factory::HasSSE2()) { |
|
194 HalfImageHorizontal_SSE2(aSource, aSourceStride, aSourceSize, aDest, aDestStride); |
|
195 } else |
|
196 #endif |
|
197 { |
|
198 HalfImageHorizontal_C(aSource, aSourceStride, aSourceSize, aDest, aDestStride); |
|
199 } |
|
200 } |
|
201 |
|
202 void |
|
203 ImageHalfScaler::HalfImage2D_C(uint8_t *aSource, int32_t aSourceStride, |
|
204 const IntSize &aSourceSize, uint8_t *aDest, |
|
205 uint32_t aDestStride) |
|
206 { |
|
207 for (int y = 0; y < aSourceSize.height; y += 2) { |
|
208 uint32_t *storage = (uint32_t*)(aDest + (y / 2) * aDestStride); |
|
209 for (int x = 0; x < aSourceSize.width; x += 2) { |
|
210 uint8_t *upperRow = aSource + (y * aSourceStride + x * 4); |
|
211 uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4); |
|
212 |
|
213 *storage++ = Avg2x2(*(uint32_t*)upperRow, *((uint32_t*)upperRow + 1), |
|
214 *(uint32_t*)lowerRow, *((uint32_t*)lowerRow + 1)); |
|
215 } |
|
216 } |
|
217 } |
|
218 |
|
219 void |
|
220 ImageHalfScaler::HalfImageVertical_C(uint8_t *aSource, int32_t aSourceStride, |
|
221 const IntSize &aSourceSize, uint8_t *aDest, |
|
222 uint32_t aDestStride) |
|
223 { |
|
224 for (int y = 0; y < aSourceSize.height; y += 2) { |
|
225 uint32_t *storage = (uint32_t*)(aDest + (y / 2) * aDestStride); |
|
226 for (int x = 0; x < aSourceSize.width; x++) { |
|
227 uint32_t *upperRow = (uint32_t*)(aSource + (y * aSourceStride + x * 4)); |
|
228 uint32_t *lowerRow = (uint32_t*)(aSource + ((y + 1) * aSourceStride + x * 4)); |
|
229 |
|
230 *storage++ = Avg2(*upperRow, *lowerRow); |
|
231 } |
|
232 } |
|
233 } |
|
234 |
|
235 void |
|
236 ImageHalfScaler::HalfImageHorizontal_C(uint8_t *aSource, int32_t aSourceStride, |
|
237 const IntSize &aSourceSize, uint8_t *aDest, |
|
238 uint32_t aDestStride) |
|
239 { |
|
240 for (int y = 0; y < aSourceSize.height; y++) { |
|
241 uint32_t *storage = (uint32_t*)(aDest + y * aDestStride); |
|
242 for (int x = 0; x < aSourceSize.width; x+= 2) { |
|
243 uint32_t *pixels = (uint32_t*)(aSource + (y * aSourceStride + x * 4)); |
|
244 |
|
245 *storage++ = Avg2(*pixels, *(pixels + 1)); |
|
246 } |
|
247 } |
|
248 } |
|
249 |
|
250 } |
|
251 } |