| // 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/stringprintf.h" |
| #include "base/time.h" |
| #include "media/base/video_frame.h" |
| #include "media/base/yuv_convert.h" |
| #include "third_party/skia/include/core/SkRegion.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 StringPrintf("%02d%02d/%02d%02d%02d:", |
| tex.month, tex.day_of_month, |
| tex.hour, tex.minute, tex.second); |
| } |
| |
| // Helper methods to calculate plane offset given the coordinates. |
| static int CalculateRGBOffset(int x, int y, int stride) { |
| return stride * y + kBytesPerPixelRGB32 * x; |
| } |
| |
| static int CalculateYOffset(int x, int y, int stride) { |
| DCHECK(((x & 1) == 0) && ((y & 1) == 0)); |
| return stride * y + x; |
| } |
| |
| static 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);; |
| |
| media::ConvertRGB32ToYUV(rgb_plane + rgb_offset, |
| y_plane + y_offset, |
| u_plane + uv_offset, |
| v_plane + uv_offset, |
| width, |
| height, |
| rgb_stride, |
| y_stride, |
| uv_stride); |
| } |
| |
| void ConvertAndScaleYUVToRGB32Rect(const uint8* source_yplane, |
| const uint8* source_uplane, |
| const uint8* source_vplane, |
| int source_ystride, |
| int source_uvstride, |
| const SkISize& source_size, |
| const SkIRect& source_buffer_rect, |
| uint8* dest_buffer, |
| int dest_stride, |
| const SkISize& dest_size, |
| const SkIRect& dest_buffer_rect, |
| const SkIRect& dest_rect) { |
| // N.B. It is caller's responsibility to check if strides are large enough. We |
| // cannot do it here anyway. |
| DCHECK(SkIRect::MakeSize(source_size).contains(source_buffer_rect)); |
| DCHECK(SkIRect::MakeSize(dest_size).contains(dest_buffer_rect)); |
| DCHECK(dest_buffer_rect.contains(dest_rect)); |
| DCHECK(ScaleRect(source_buffer_rect, source_size, dest_size). |
| contains(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.x(), |
| source_buffer_rect.y(), |
| source_ystride); |
| int uv_offset = - CalculateUVOffset(source_buffer_rect.x(), |
| source_buffer_rect.y(), |
| source_uvstride); |
| int rgb_offset = - CalculateRGBOffset(dest_buffer_rect.x(), |
| dest_buffer_rect.y(), |
| dest_stride); |
| |
| // See if scaling is needed. |
| if (source_size == dest_size) { |
| // Calculate the inner rectangle that can be copied by the optimized |
| // ConvertYUVToRGB32(). |
| SkIRect inner_rect = |
| SkIRect::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.x(), inner_rect.y(), |
| source_ystride); |
| uv_offset += CalculateUVOffset(inner_rect.x(), inner_rect.y(), |
| source_uvstride); |
| rgb_offset += CalculateRGBOffset(inner_rect.x(), inner_rect.y(), |
| dest_stride); |
| |
| media::ConvertYUVToRGB32(source_yplane + y_offset, |
| source_uplane + uv_offset, |
| source_vplane + uv_offset, |
| dest_buffer + rgb_offset, |
| inner_rect.width(), |
| inner_rect.height(), |
| source_ystride, |
| source_uvstride, |
| dest_stride, |
| media::YV12); |
| |
| // Now see if some pixels weren't copied due to alignment. |
| if (dest_rect != inner_rect) { |
| SkIRect outer_rect = |
| SkIRect::MakeLTRB(RoundToTwosMultiple(dest_rect.left()), |
| RoundToTwosMultiple(dest_rect.top()), |
| dest_rect.right(), |
| dest_rect.bottom()); |
| |
| SkIPoint offset = SkIPoint::Make(outer_rect.x() - inner_rect.x(), |
| outer_rect.y() - inner_rect.y()); |
| |
| // 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. |
| SkRegion edges(dest_rect); |
| edges.op(inner_rect, SkRegion::kDifference_Op); |
| for (SkRegion::Iterator i(edges); !i.done(); i.next()) { |
| SkIRect rect(i.rect()); |
| rect.offset(- 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); |
| } |
| |
| SkIRect AlignRect(const SkIRect& rect) { |
| int x = RoundToTwosMultiple(rect.left()); |
| int y = RoundToTwosMultiple(rect.top()); |
| int right = RoundToTwosMultiple(rect.right() + 1); |
| int bottom = RoundToTwosMultiple(rect.bottom() + 1); |
| return SkIRect::MakeLTRB(x, y, right, bottom); |
| } |
| |
| SkIRect ScaleRect(const SkIRect& rect, |
| const SkISize& in_size, |
| const SkISize& 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 SkIRect::MakeLTRB(left, top, right, bottom); |
| } |
| |
| void CopyRect(const uint8* src_plane, |
| int src_plane_stride, |
| uint8* dest_plane, |
| int dest_plane_stride, |
| int bytes_per_pixel, |
| const SkIRect& rect) { |
| // Get the address of the starting point. |
| const int src_y_offset = src_plane_stride * rect.top(); |
| const int dest_y_offset = dest_plane_stride * rect.top(); |
| const int x_offset = bytes_per_pixel * rect.left(); |
| src_plane += src_y_offset + x_offset; |
| dest_plane += dest_y_offset + x_offset; |
| |
| // Copy pixels in the rectangle line by line. |
| const int bytes_per_line = bytes_per_pixel * rect.width(); |
| const int height = rect.height(); |
| for (int i = 0 ; i < height; ++i) { |
| memcpy(dest_plane, src_plane, bytes_per_line); |
| src_plane += src_plane_stride; |
| dest_plane += dest_plane_stride; |
| } |
| } |
| |
| void CopyRGB32Rect(const uint8* source_buffer, |
| int source_stride, |
| const SkIRect& source_buffer_rect, |
| uint8* dest_buffer, |
| int dest_stride, |
| const SkIRect& dest_buffer_rect, |
| const SkIRect& dest_rect) { |
| DCHECK(dest_buffer_rect.contains(dest_rect)); |
| DCHECK(source_buffer_rect.contains(dest_rect)); |
| |
| // Get the address of the starting point. |
| int source_offset = CalculateRGBOffset(dest_rect.x() - source_buffer_rect.x(), |
| dest_rect.y() - source_buffer_rect.y(), |
| source_stride); |
| int dest_offset = CalculateRGBOffset(dest_rect.x() - dest_buffer_rect.x(), |
| dest_rect.y() - dest_buffer_rect.y(), |
| source_stride); |
| |
| // Copy bits. |
| CopyRect(source_buffer + source_offset, |
| source_stride, |
| dest_buffer + dest_offset, |
| dest_stride, |
| kBytesPerPixelRGB32, |
| SkIRect::MakeWH(dest_rect.width(), dest_rect.height())); |
| } |
| |
| } // namespace remoting |