Home | History | Annotate | Download | only in source
      1 /*
      2  *  Copyright 2012 The LibYuv Project Authors. All rights reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS. All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include "libyuv/rotate.h"
     12 
     13 #include "libyuv/cpu_id.h"
     14 #include "libyuv/convert.h"
     15 #include "libyuv/planar_functions.h"
     16 #include "libyuv/row.h"
     17 
     18 #ifdef __cplusplus
     19 namespace libyuv {
     20 extern "C" {
     21 #endif
     22 
     23 // ARGBScale has a function to copy pixels to a row, striding each source
     24 // pixel by a constant.
     25 #if !defined(LIBYUV_DISABLE_X86) && \
     26     (defined(_M_IX86) || \
     27     (defined(__x86_64__) && !defined(__native_client__)) || defined(__i386__))
     28 #define HAS_SCALEARGBROWDOWNEVEN_SSE2
     29 void ScaleARGBRowDownEven_SSE2(const uint8* src_ptr, int src_stride,
     30                                int src_stepx,
     31                                uint8* dst_ptr, int dst_width);
     32 #endif
     33 #if !defined(LIBYUV_DISABLE_NEON) && !defined(__native_client__) && \
     34     (defined(__ARM_NEON__) || defined(LIBYUV_NEON))
     35 #define HAS_SCALEARGBROWDOWNEVEN_NEON
     36 void ScaleARGBRowDownEven_NEON(const uint8* src_ptr, int src_stride,
     37                                int src_stepx,
     38                                uint8* dst_ptr, int dst_width);
     39 #endif
     40 
     41 void ScaleARGBRowDownEven_C(const uint8* src_ptr, int,
     42                             int src_stepx,
     43                             uint8* dst_ptr, int dst_width);
     44 
     45 static void ARGBTranspose(const uint8* src, int src_stride,
     46                           uint8* dst, int dst_stride,
     47                           int width, int height) {
     48   int i;
     49   int src_pixel_step = src_stride >> 2;
     50   void (*ScaleARGBRowDownEven)(const uint8* src_ptr, int src_stride,
     51       int src_step, uint8* dst_ptr, int dst_width) = ScaleARGBRowDownEven_C;
     52 #if defined(HAS_SCALEARGBROWDOWNEVEN_SSE2)
     53   if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(height, 4) &&  // Width of dest.
     54       IS_ALIGNED(dst, 16) && IS_ALIGNED(dst_stride, 16)) {
     55     ScaleARGBRowDownEven = ScaleARGBRowDownEven_SSE2;
     56   }
     57 #elif defined(HAS_SCALEARGBROWDOWNEVEN_NEON)
     58   if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(height, 4) &&  // Width of dest.
     59       IS_ALIGNED(src, 4)) {
     60     ScaleARGBRowDownEven = ScaleARGBRowDownEven_NEON;
     61   }
     62 #endif
     63 
     64   for (i = 0; i < width; ++i) {  // column of source to row of dest.
     65     ScaleARGBRowDownEven(src, 0, src_pixel_step, dst, height);
     66     dst += dst_stride;
     67     src += 4;
     68   }
     69 }
     70 
     71 void ARGBRotate90(const uint8* src, int src_stride,
     72                   uint8* dst, int dst_stride,
     73                   int width, int height) {
     74   // Rotate by 90 is a ARGBTranspose with the source read
     75   // from bottom to top. So set the source pointer to the end
     76   // of the buffer and flip the sign of the source stride.
     77   src += src_stride * (height - 1);
     78   src_stride = -src_stride;
     79   ARGBTranspose(src, src_stride, dst, dst_stride, width, height);
     80 }
     81 
     82 void ARGBRotate270(const uint8* src, int src_stride,
     83                     uint8* dst, int dst_stride,
     84                     int width, int height) {
     85   // Rotate by 270 is a ARGBTranspose with the destination written
     86   // from bottom to top. So set the destination pointer to the end
     87   // of the buffer and flip the sign of the destination stride.
     88   dst += dst_stride * (width - 1);
     89   dst_stride = -dst_stride;
     90   ARGBTranspose(src, src_stride, dst, dst_stride, width, height);
     91 }
     92 
     93 void ARGBRotate180(const uint8* src, int src_stride,
     94                    uint8* dst, int dst_stride,
     95                    int width, int height) {
     96   // Swap first and last row and mirror the content. Uses a temporary row.
     97   align_buffer_64(row, width * 4);
     98   const uint8* src_bot = src + src_stride * (height - 1);
     99   uint8* dst_bot = dst + dst_stride * (height - 1);
    100   int half_height = (height + 1) >> 1;
    101   int y;
    102   void (*ARGBMirrorRow)(const uint8* src, uint8* dst, int width) =
    103       ARGBMirrorRow_C;
    104   void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C;
    105 #if defined(HAS_ARGBMIRRORROW_SSSE3)
    106   if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 4) &&
    107       IS_ALIGNED(src, 16) && IS_ALIGNED(src_stride, 16) &&
    108       IS_ALIGNED(dst, 16) && IS_ALIGNED(dst_stride, 16)) {
    109     ARGBMirrorRow = ARGBMirrorRow_SSSE3;
    110   }
    111 #endif
    112 #if defined(HAS_ARGBMIRRORROW_AVX2)
    113   if (TestCpuFlag(kCpuHasAVX2) && IS_ALIGNED(width, 8)) {
    114     ARGBMirrorRow = ARGBMirrorRow_AVX2;
    115   }
    116 #endif
    117 #if defined(HAS_ARGBMIRRORROW_NEON)
    118   if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 4)) {
    119     ARGBMirrorRow = ARGBMirrorRow_NEON;
    120   }
    121 #endif
    122 #if defined(HAS_COPYROW_NEON)
    123   if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width * 4, 32)) {
    124     CopyRow = CopyRow_NEON;
    125   }
    126 #endif
    127 #if defined(HAS_COPYROW_X86)
    128   if (TestCpuFlag(kCpuHasX86)) {
    129     CopyRow = CopyRow_X86;
    130   }
    131 #endif
    132 #if defined(HAS_COPYROW_SSE2)
    133   if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width * 4, 32) &&
    134       IS_ALIGNED(src, 16) && IS_ALIGNED(src_stride, 16) &&
    135       IS_ALIGNED(dst, 16) && IS_ALIGNED(dst_stride, 16)) {
    136     CopyRow = CopyRow_SSE2;
    137   }
    138 #endif
    139 #if defined(HAS_COPYROW_ERMS)
    140   if (TestCpuFlag(kCpuHasERMS)) {
    141     CopyRow = CopyRow_ERMS;
    142   }
    143 #endif
    144 #if defined(HAS_COPYROW_MIPS)
    145   if (TestCpuFlag(kCpuHasMIPS)) {
    146     CopyRow = CopyRow_MIPS;
    147   }
    148 #endif
    149 
    150   // Odd height will harmlessly mirror the middle row twice.
    151   for (y = 0; y < half_height; ++y) {
    152     ARGBMirrorRow(src, row, width);  // Mirror first row into a buffer
    153     ARGBMirrorRow(src_bot, dst, width);  // Mirror last row into first row
    154     CopyRow(row, dst_bot, width * 4);  // Copy first mirrored row into last
    155     src += src_stride;
    156     dst += dst_stride;
    157     src_bot -= src_stride;
    158     dst_bot -= dst_stride;
    159   }
    160   free_aligned_buffer_64(row);
    161 }
    162 
    163 LIBYUV_API
    164 int ARGBRotate(const uint8* src_argb, int src_stride_argb,
    165                uint8* dst_argb, int dst_stride_argb,
    166                int width, int height,
    167                enum RotationMode mode) {
    168   if (!src_argb || width <= 0 || height == 0 || !dst_argb) {
    169     return -1;
    170   }
    171 
    172   // Negative height means invert the image.
    173   if (height < 0) {
    174     height = -height;
    175     src_argb = src_argb + (height - 1) * src_stride_argb;
    176     src_stride_argb = -src_stride_argb;
    177   }
    178 
    179   switch (mode) {
    180     case kRotate0:
    181       // copy frame
    182       return ARGBCopy(src_argb, src_stride_argb,
    183                       dst_argb, dst_stride_argb,
    184                       width, height);
    185     case kRotate90:
    186       ARGBRotate90(src_argb, src_stride_argb,
    187                    dst_argb, dst_stride_argb,
    188                    width, height);
    189       return 0;
    190     case kRotate270:
    191       ARGBRotate270(src_argb, src_stride_argb,
    192                     dst_argb, dst_stride_argb,
    193                     width, height);
    194       return 0;
    195     case kRotate180:
    196       ARGBRotate180(src_argb, src_stride_argb,
    197                     dst_argb, dst_stride_argb,
    198                     width, height);
    199       return 0;
    200     default:
    201       break;
    202   }
    203   return -1;
    204 }
    205 
    206 #ifdef __cplusplus
    207 }  // extern "C"
    208 }  // namespace libyuv
    209 #endif
    210