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