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