text: // Tencent is pleased to support the open source community by making ncnn available.
  //
  // Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
  //
  // Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
  // in compliance with the License. You may obtain a copy of the License at
  //
  // https://opensource.org/licenses/BSD-3-Clause
  //
  // Unless required by applicable law or agreed to in writing, software distributed
  // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  // CONDITIONS OF ANY KIND, either express or implied. See the License for the
  // specific language governing permissions and limitations under the License.
  
  static void conv1x1s1_sse(const Mat& bottom_blob, Mat& top_blob, const Mat& _kernel, const Mat& _bias, const Option& opt)
  {
      int inch = bottom_blob.c;
  
      int outw = top_blob.w;
      int outh = top_blob.h;
      int outch = top_blob.c;
  
      const float* kernel = _kernel;
      const float* bias = _bias;
  
      #pragma omp parallel for num_threads(opt.num_threads)
      for (int p = 0; p < outch; p++)
      {
          Mat out = top_blob.channel(p);
  
          const float bias0 = bias ? bias[p] : 0.f;
  
          out.fill(bias0);
  
          int q = 0;
  
          for (; q + 3 < inch; q += 4)
          {
              float* outptr = out;
  
              const float* img0 = bottom_blob.channel(q);
              const float* img1 = bottom_blob.channel(q + 1);
              const float* img2 = bottom_blob.channel(q + 2);
              const float* img3 = bottom_blob.channel(q + 3);
  
              const float* kernel0 = kernel + p * inch + q;
              const float k0 = kernel0[0];
              const float k1 = kernel0[1];
              const float k2 = kernel0[2];
              const float k3 = kernel0[3];
  
              const float* r0 = img0;
              const float* r1 = img1;
              const float* r2 = img2;
              const float* r3 = img3;
  
              int size = outw * outh;
  
              int remain = size;
  
              for (; remain > 0; remain--)
              {
                  float sum = *r0 * k0;
                  float sum1 = *r1 * k1;
                  float sum2 = *r2 * k2;
                  float sum3 = *r3 * k3;
  
                  *outptr += sum + sum1 + sum2 + sum3;
  
                  r0++;
                  r1++;
                  r2++;
                  r3++;
                  outptr++;
              }
          }
  
          for (; q < inch; q++)
          {
              float* outptr = out;
  
              const float* img0 = bottom_blob.channel(q);
  
              const float* kernel0 = kernel + p * inch + q;
              const float k0 = kernel0[0];
  
              const float* r0 = img0;
  
              int size = outw * outh;
  
              int remain = size;
  
              for (; remain > 0; remain--)
              {
                  float sum = *r0 * k0;
  
                  *outptr += sum;
  
                  r0++;
                  outptr++;
              }
          }
      }
  }
  
  static void conv1x1s2_sse(const Mat& bottom_blob, Mat& top_blob, const Mat& _kernel, const Mat& _bias, const Option& opt)
  {
      int w = bottom_blob.w;
      int inch = bottom_blob.c;
  
      int outw = top_blob.w;
      int outh = top_blob.h;
      int outch = top_blob.c;
  
      const int tailstep = w - 2 * outw + w;
  
      const float* kernel = _kernel;
      const float* bias = _bias;
  
      #pragma omp parallel for num_threads(opt.num_threads)
      for (int p = 0; p < outch; p++)
      {
          Mat out = top_blob.channel(p);
  
          const float bias0 = bias ? bias[p] : 0.f;
  
          out.fill(bias0);
  
          int q = 0;
  
          for (; q + 3 < inch; q += 4)
          {
              float* outptr = out;
  
              const float* img0 = bottom_blob.channel(q);
              const float* img1 = bottom_blob.channel(q + 1);
              const float* img2 = bottom_blob.channel(q + 2);
              const float* img3 = bottom_blob.channel(q + 3);
  
              const float* kernel0 = kernel + p * inch + q;
              const float k0 = kernel0[0];
              const float k1 = kernel0[1];
              const float k2 = kernel0[2];
              const float k3 = kernel0[3];
  
              const float* r0 = img0;
              const float* r1 = img1;
              const float* r2 = img2;
              const float* r3 = img3;
  
              for (int i = 0; i < outh; i++)
              {
                  int remain = outw;
  
                  for (; remain > 0; remain--)
                  {
                      float sum = *r0 * k0;
                      float sum1 = *r1 * k1;
                      float sum2 = *r2 * k2;
                      float sum3 = *r3 * k3;
  
                      *outptr += sum + sum1 + sum2 + sum3;
  
                      r0 += 2;
                      r1 += 2;
                      r2 += 2;
                      r3 += 2;
                      outptr++;
                  }
  
                  r0 += tailstep;
                  r1 += tailstep;
                  r2 += tailstep;
                  r3 += tailstep;
              }
          }
  
          for (; q < inch; q++)
          {
              float* outptr = out;
  
              const float* img0 = bottom_blob.channel(q);
  
              const float* kernel0 = kernel + p * inch + q;
              const float k0 = kernel0[0];
  
              const float* r0 = img0;
  
              for (int i = 0; i < outh; i++)
              {
                  int remain = outw;
  
                  for (; remain > 0; remain--)
                  {
                      float sum = *r0 * k0;
  
                      *outptr += sum;
  
                      r0 += 2;
                      outptr++;
                  }
  
                  r0 += tailstep;
              }
          }
      }
  }
  
  static void conv1x1s1_sgemm_sse(const Mat& bottom_blob, Mat& top_blob, const Mat& kernel, const Mat& _bias, const Option& opt)
  {
      int w = bottom_blob.w;
      int h = bottom_blob.h;
      const int size = w * h;
  
      Mat bottom_im2col = bottom_blob;
      bottom_im2col.w = size;
      bottom_im2col.h = 1;
  
      im2col_sgemm_sse(bottom_im2col, top_blob, kernel, _bias, opt);
  }
  
  static void conv1x1s2_sgemm_sse(const Mat& bottom_blob, Mat& top_blob, const Mat& kernel, const Mat& _bias, const Option& opt)
  {
      int w = bottom_blob.w;
      int channels = bottom_blob.c;
      size_t elemsize = bottom_blob.elemsize;
      int elempack = bottom_blob.elempack;
  
      int outw = top_blob.w;
      int outh = top_blob.h;
  
      const int tailstep = w - 2 * outw + w;
  
      Mat bottom_blob_shrinked;
      bottom_blob_shrinked.create(outw, outh, channels, elemsize, elempack, opt.workspace_allocator);
  
      #pragma omp parallel for num_threads(opt.num_threads)
      for (int p = 0; p < channels; p++)
      {
          const float* r0 = bottom_blob.channel(p);
          float* outptr = bottom_blob_shrinked.channel(p);
  
          for (int i = 0; i < outh; i++)
          {
              for (int j = 0; j < outw; j++)
              {
                  outptr[0] = r0[0];
  
                  r0 += 2;
                  outptr += 1;
              }
  
              r0 += tailstep;
          }
      }
  
      conv1x1s1_sgemm_sse(bottom_blob_shrinked, top_blob, kernel, _bias, opt);
  }
  