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
Avg2x2(uint32_t a,uint32_t b,uint32_t c,uint32_t d)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
Avg2(uint32_t a,uint32_t b)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
ScaleForSize(const IntSize & aSize)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 delete [] mDataStorage;
69
70 IntSize internalSurfSize;
71 internalSurfSize.width = max(scaleSize.width, mOrigSize.width / 2);
72 internalSurfSize.height = max(scaleSize.height, mOrigSize.height / 2);
73
74 size_t bufLen = 0;
75 mStride = GetAlignedStride<16>(internalSurfSize.width, 4);
76 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 bufLen = BufferSizeFromStrideAndHeight(mStride, internalSurfSize.height, 15);
80 }
81
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
HalfImage2D(uint8_t * aSource,int32_t aSourceStride,const IntSize & aSourceSize,uint8_t * aDest,uint32_t aDestStride)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
HalfImageVertical(uint8_t * aSource,int32_t aSourceStride,const IntSize & aSourceSize,uint8_t * aDest,uint32_t aDestStride)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
HalfImageHorizontal(uint8_t * aSource,int32_t aSourceStride,const IntSize & aSourceSize,uint8_t * aDest,uint32_t aDestStride)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
HalfImage2D_C(uint8_t * aSource,int32_t aSourceStride,const IntSize & aSourceSize,uint8_t * aDest,uint32_t aDestStride)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
HalfImageVertical_C(uint8_t * aSource,int32_t aSourceStride,const IntSize & aSourceSize,uint8_t * aDest,uint32_t aDestStride)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
HalfImageHorizontal_C(uint8_t * aSource,int32_t aSourceStride,const IntSize & aSourceSize,uint8_t * aDest,uint32_t aDestStride)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 } // namespace gfx
251 } // namespace mozilla
252