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 "private/qmemrotate_p.h" |
41 | |
42 | QT_BEGIN_NAMESPACE |
43 | |
44 | static const int tileSize = 32; |
45 | |
46 | template<class T> |
47 | static inline void qt_memrotate90_tiled(const T *src, int w, int h, int isstride, T *dest, int idstride) |
48 | { |
49 | const qsizetype sstride = isstride / sizeof(T); |
50 | const qsizetype dstride = idstride / sizeof(T); |
51 | |
52 | const int pack = sizeof(quint32) / sizeof(T); |
53 | const int unaligned = |
54 | qMin(a: uint((quintptr(dest) & (sizeof(quint32)-1)) / sizeof(T)), b: uint(h)); |
55 | const int restX = w % tileSize; |
56 | const int restY = (h - unaligned) % tileSize; |
57 | const int unoptimizedY = restY % pack; |
58 | const int numTilesX = w / tileSize + (restX > 0); |
59 | const int numTilesY = (h - unaligned) / tileSize + (restY >= pack); |
60 | |
61 | for (int tx = 0; tx < numTilesX; ++tx) { |
62 | const int startx = w - tx * tileSize - 1; |
63 | const int stopx = qMax(a: startx - tileSize, b: 0); |
64 | |
65 | if (unaligned) { |
66 | for (int x = startx; x >= stopx; --x) { |
67 | T *d = dest + (w - x - 1) * dstride; |
68 | for (int y = 0; y < unaligned; ++y) { |
69 | *d++ = src[y * sstride + x]; |
70 | } |
71 | } |
72 | } |
73 | |
74 | for (int ty = 0; ty < numTilesY; ++ty) { |
75 | const int starty = ty * tileSize + unaligned; |
76 | const int stopy = qMin(a: starty + tileSize, b: h - unoptimizedY); |
77 | |
78 | for (int x = startx; x >= stopx; --x) { |
79 | quint32 *d = reinterpret_cast<quint32*>(dest + (w - x - 1) * dstride + starty); |
80 | for (int y = starty; y < stopy; y += pack) { |
81 | quint32 c = src[y * sstride + x]; |
82 | for (int i = 1; i < pack; ++i) { |
83 | const int shift = (sizeof(T) * 8 * i); |
84 | const T color = src[(y + i) * sstride + x]; |
85 | c |= color << shift; |
86 | } |
87 | *d++ = c; |
88 | } |
89 | } |
90 | } |
91 | |
92 | if (unoptimizedY) { |
93 | const int starty = h - unoptimizedY; |
94 | for (int x = startx; x >= stopx; --x) { |
95 | T *d = dest + (w - x - 1) * dstride + starty; |
96 | for (int y = starty; y < h; ++y) { |
97 | *d++ = src[y * sstride + x]; |
98 | } |
99 | } |
100 | } |
101 | } |
102 | } |
103 | |
104 | template<class T> |
105 | static inline void qt_memrotate90_tiled_unpacked(const T *src, int w, int h, int isstride, T *dest, int idstride) |
106 | { |
107 | const qsizetype sstride = isstride; |
108 | const qsizetype dstride = idstride; |
109 | const int numTilesX = (w + tileSize - 1) / tileSize; |
110 | const int numTilesY = (h + tileSize - 1) / tileSize; |
111 | |
112 | for (int tx = 0; tx < numTilesX; ++tx) { |
113 | const int startx = w - tx * tileSize - 1; |
114 | const int stopx = qMax(a: startx - tileSize, b: 0); |
115 | |
116 | for (int ty = 0; ty < numTilesY; ++ty) { |
117 | const int starty = ty * tileSize; |
118 | const int stopy = qMin(a: starty + tileSize, b: h); |
119 | |
120 | for (int x = startx; x >= stopx; --x) { |
121 | T *d = (T *)((char*)dest + (w - x - 1) * dstride) + starty; |
122 | const char *s = (const char*)(src + x) + starty * sstride; |
123 | for (int y = starty; y < stopy; ++y) { |
124 | *d++ = *(const T *)(s); |
125 | s += sstride; |
126 | } |
127 | } |
128 | } |
129 | } |
130 | } |
131 | |
132 | template<class T> |
133 | static inline void qt_memrotate270_tiled(const T *src, int w, int h, int isstride, T *dest, int idstride) |
134 | { |
135 | const qsizetype sstride = isstride / sizeof(T); |
136 | const qsizetype dstride = idstride / sizeof(T); |
137 | |
138 | const int pack = sizeof(quint32) / sizeof(T); |
139 | const int unaligned = |
140 | qMin(a: uint((quintptr(dest) & (sizeof(quint32)-1)) / sizeof(T)), b: uint(h)); |
141 | const int restX = w % tileSize; |
142 | const int restY = (h - unaligned) % tileSize; |
143 | const int unoptimizedY = restY % pack; |
144 | const int numTilesX = w / tileSize + (restX > 0); |
145 | const int numTilesY = (h - unaligned) / tileSize + (restY >= pack); |
146 | |
147 | for (int tx = 0; tx < numTilesX; ++tx) { |
148 | const int startx = tx * tileSize; |
149 | const int stopx = qMin(a: startx + tileSize, b: w); |
150 | |
151 | if (unaligned) { |
152 | for (int x = startx; x < stopx; ++x) { |
153 | T *d = dest + x * dstride; |
154 | for (int y = h - 1; y >= h - unaligned; --y) { |
155 | *d++ = src[y * sstride + x]; |
156 | } |
157 | } |
158 | } |
159 | |
160 | for (int ty = 0; ty < numTilesY; ++ty) { |
161 | const int starty = h - 1 - unaligned - ty * tileSize; |
162 | const int stopy = qMax(a: starty - tileSize, b: unoptimizedY); |
163 | |
164 | for (int x = startx; x < stopx; ++x) { |
165 | quint32 *d = reinterpret_cast<quint32*>(dest + x * dstride |
166 | + h - 1 - starty); |
167 | for (int y = starty; y >= stopy; y -= pack) { |
168 | quint32 c = src[y * sstride + x]; |
169 | for (int i = 1; i < pack; ++i) { |
170 | const int shift = (sizeof(T) * 8 * i); |
171 | const T color = src[(y - i) * sstride + x]; |
172 | c |= color << shift; |
173 | } |
174 | *d++ = c; |
175 | } |
176 | } |
177 | } |
178 | if (unoptimizedY) { |
179 | const int starty = unoptimizedY - 1; |
180 | for (int x = startx; x < stopx; ++x) { |
181 | T *d = dest + x * dstride + h - 1 - starty; |
182 | for (int y = starty; y >= 0; --y) { |
183 | *d++ = src[y * sstride + x]; |
184 | } |
185 | } |
186 | } |
187 | } |
188 | } |
189 | |
190 | template<class T> |
191 | static inline void qt_memrotate270_tiled_unpacked(const T *src, int w, int h, int isstride, T *dest, int idstride) |
192 | { |
193 | const qsizetype sstride = isstride; |
194 | const qsizetype dstride = idstride; |
195 | const int numTilesX = (w + tileSize - 1) / tileSize; |
196 | const int numTilesY = (h + tileSize - 1) / tileSize; |
197 | |
198 | for (int tx = 0; tx < numTilesX; ++tx) { |
199 | const int startx = tx * tileSize; |
200 | const int stopx = qMin(a: startx + tileSize, b: w); |
201 | |
202 | for (int ty = 0; ty < numTilesY; ++ty) { |
203 | const int starty = h - 1 - ty * tileSize; |
204 | const int stopy = qMax(a: starty - tileSize, b: 0); |
205 | |
206 | for (int x = startx; x < stopx; ++x) { |
207 | T *d = (T*)((char*)dest + x * dstride) + h - 1 - starty; |
208 | const char *s = (const char*)(src + x) + starty * sstride; |
209 | for (int y = starty; y >= stopy; --y) { |
210 | *d++ = *(const T*)s; |
211 | s -= sstride; |
212 | } |
213 | } |
214 | } |
215 | } |
216 | } |
217 | |
218 | |
219 | template <class T> |
220 | static |
221 | inline void qt_memrotate90_template(const T *src, int srcWidth, int srcHeight, int srcStride, |
222 | T *dest, int dstStride) |
223 | { |
224 | #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN |
225 | // packed algorithm assumes little endian and that sizeof(quint32)/sizeof(T) is an integer |
226 | if (sizeof(quint32) % sizeof(T) == 0) |
227 | qt_memrotate90_tiled<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); |
228 | else |
229 | #endif |
230 | qt_memrotate90_tiled_unpacked<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); |
231 | } |
232 | |
233 | template <> |
234 | inline void qt_memrotate90_template<quint32>(const quint32 *src, int w, int h, int sstride, quint32 *dest, int dstride) |
235 | { |
236 | // packed algorithm doesn't have any benefit for quint32 |
237 | qt_memrotate90_tiled_unpacked(src, w, h, isstride: sstride, dest, idstride: dstride); |
238 | } |
239 | |
240 | template <> |
241 | inline void qt_memrotate90_template<quint64>(const quint64 *src, int w, int h, int sstride, quint64 *dest, int dstride) |
242 | { |
243 | qt_memrotate90_tiled_unpacked(src, w, h, isstride: sstride, dest, idstride: dstride); |
244 | } |
245 | |
246 | template<class T> |
247 | static inline void qt_memrotate180_template(const T *src, int w, int h, int isstride, T *dest, int idstride) |
248 | { |
249 | const qsizetype sstride = isstride; |
250 | const qsizetype dstride = idstride; |
251 | |
252 | const char *s = (const char*)(src) + (h - 1) * sstride; |
253 | for (int dy = 0; dy < h; ++dy) { |
254 | T *d = reinterpret_cast<T*>((char *)(dest) + dy * dstride); |
255 | src = reinterpret_cast<const T*>(s); |
256 | for (int dx = 0; dx < w; ++dx) { |
257 | d[dx] = src[w - 1 - dx]; |
258 | } |
259 | s -= sstride; |
260 | } |
261 | } |
262 | |
263 | template <class T> |
264 | static |
265 | inline void qt_memrotate270_template(const T *src, int srcWidth, int srcHeight, int srcStride, |
266 | T *dest, int dstStride) |
267 | { |
268 | #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN |
269 | // packed algorithm assumes little endian and that sizeof(quint32)/sizeof(T) is an integer |
270 | if (sizeof(quint32) % sizeof(T) == 0) |
271 | qt_memrotate270_tiled<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); |
272 | else |
273 | #endif |
274 | qt_memrotate270_tiled_unpacked<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); |
275 | } |
276 | |
277 | template <> |
278 | inline void qt_memrotate270_template<quint32>(const quint32 *src, int w, int h, int sstride, quint32 *dest, int dstride) |
279 | { |
280 | // packed algorithm doesn't have any benefit for quint32 |
281 | qt_memrotate270_tiled_unpacked(src, w, h, isstride: sstride, dest, idstride: dstride); |
282 | } |
283 | |
284 | template <> |
285 | inline void qt_memrotate270_template<quint64>(const quint64 *src, int w, int h, int sstride, quint64 *dest, int dstride) |
286 | { |
287 | qt_memrotate270_tiled_unpacked(src, w, h, isstride: sstride, dest, idstride: dstride); |
288 | } |
289 | |
290 | #define QT_IMPL_MEMROTATE(type) \ |
291 | Q_GUI_EXPORT void qt_memrotate90(const type *src, int w, int h, int sstride, \ |
292 | type *dest, int dstride) \ |
293 | { \ |
294 | qt_memrotate90_template(src, w, h, sstride, dest, dstride); \ |
295 | } \ |
296 | Q_GUI_EXPORT void qt_memrotate180(const type *src, int w, int h, int sstride, \ |
297 | type *dest, int dstride) \ |
298 | { \ |
299 | qt_memrotate180_template(src, w, h, sstride, dest, dstride); \ |
300 | } \ |
301 | Q_GUI_EXPORT void qt_memrotate270(const type *src, int w, int h, int sstride, \ |
302 | type *dest, int dstride) \ |
303 | { \ |
304 | qt_memrotate270_template(src, w, h, sstride, dest, dstride); \ |
305 | } |
306 | |
307 | #define QT_IMPL_SIMPLE_MEMROTATE(type) \ |
308 | Q_GUI_EXPORT void qt_memrotate90(const type *src, int w, int h, int sstride, \ |
309 | type *dest, int dstride) \ |
310 | { \ |
311 | qt_memrotate90_tiled_unpacked(src, w, h, sstride, dest, dstride); \ |
312 | } \ |
313 | Q_GUI_EXPORT void qt_memrotate180(const type *src, int w, int h, int sstride, \ |
314 | type *dest, int dstride) \ |
315 | { \ |
316 | qt_memrotate180_template(src, w, h, sstride, dest, dstride); \ |
317 | } \ |
318 | Q_GUI_EXPORT void qt_memrotate270(const type *src, int w, int h, int sstride, \ |
319 | type *dest, int dstride) \ |
320 | { \ |
321 | qt_memrotate270_tiled_unpacked(src, w, h, sstride, dest, dstride); \ |
322 | } |
323 | |
324 | QT_IMPL_MEMROTATE(quint64) |
325 | QT_IMPL_MEMROTATE(quint32) |
326 | QT_IMPL_MEMROTATE(quint16) |
327 | QT_IMPL_MEMROTATE(quint24) |
328 | QT_IMPL_MEMROTATE(quint8) |
329 | |
330 | void qt_memrotate90_8(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
331 | { |
332 | qt_memrotate90(src: srcPixels, w, h, sstride: sbpl, dest: destPixels, dstride: dbpl); |
333 | } |
334 | |
335 | void qt_memrotate180_8(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
336 | { |
337 | qt_memrotate180(src: srcPixels, w, h, sstride: sbpl, dest: destPixels, dstride: dbpl); |
338 | } |
339 | |
340 | void qt_memrotate270_8(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
341 | { |
342 | qt_memrotate270(src: srcPixels, w, h, sstride: sbpl, dest: destPixels, dstride: dbpl); |
343 | } |
344 | |
345 | void qt_memrotate90_16(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
346 | { |
347 | qt_memrotate90(src: (const ushort *)srcPixels, w, h, sstride: sbpl, dest: (ushort *)destPixels, dstride: dbpl); |
348 | } |
349 | |
350 | void qt_memrotate180_16(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
351 | { |
352 | qt_memrotate180(src: (const ushort *)srcPixels, w, h, sstride: sbpl, dest: (ushort *)destPixels, dstride: dbpl); |
353 | } |
354 | |
355 | void qt_memrotate270_16(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
356 | { |
357 | qt_memrotate270(src: (const ushort *)srcPixels, w, h, sstride: sbpl, dest: (ushort *)destPixels, dstride: dbpl); |
358 | } |
359 | |
360 | void qt_memrotate90_24(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
361 | { |
362 | qt_memrotate90(src: (const quint24 *)srcPixels, w, h, sstride: sbpl, dest: (quint24 *)destPixels, dstride: dbpl); |
363 | } |
364 | |
365 | void qt_memrotate180_24(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
366 | { |
367 | qt_memrotate180(src: (const quint24 *)srcPixels, w, h, sstride: sbpl, dest: (quint24 *)destPixels, dstride: dbpl); |
368 | } |
369 | |
370 | void qt_memrotate270_24(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
371 | { |
372 | qt_memrotate270(src: (const quint24 *)srcPixels, w, h, sstride: sbpl, dest: (quint24 *)destPixels, dstride: dbpl); |
373 | } |
374 | |
375 | void qt_memrotate90_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
376 | { |
377 | qt_memrotate90(src: (const uint *)srcPixels, w, h, sstride: sbpl, dest: (uint *)destPixels, dstride: dbpl); |
378 | } |
379 | |
380 | void qt_memrotate180_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
381 | { |
382 | qt_memrotate180(src: (const uint *)srcPixels, w, h, sstride: sbpl, dest: (uint *)destPixels, dstride: dbpl); |
383 | } |
384 | |
385 | void qt_memrotate270_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
386 | { |
387 | qt_memrotate270(src: (const uint *)srcPixels, w, h, sstride: sbpl, dest: (uint *)destPixels, dstride: dbpl); |
388 | } |
389 | |
390 | |
391 | void qt_memrotate90_64(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
392 | { |
393 | qt_memrotate90(src: (const quint64 *)srcPixels, w, h, sstride: sbpl, dest: (quint64 *)destPixels, dstride: dbpl); |
394 | } |
395 | |
396 | void qt_memrotate180_64(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
397 | { |
398 | qt_memrotate180(src: (const quint64 *)srcPixels, w, h, sstride: sbpl, dest: (quint64 *)destPixels, dstride: dbpl); |
399 | } |
400 | |
401 | void qt_memrotate270_64(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) |
402 | { |
403 | qt_memrotate270(src: (const quint64 *)srcPixels, w, h, sstride: sbpl, dest: (quint64 *)destPixels, dstride: dbpl); |
404 | } |
405 | |
406 | MemRotateFunc qMemRotateFunctions[QPixelLayout::BPPCount][3] = |
407 | // 90, 180, 270 |
408 | { |
409 | { nullptr, nullptr, nullptr }, // BPPNone, |
410 | { nullptr, nullptr, nullptr }, // BPP1MSB, |
411 | { nullptr, nullptr, nullptr }, // BPP1LSB, |
412 | { qt_memrotate90_8, qt_memrotate180_8, qt_memrotate270_8 }, // BPP8, |
413 | { qt_memrotate90_16, qt_memrotate180_16, qt_memrotate270_16 }, // BPP16, |
414 | { qt_memrotate90_24, qt_memrotate180_24, qt_memrotate270_24 }, // BPP24 |
415 | { qt_memrotate90_32, qt_memrotate180_32, qt_memrotate270_32 }, // BPP32 |
416 | { qt_memrotate90_64, qt_memrotate180_64, qt_memrotate270_64 }, // BPP64 |
417 | }; |
418 | |
419 | QT_END_NAMESPACE |
420 | |