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