1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtGui module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qmemrotate_p.h" |
41 | #include "qpixellayout_p.h" |
42 | |
43 | QT_BEGIN_NAMESPACE |
44 | |
45 | static const int tileSize = 32; |
46 | |
47 | template <class T> |
48 | static |
49 | inline void qt_memrotate90_tiled(const T *src, int w, int h, int sstride, T *dest, int dstride) |
50 | { |
51 | sstride /= sizeof(T); |
52 | dstride /= sizeof(T); |
53 | |
54 | const int pack = sizeof(quint32) / sizeof(T); |
55 | const int unaligned = |
56 | qMin(uint((quintptr(dest) & (sizeof(quint32)-1)) / sizeof(T)), uint(h)); |
57 | const int restX = w % tileSize; |
58 | const int restY = (h - unaligned) % tileSize; |
59 | const int unoptimizedY = restY % pack; |
60 | const int numTilesX = w / tileSize + (restX > 0); |
61 | const int numTilesY = (h - unaligned) / tileSize + (restY >= pack); |
62 | |
63 | for (int tx = 0; tx < numTilesX; ++tx) { |
64 | const int startx = w - tx * tileSize - 1; |
65 | const int stopx = qMax(startx - tileSize, 0); |
66 | |
67 | if (unaligned) { |
68 | for (int x = startx; x >= stopx; --x) { |
69 | T *d = dest + (w - x - 1) * dstride; |
70 | for (int y = 0; y < unaligned; ++y) { |
71 | *d++ = src[y * sstride + x]; |
72 | } |
73 | } |
74 | } |
75 | |
76 | for (int ty = 0; ty < numTilesY; ++ty) { |
77 | const int starty = ty * tileSize + unaligned; |
78 | const int stopy = qMin(starty + tileSize, h - unoptimizedY); |
79 | |
80 | for (int x = startx; x >= stopx; --x) { |
81 | quint32 *d = reinterpret_cast<quint32*>(dest + (w - x - 1) * dstride + starty); |
82 | for (int y = starty; y < stopy; y += pack) { |
83 | quint32 c = src[y * sstride + x]; |
84 | for (int i = 1; i < pack; ++i) { |
85 | const int shift = (sizeof(T) * 8 * i); |
86 | const T color = src[(y + i) * sstride + x]; |
87 | c |= color << shift; |
88 | } |
89 | *d++ = c; |
90 | } |
91 | } |
92 | } |
93 | |
94 | if (unoptimizedY) { |
95 | const int starty = h - unoptimizedY; |
96 | for (int x = startx; x >= stopx; --x) { |
97 | T *d = dest + (w - x - 1) * dstride + starty; |
98 | for (int y = starty; y < h; ++y) { |
99 | *d++ = src[y * sstride + x]; |
100 | } |
101 | } |
102 | } |
103 | } |
104 | } |
105 | |
106 | template <class T> |
107 | static |
108 | inline void qt_memrotate90_tiled_unpacked(const T *src, int w, int h, int sstride, T *dest, |
109 | int dstride) |
110 | { |
111 | const int numTilesX = (w + tileSize - 1) / tileSize; |
112 | const int numTilesY = (h + tileSize - 1) / tileSize; |
113 | |
114 | for (int tx = 0; tx < numTilesX; ++tx) { |
115 | const int startx = w - tx * tileSize - 1; |
116 | const int stopx = qMax(startx - tileSize, 0); |
117 | |
118 | for (int ty = 0; ty < numTilesY; ++ty) { |
119 | const int starty = ty * tileSize; |
120 | const int stopy = qMin(starty + tileSize, h); |
121 | |
122 | for (int x = startx; x >= stopx; --x) { |
123 | T *d = (T *)((char*)dest + (w - x - 1) * dstride) + starty; |
124 | const char *s = (const char*)(src + x) + starty * sstride; |
125 | for (int y = starty; y < stopy; ++y) { |
126 | *d++ = *(const T *)(s); |
127 | s += sstride; |
128 | } |
129 | } |
130 | } |
131 | } |
132 | } |
133 | |
134 | template <class T> |
135 | static |
136 | inline void qt_memrotate270_tiled(const T *src, int w, int h, int sstride, T *dest, int dstride) |
137 | { |
138 | sstride /= sizeof(T); |
139 | dstride /= sizeof(T); |
140 | |
141 | const int pack = sizeof(quint32) / sizeof(T); |
142 | const int unaligned = |
143 | qMin(uint((quintptr(dest) & (sizeof(quint32)-1)) / sizeof(T)), uint(h)); |
144 | const int restX = w % tileSize; |
145 | const int restY = (h - unaligned) % tileSize; |
146 | const int unoptimizedY = restY % pack; |
147 | const int numTilesX = w / tileSize + (restX > 0); |
148 | const int numTilesY = (h - unaligned) / tileSize + (restY >= pack); |
149 | |
150 | for (int tx = 0; tx < numTilesX; ++tx) { |
151 | const int startx = tx * tileSize; |
152 | const int stopx = qMin(startx + tileSize, w); |
153 | |
154 | if (unaligned) { |
155 | for (int x = startx; x < stopx; ++x) { |
156 | T *d = dest + x * dstride; |
157 | for (int y = h - 1; y >= h - unaligned; --y) { |
158 | *d++ = src[y * sstride + x]; |
159 | } |
160 | } |
161 | } |
162 | |
163 | for (int ty = 0; ty < numTilesY; ++ty) { |
164 | const int starty = h - 1 - unaligned - ty * tileSize; |
165 | const int stopy = qMax(starty - tileSize, unoptimizedY); |
166 | |
167 | for (int x = startx; x < stopx; ++x) { |
168 | quint32 *d = reinterpret_cast<quint32*>(dest + x * dstride |
169 | + h - 1 - starty); |
170 | for (int y = starty; y >= stopy; y -= pack) { |
171 | quint32 c = src[y * sstride + x]; |
172 | for (int i = 1; i < pack; ++i) { |
173 | const int shift = (sizeof(T) * 8 * i); |
174 | const T color = src[(y - i) * sstride + x]; |
175 | c |= color << shift; |
176 | } |
177 | *d++ = c; |
178 | } |
179 | } |
180 | } |
181 | if (unoptimizedY) { |
182 | const int starty = unoptimizedY - 1; |
183 | for (int x = startx; x < stopx; ++x) { |
184 | T *d = dest + x * dstride + h - 1 - starty; |
185 | for (int y = starty; y >= 0; --y) { |
186 | *d++ = src[y * sstride + x]; |
187 | } |
188 | } |
189 | } |
190 | } |
191 | } |
192 | |
193 | template <class T> |
194 | static |
195 | inline void qt_memrotate270_tiled_unpacked(const T *src, int w, int h, int sstride, T *dest, |
196 | int dstride) |
197 | { |
198 | const int numTilesX = (w + tileSize - 1) / tileSize; |
199 | const int numTilesY = (h + tileSize - 1) / tileSize; |
200 | |
201 | for (int tx = 0; tx < numTilesX; ++tx) { |
202 | const int startx = tx * tileSize; |
203 | const int stopx = qMin(startx + tileSize, w); |
204 | |
205 | for (int ty = 0; ty < numTilesY; ++ty) { |
206 | const int starty = h - 1 - ty * tileSize; |
207 | const int stopy = qMax(starty - tileSize, 0); |
208 | |
209 | for (int x = startx; x < stopx; ++x) { |
210 | T *d = (T*)((char*)dest + x * dstride) + h - 1 - starty; |
211 | const char *s = (const char*)(src + x) + starty * sstride; |
212 | for (int y = starty; y >= stopy; --y) { |
213 | *d++ = *(const T*)s; |
214 | s -= sstride; |
215 | } |
216 | } |
217 | } |
218 | } |
219 | } |
220 | |
221 | |
222 | template <class T> |
223 | static |
224 | inline void qt_memrotate90_template(const T *src, int srcWidth, int srcHeight, int srcStride, |
225 | T *dest, int dstStride) |
226 | { |
227 | #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN |
228 | // packed algorithm assumes little endian and that sizeof(quint32)/sizeof(T) is an integer |
229 | if (sizeof(quint32) % sizeof(T) == 0) |
230 | qt_memrotate90_tiled<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); |
231 | else |
232 | #endif |
233 | qt_memrotate90_tiled_unpacked<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); |
234 | } |
235 | |
236 | template <> |
237 | inline void qt_memrotate90_template<quint32>(const quint32 *src, int w, int h, int sstride, quint32 *dest, int dstride) |
238 | { |
239 | // packed algorithm doesn't have any benefit for quint32 |
240 | qt_memrotate90_tiled_unpacked(src, w, h, sstride, dest, dstride); |
241 | } |
242 | |
243 | template <> |
244 | inline void qt_memrotate90_template<quint64>(const quint64 *src, int w, int h, int sstride, quint64 *dest, int dstride) |
245 | { |
246 | qt_memrotate90_tiled_unpacked(src, w, h, sstride, dest, dstride); |
247 | } |
248 | |
249 | template <class T> |
250 | static |
251 | inline void qt_memrotate180_template(const T *src, int w, int h, int sstride, T *dest, int dstride) |
252 | { |
253 | const char *s = (const char*)(src) + (h - 1) * sstride; |
254 | for (int dy = 0; dy < h; ++dy) { |
255 | T *d = reinterpret_cast<T*>((char *)(dest) + dy * dstride); |
256 | src = reinterpret_cast<const T*>(s); |
257 | for (int dx = 0; dx < w; ++dx) { |
258 | d[dx] = src[w - 1 - dx]; |
259 | } |
260 | s -= sstride; |
261 | } |
262 | } |
263 | |
264 | template <class T> |
265 | static |
266 | inline void qt_memrotate270_template(const T *src, int srcWidth, int srcHeight, int srcStride, |
267 | T *dest, int dstStride) |
268 | { |
269 | #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN |
270 | // packed algorithm assumes little endian and that sizeof(quint32)/sizeof(T) is an integer |
271 | if (sizeof(quint32) % sizeof(T) == 0) |
272 | qt_memrotate270_tiled<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); |
273 | else |
274 | #endif |
275 | qt_memrotate270_tiled_unpacked<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); |
276 | } |
277 | |
278 | template <> |
279 | inline void qt_memrotate270_template<quint32>(const quint32 *src, int w, int h, int sstride, quint32 *dest, int dstride) |
280 | { |
281 | // packed algorithm doesn't have any benefit for quint32 |
282 | qt_memrotate270_tiled_unpacked(src, w, h, sstride, dest, dstride); |
283 | } |
284 | |
285 | template <> |
286 | inline void qt_memrotate270_template<quint64>(const quint64 *src, int w, int h, int sstride, quint64 *dest, int dstride) |
287 | { |
288 | qt_memrotate270_tiled_unpacked(src, w, h, sstride, dest, dstride); |
289 | } |
290 | |
291 | #define QT_IMPL_MEMROTATE(type) \ |
292 | Q_GUI_EXPORT void qt_memrotate90(const type *src, int w, int h, int sstride, \ |
293 | type *dest, int dstride) \ |
294 | { \ |
295 | qt_memrotate90_template(src, w, h, sstride, dest, dstride); \ |
296 | } \ |
297 | Q_GUI_EXPORT void qt_memrotate180(const type *src, int w, int h, int sstride, \ |
298 | type *dest, int dstride) \ |
299 | { \ |
300 | qt_memrotate180_template(src, w, h, sstride, dest, dstride); \ |
301 | } \ |
302 | Q_GUI_EXPORT void qt_memrotate270(const type *src, int w, int h, int sstride, \ |
303 | type *dest, int dstride) \ |
304 | { \ |
305 | qt_memrotate270_template(src, w, h, sstride, dest, dstride); \ |
306 | } |
307 | |
308 | #define QT_IMPL_SIMPLE_MEMROTATE(type) \ |
309 | Q_GUI_EXPORT void qt_memrotate90(const type *src, int w, int h, int sstride, \ |
310 | type *dest, int dstride) \ |
311 | { \ |
312 | qt_memrotate90_tiled_unpacked(src, w, h, sstride, dest, dstride); \ |
313 | } \ |
314 | Q_GUI_EXPORT void qt_memrotate180(const type *src, int w, int h, int sstride, \ |
315 | type *dest, int dstride) \ |
316 | { \ |
317 | qt_memrotate180_template(src, w, h, sstride, dest, dstride); \ |
318 | } \ |
319 | Q_GUI_EXPORT void qt_memrotate270(const type *src, int w, int h, int sstride, \ |
320 | type *dest, int dstride) \ |
321 | { \ |
322 | qt_memrotate270_tiled_unpacked(src, w, h, sstride, dest, dstride); \ |
323 | } |
324 | |
325 | QT_IMPL_MEMROTATE(quint64) |
326 | QT_IMPL_MEMROTATE(quint32) |
327 | QT_IMPL_MEMROTATE(quint16) |
328 | QT_IMPL_MEMROTATE(quint24) |
329 | QT_IMPL_MEMROTATE(quint8) |
330 | |
331 | void qt_memrotate90_8(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
332 | { |
333 | qt_memrotate90(srcPixels, w, h, sbpl, destPixels, dbpl); |
334 | } |
335 | |
336 | void qt_memrotate180_8(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
337 | { |
338 | qt_memrotate180(srcPixels, w, h, sbpl, destPixels, dbpl); |
339 | } |
340 | |
341 | void qt_memrotate270_8(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
342 | { |
343 | qt_memrotate270(srcPixels, w, h, sbpl, destPixels, dbpl); |
344 | } |
345 | |
346 | void qt_memrotate90_16(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
347 | { |
348 | qt_memrotate90((const ushort *)srcPixels, w, h, sbpl, (ushort *)destPixels, dbpl); |
349 | } |
350 | |
351 | void qt_memrotate180_16(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
352 | { |
353 | qt_memrotate180((const ushort *)srcPixels, w, h, sbpl, (ushort *)destPixels, dbpl); |
354 | } |
355 | |
356 | void qt_memrotate270_16(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
357 | { |
358 | qt_memrotate270((const ushort *)srcPixels, w, h, sbpl, (ushort *)destPixels, dbpl); |
359 | } |
360 | |
361 | void qt_memrotate90_24(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
362 | { |
363 | qt_memrotate90((const quint24 *)srcPixels, w, h, sbpl, (quint24 *)destPixels, dbpl); |
364 | } |
365 | |
366 | void qt_memrotate180_24(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
367 | { |
368 | qt_memrotate180((const quint24 *)srcPixels, w, h, sbpl, (quint24 *)destPixels, dbpl); |
369 | } |
370 | |
371 | void qt_memrotate270_24(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
372 | { |
373 | qt_memrotate270((const quint24 *)srcPixels, w, h, sbpl, (quint24 *)destPixels, dbpl); |
374 | } |
375 | |
376 | void qt_memrotate90_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
377 | { |
378 | qt_memrotate90((const uint *)srcPixels, w, h, sbpl, (uint *)destPixels, dbpl); |
379 | } |
380 | |
381 | void qt_memrotate180_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
382 | { |
383 | qt_memrotate180((const uint *)srcPixels, w, h, sbpl, (uint *)destPixels, dbpl); |
384 | } |
385 | |
386 | void qt_memrotate270_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
387 | { |
388 | qt_memrotate270((const uint *)srcPixels, w, h, sbpl, (uint *)destPixels, dbpl); |
389 | } |
390 | |
391 | |
392 | void qt_memrotate90_64(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
393 | { |
394 | qt_memrotate90((const quint64 *)srcPixels, w, h, sbpl, (quint64 *)destPixels, dbpl); |
395 | } |
396 | |
397 | void qt_memrotate180_64(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
398 | { |
399 | qt_memrotate180((const quint64 *)srcPixels, w, h, sbpl, (quint64 *)destPixels, dbpl); |
400 | } |
401 | |
402 | void qt_memrotate270_64(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
403 | { |
404 | qt_memrotate270((const quint64 *)srcPixels, w, h, sbpl, (quint64 *)destPixels, dbpl); |
405 | } |
406 | |
407 | MemRotateFunc qMemRotateFunctions[QPixelLayout::BPPCount][3] = |
408 | // 90, 180, 270 |
409 | { |
410 | { nullptr, nullptr, nullptr }, // BPPNone, |
411 | { nullptr, nullptr, nullptr }, // BPP1MSB, |
412 | { nullptr, nullptr, nullptr }, // BPP1LSB, |
413 | { qt_memrotate90_8, qt_memrotate180_8, qt_memrotate270_8 }, // BPP8, |
414 | { qt_memrotate90_16, qt_memrotate180_16, qt_memrotate270_16 }, // BPP16, |
415 | { qt_memrotate90_24, qt_memrotate180_24, qt_memrotate270_24 }, // BPP24 |
416 | { qt_memrotate90_32, qt_memrotate180_32, qt_memrotate270_32 }, // BPP32 |
417 | { qt_memrotate90_64, qt_memrotate180_64, qt_memrotate270_64 }, // BPP64 |
418 | }; |
419 | |
420 | QT_END_NAMESPACE |
421 | |