133cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp/*
233cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp *  Copyright 2012 The LibYuv Project Authors. All rights reserved.
333cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp *
433cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp *  Use of this source code is governed by a BSD-style license
533cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp *  that can be found in the LICENSE file in the root of the source
633cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp *  tree. An additional intellectual property rights grant can be found
733cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp *  in the file PATENTS.  All contributing project authors may
833cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp *  be found in the AUTHORS file in the root of the source tree.
933cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp */
1033cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp
1133cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#include "libyuv/rotate.h"
1233cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp
1333cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#include "libyuv/cpu_id.h"
1433cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#include "libyuv/convert.h"
1533cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#include "libyuv/planar_functions.h"
1633cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#include "libyuv/row.h"
1733cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp
1833cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#ifdef __cplusplus
1933cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkampnamespace libyuv {
2033cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkampextern "C" {
2133cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#endif
2233cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp
2333cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp// ARGBScale has a function to copy pixels to a row, striding each source
2433cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp// pixel by a constant.
2533cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#if !defined(YUV_DISABLE_ASM) && (defined(_M_IX86) || \
2633cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  defined(__x86_64__) || defined(__i386__))
2733cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#define HAS_SCALEARGBROWDOWNEVEN_SSE2
2833cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkampvoid ScaleARGBRowDownEven_SSE2(const uint8* src_ptr, int src_stride,
2933cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp                               int src_stepx,
3033cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp                               uint8* dst_ptr, int dst_width);
3133cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#endif
3233cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkampvoid ScaleARGBRowDownEven_C(const uint8* src_ptr, int,
3333cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp                            int src_stepx,
3433cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp                            uint8* dst_ptr, int dst_width);
3533cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp
3633cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkampstatic void ARGBTranspose(const uint8* src, int src_stride,
3733cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp                          uint8* dst, int dst_stride,
3833cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp                          int width, int height) {
3933cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  void (*ScaleARGBRowDownEven)(const uint8* src_ptr, int src_stride,
4033cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp      int src_step, uint8* dst_ptr, int dst_width) = ScaleARGBRowDownEven_C;
4133cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#if defined(HAS_SCALEARGBROWDOWNEVEN_SSE2)
4233cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  if (TestCpuFlag(kCpuHasSSE2) &&
4333cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp      IS_ALIGNED(height, 4) &&  // width of dest.
4433cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp      IS_ALIGNED(dst, 16) && IS_ALIGNED(dst_stride, 16)) {
4533cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    ScaleARGBRowDownEven = ScaleARGBRowDownEven_SSE2;
4633cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  }
4733cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#endif
4833cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp
4933cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  int src_pixel_step = src_stride / 4;
5033cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  for (int i = 0; i < width; ++i) {  // column of source to row of dest.
5133cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    ScaleARGBRowDownEven(src, 0, src_pixel_step, dst, height);
5233cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    dst += dst_stride;
5333cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    src += 4;
5433cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  }
5533cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp}
5633cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp
5733cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkampvoid ARGBRotate90(const uint8* src, int src_stride,
5833cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp                  uint8* dst, int dst_stride,
5933cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp                  int width, int height) {
6033cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  // Rotate by 90 is a ARGBTranspose with the source read
6133cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  // from bottom to top. So set the source pointer to the end
6233cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  // of the buffer and flip the sign of the source stride.
6333cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  src += src_stride * (height - 1);
6433cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  src_stride = -src_stride;
6533cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  ARGBTranspose(src, src_stride, dst, dst_stride, width, height);
6633cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp}
6733cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp
6833cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkampvoid ARGBRotate270(const uint8* src, int src_stride,
6933cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp                    uint8* dst, int dst_stride,
7033cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp                    int width, int height) {
7133cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  // Rotate by 270 is a ARGBTranspose with the destination written
7233cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  // from bottom to top. So set the destination pointer to the end
7333cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  // of the buffer and flip the sign of the destination stride.
7433cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  dst += dst_stride * (width - 1);
7533cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  dst_stride = -dst_stride;
7633cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  ARGBTranspose(src, src_stride, dst, dst_stride, width, height);
7733cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp}
7833cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp
7933cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkampvoid ARGBRotate180(const uint8* src, int src_stride,
8033cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp                   uint8* dst, int dst_stride,
8133cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp                   int width, int height) {
8233cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  void (*ARGBMirrorRow)(const uint8* src, uint8* dst, int width) =
8333cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp      ARGBMirrorRow_C;
8433cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#if defined(HAS_ARGBMIRRORROW_SSSE3)
8533cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 4) &&
8633cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp      IS_ALIGNED(src, 16) && IS_ALIGNED(src_stride, 16) &&
8733cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp      IS_ALIGNED(dst, 16) && IS_ALIGNED(dst_stride, 16)) {
8833cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    ARGBMirrorRow = ARGBMirrorRow_SSSE3;
8933cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  }
9033cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#endif
9133cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C;
9233cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#if defined(HAS_COPYROW_NEON)
9333cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width * 4, 64)) {
9433cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    CopyRow = CopyRow_NEON;
9533cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  }
9633cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#endif
9733cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#if defined(HAS_COPYROW_X86)
9833cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  if (TestCpuFlag(kCpuHasX86)) {
9933cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    CopyRow = CopyRow_X86;
10033cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  }
10133cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#endif
10233cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#if defined(HAS_COPYROW_SSE2)
10333cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width * 4, 32) &&
10433cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp      IS_ALIGNED(src, 16) && IS_ALIGNED(src_stride, 16) &&
10533cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp      IS_ALIGNED(dst, 16) && IS_ALIGNED(dst_stride, 16)) {
10633cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    CopyRow = CopyRow_SSE2;
10733cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  }
10833cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#endif
10933cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  if (width * 4 > kMaxStride) {
11033cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    return;
11133cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  }
11233cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  // Swap first and last row and mirror the content. Uses a temporary row.
11333cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  SIMD_ALIGNED(uint8 row[kMaxStride]);
11433cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  const uint8* src_bot = src + src_stride * (height - 1);
11533cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  uint8* dst_bot = dst + dst_stride * (height - 1);
11633cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  int half_height = (height + 1) >> 1;
11733cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  // Odd height will harmlessly mirror the middle row twice.
11833cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  for (int y = 0; y < half_height; ++y) {
11933cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    ARGBMirrorRow(src, row, width);  // Mirror first row into a buffer
12033cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    src += src_stride;
12133cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    ARGBMirrorRow(src_bot, dst, width);  // Mirror last row into first row
12233cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    dst += dst_stride;
12333cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    CopyRow(row, dst_bot, width * 4);  // Copy first mirrored row into last
12433cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    src_bot -= src_stride;
12533cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    dst_bot -= dst_stride;
12633cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  }
12733cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp}
12833cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp
12933cfdeb7b267ab635413797fffb046b73272f7ecHendrik DahlkampLIBYUV_API
13033cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkampint ARGBRotate(const uint8* src_argb, int src_stride_argb,
13133cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp               uint8* dst_argb, int dst_stride_argb,
13233cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp               int width, int height,
13333cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp               RotationMode mode) {
13433cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  if (!src_argb || width <= 0 || height == 0 || !dst_argb) {
13533cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    return -1;
13633cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  }
13733cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp
13833cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  // Negative height means invert the image.
13933cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  if (height < 0) {
14033cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    height = -height;
14133cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    src_argb = src_argb + (height - 1) * src_stride_argb;
14233cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    src_stride_argb = -src_stride_argb;
14333cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  }
14433cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp
14533cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  switch (mode) {
14633cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    case kRotate0:
14733cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp      // copy frame
14833cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp      return ARGBCopy(src_argb, src_stride_argb,
14933cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp                      dst_argb, dst_stride_argb,
15033cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp                      width, height);
15133cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    case kRotate90:
15233cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp      ARGBRotate90(src_argb, src_stride_argb,
15333cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp                   dst_argb, dst_stride_argb,
15433cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp                   width, height);
15533cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp      return 0;
15633cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    case kRotate270:
15733cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp      ARGBRotate270(src_argb, src_stride_argb,
15833cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp                    dst_argb, dst_stride_argb,
15933cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp                    width, height);
16033cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp      return 0;
16133cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    case kRotate180:
16233cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp      ARGBRotate180(src_argb, src_stride_argb,
16333cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp                    dst_argb, dst_stride_argb,
16433cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp                    width, height);
16533cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp      return 0;
16633cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp    default:
16733cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp      break;
16833cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  }
16933cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp  return -1;
17033cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp}
17133cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp
17233cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#ifdef __cplusplus
17333cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp}  // extern "C"
17433cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp}  // namespace libyuv
17533cfdeb7b267ab635413797fffb046b73272f7ecHendrik Dahlkamp#endif
176