root/remoting/base/util.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GetTimestampString
  2. CalculateRGBOffset
  3. CalculateYOffset
  4. CalculateUVOffset
  5. ConvertRGB32ToYUVWithRect
  6. ConvertAndScaleYUVToRGB32Rect
  7. RoundToTwosMultiple
  8. AlignRect
  9. ScaleRect
  10. CopyRGB32Rect
  11. ReplaceLfByCrLf
  12. ReplaceCrLfByLf
  13. StringIsUtf8
  14. DoesRectContain

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/base/util.h"

#include <math.h>

#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "media/base/video_frame.h"
#include "media/base/yuv_convert.h"
#include "third_party/libyuv/include/libyuv/convert.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_region.h"

using media::VideoFrame;

namespace remoting {

enum { kBytesPerPixelRGB32 = 4 };

// Do not write LOG messages in this routine since it is called from within
// our LOG message handler. Bad things will happen.
std::string GetTimestampString() {
  base::Time t = base::Time::NowFromSystemTime();
  base::Time::Exploded tex;
  t.LocalExplode(&tex);
  return base::StringPrintf("%02d%02d/%02d%02d%02d:",
                            tex.month, tex.day_of_month,
                            tex.hour, tex.minute, tex.second);
}

int CalculateRGBOffset(int x, int y, int stride) {
  return stride * y + kBytesPerPixelRGB32 * x;
}

int CalculateYOffset(int x, int y, int stride) {
  DCHECK(((x & 1) == 0) && ((y & 1) == 0));
  return stride * y + x;
}

int CalculateUVOffset(int x, int y, int stride) {
  DCHECK(((x & 1) == 0) && ((y & 1) == 0));
  return stride * y / 2 + x / 2;
}

void ConvertRGB32ToYUVWithRect(const uint8* rgb_plane,
                               uint8* y_plane,
                               uint8* u_plane,
                               uint8* v_plane,
                               int x,
                               int y,
                               int width,
                               int height,
                               int rgb_stride,
                               int y_stride,
                               int uv_stride) {
  int rgb_offset = CalculateRGBOffset(x, y, rgb_stride);
  int y_offset = CalculateYOffset(x, y, y_stride);
  int uv_offset = CalculateUVOffset(x, y, uv_stride);;

  libyuv::ARGBToI420(rgb_plane + rgb_offset, rgb_stride,
                     y_plane + y_offset, y_stride,
                     u_plane + uv_offset, uv_stride,
                     v_plane + uv_offset, uv_stride,
                     width, height);
}

void ConvertAndScaleYUVToRGB32Rect(
    const uint8* source_yplane,
    const uint8* source_uplane,
    const uint8* source_vplane,
    int source_ystride,
    int source_uvstride,
    const webrtc::DesktopSize& source_size,
    const webrtc::DesktopRect& source_buffer_rect,
    uint8* dest_buffer,
    int dest_stride,
    const webrtc::DesktopSize& dest_size,
    const webrtc::DesktopRect& dest_buffer_rect,
    const webrtc::DesktopRect& dest_rect) {
  // N.B. It is caller's responsibility to check if strides are large enough. We
  // cannot do it here anyway.
  DCHECK(DoesRectContain(webrtc::DesktopRect::MakeSize(source_size),
                         source_buffer_rect));
  DCHECK(DoesRectContain(webrtc::DesktopRect::MakeSize(dest_size),
                         dest_buffer_rect));
  DCHECK(DoesRectContain(dest_buffer_rect, dest_rect));
  DCHECK(DoesRectContain(ScaleRect(source_buffer_rect, source_size, dest_size),
                         dest_rect));

  // If the source and/or destination buffers don't start at (0, 0)
  // offset the pointers to pretend we have complete buffers.
  int y_offset = - CalculateYOffset(source_buffer_rect.left(),
                                    source_buffer_rect.top(),
                                    source_ystride);
  int uv_offset = - CalculateUVOffset(source_buffer_rect.left(),
                                      source_buffer_rect.top(),
                                      source_uvstride);
  int rgb_offset = - CalculateRGBOffset(dest_buffer_rect.left(),
                                        dest_buffer_rect.top(),
                                        dest_stride);

  // See if scaling is needed.
  if (source_size.equals(dest_size)) {
    // Calculate the inner rectangle that can be copied by the optimized
    // libyuv::I420ToARGB().
    webrtc::DesktopRect inner_rect =
        webrtc::DesktopRect::MakeLTRB(RoundToTwosMultiple(dest_rect.left() + 1),
                                      RoundToTwosMultiple(dest_rect.top() + 1),
                                      dest_rect.right(), dest_rect.bottom());

    // Offset pointers to point to the top left corner of the inner rectangle.
    y_offset += CalculateYOffset(inner_rect.left(), inner_rect.top(),
                                 source_ystride);
    uv_offset += CalculateUVOffset(inner_rect.left(), inner_rect.top(),
                                   source_uvstride);
    rgb_offset += CalculateRGBOffset(inner_rect.left(), inner_rect.top(),
                                     dest_stride);

    libyuv::I420ToARGB(source_yplane + y_offset, source_ystride,
                       source_uplane + uv_offset, source_uvstride,
                       source_vplane + uv_offset, source_uvstride,
                       dest_buffer + rgb_offset, dest_stride,
                       inner_rect.width(), inner_rect.height());

    // Now see if some pixels weren't copied due to alignment.
    if (!dest_rect.equals(inner_rect)) {
      webrtc::DesktopRect outer_rect =
          webrtc::DesktopRect::MakeLTRB(RoundToTwosMultiple(dest_rect.left()),
                                        RoundToTwosMultiple(dest_rect.top()),
                                        dest_rect.right(), dest_rect.bottom());

      webrtc::DesktopVector offset(outer_rect.left() - inner_rect.left(),
                                   outer_rect.top() - inner_rect.top());

      // Offset the pointers to point to the top left corner of the outer
      // rectangle.
      y_offset += CalculateYOffset(offset.x(), offset.y(), source_ystride);
      uv_offset += CalculateUVOffset(offset.x(), offset.y(), source_uvstride);
      rgb_offset += CalculateRGBOffset(offset.x(), offset.y(), dest_stride);

      // Draw unaligned edges.
      webrtc::DesktopRegion edges(dest_rect);
      edges.Subtract(inner_rect);
      for (webrtc::DesktopRegion::Iterator i(edges); !i.IsAtEnd();
           i.Advance()) {
        webrtc::DesktopRect rect = i.rect();
        rect.Translate(-outer_rect.left(), -outer_rect.top());
        media::ScaleYUVToRGB32WithRect(source_yplane + y_offset,
                                       source_uplane + uv_offset,
                                       source_vplane + uv_offset,
                                       dest_buffer + rgb_offset,
                                       source_size.width(),
                                       source_size.height(),
                                       dest_size.width(),
                                       dest_size.height(),
                                       rect.left(),
                                       rect.top(),
                                       rect.right(),
                                       rect.bottom(),
                                       source_ystride,
                                       source_uvstride,
                                       dest_stride);
      }
    }
  } else {
    media::ScaleYUVToRGB32WithRect(source_yplane + y_offset,
                                   source_uplane + uv_offset,
                                   source_vplane + uv_offset,
                                   dest_buffer + rgb_offset,
                                   source_size.width(),
                                   source_size.height(),
                                   dest_size.width(),
                                   dest_size.height(),
                                   dest_rect.left(),
                                   dest_rect.top(),
                                   dest_rect.right(),
                                   dest_rect.bottom(),
                                   source_ystride,
                                   source_uvstride,
                                   dest_stride);
  }
}

int RoundToTwosMultiple(int x) {
  return x & (~1);
}

webrtc::DesktopRect AlignRect(const webrtc::DesktopRect& rect) {
  int x = RoundToTwosMultiple(rect.left());
  int y = RoundToTwosMultiple(rect.top());
  int right = RoundToTwosMultiple(rect.right() + 1);
  int bottom = RoundToTwosMultiple(rect.bottom() + 1);
  return webrtc::DesktopRect::MakeLTRB(x, y, right, bottom);
}

webrtc::DesktopRect ScaleRect(const webrtc::DesktopRect& rect,
                              const webrtc::DesktopSize& in_size,
                              const webrtc::DesktopSize& out_size) {
  int left = (rect.left() * out_size.width()) / in_size.width();
  int top = (rect.top() * out_size.height()) / in_size.height();
  int right = (rect.right() * out_size.width() + in_size.width() - 1) /
      in_size.width();
  int bottom = (rect.bottom() * out_size.height() + in_size.height() - 1) /
      in_size.height();
  return webrtc::DesktopRect::MakeLTRB(left, top, right, bottom);
}

void CopyRGB32Rect(const uint8* source_buffer,
                   int source_stride,
                   const webrtc::DesktopRect& source_buffer_rect,
                   uint8* dest_buffer,
                   int dest_stride,
                   const webrtc::DesktopRect& dest_buffer_rect,
                   const webrtc::DesktopRect& dest_rect) {
  DCHECK(DoesRectContain(dest_buffer_rect, dest_rect));
  DCHECK(DoesRectContain(source_buffer_rect, dest_rect));

  // Get the address of the starting point.
  source_buffer += CalculateRGBOffset(
      dest_rect.left() - source_buffer_rect.left(),
      dest_rect.top() - source_buffer_rect.top(),
      source_stride);
  dest_buffer += CalculateRGBOffset(
      dest_rect.left() - dest_buffer_rect.left(),
      dest_rect.top() - dest_buffer_rect.top(),
      source_stride);

  // Copy pixels in the rectangle line by line.
  const int bytes_per_line = kBytesPerPixelRGB32 * dest_rect.width();
  for (int i = 0 ; i < dest_rect.height(); ++i) {
    memcpy(dest_buffer, source_buffer, bytes_per_line);
    source_buffer += source_stride;
    dest_buffer += dest_stride;
  }
}

std::string ReplaceLfByCrLf(const std::string& in) {
  std::string out;
  out.resize(2 * in.size());
  char* out_p_begin = &out[0];
  char* out_p = out_p_begin;
  const char* in_p_begin = &in[0];
  const char* in_p_end = &in[in.size()];
  for (const char* in_p = in_p_begin; in_p < in_p_end; ++in_p) {
    char c = *in_p;
    if (c == '\n') {
      *out_p++ = '\r';
    }
    *out_p++ = c;
  }
  out.resize(out_p - out_p_begin);
  return out;
}

std::string ReplaceCrLfByLf(const std::string& in) {
  std::string out;
  out.resize(in.size());
  char* out_p_begin = &out[0];
  char* out_p = out_p_begin;
  const char* in_p_begin = &in[0];
  const char* in_p_end = &in[in.size()];
  for (const char* in_p = in_p_begin; in_p < in_p_end; ++in_p) {
    char c = *in_p;
    if ((c == '\r') && (in_p + 1 < in_p_end) && (*(in_p + 1) == '\n')) {
      *out_p++ = '\n';
      ++in_p;
    } else {
      *out_p++ = c;
    }
  }
  out.resize(out_p - out_p_begin);
  return out;
}

bool StringIsUtf8(const char* data, size_t length) {
  const char* ptr = data;
  const char* ptr_end = data + length;
  while (ptr != ptr_end) {
    if ((*ptr & 0x80) == 0) {
      // Single-byte symbol.
      ++ptr;
    } else if ((*ptr & 0xc0) == 0x80 || (*ptr & 0xfe) == 0xfe) {
      // Invalid first byte.
      return false;
    } else {
      // First byte of a multi-byte symbol. The bits from 2 to 6 are the count
      // of continuation bytes (up to 5 of them).
      for (char first = *ptr << 1; first & 0x80; first <<= 1) {
        ++ptr;

        // Missing continuation byte.
        if (ptr == ptr_end)
          return false;

        // Invalid continuation byte.
        if ((*ptr & 0xc0) != 0x80)
          return false;
      }

      ++ptr;
    }
  }

  return true;
}

bool DoesRectContain(const webrtc::DesktopRect& a,
                     const webrtc::DesktopRect& b) {
  webrtc::DesktopRect intersection(a);
  intersection.IntersectWith(b);
  return intersection.equals(b);
}

}  // namespace remoting

/* [<][>][^][v][top][bottom][index][help] */