/* * Copyright (C) 2012-2017 Intel Corporation * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ #define LOG_TAG "ImageScalerCore" #include "LogHelper.h" #include "ImageScalerCore.h" #include #include #define RESOLUTION_VGA_WIDTH 640 #define RESOLUTION_VGA_HEIGHT 480 #define RESOLUTION_QVGA_WIDTH 320 #define RESOLUTION_QVGA_HEIGHT 240 #define RESOLUTION_QCIF_WIDTH 176 #define RESOLUTION_QCIF_HEIGHT 144 #define MIN(a,b) ((a)<(b)?(a):(b)) NAMESPACE_DECLARATION { void ImageScalerCore::downScaleImage(std::shared_ptr srcBuf, std::shared_ptr dstBuf) { downScaleImage(srcBuf->data(), dstBuf->data(), dstBuf->width(), dstBuf->height(), dstBuf->stride(), srcBuf->width(), srcBuf->height(), srcBuf->stride(), srcBuf->v4l2Fmt()); } void ImageScalerCore::downScaleImage(void *src, void *dest, int dest_w, int dest_h, int dest_stride, int src_w, int src_h, int src_stride, int format, int src_skip_lines_top, // number of lines that are skipped from src image start pointer int src_skip_lines_bottom) // number of lines that are skipped after reading src_h (should be set always to reach full image height) { unsigned char *m_dest = (unsigned char *)dest; const unsigned char * m_src = (const unsigned char *)src; switch (format) { case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_NV12: { // downscale & crop ImageScalerCore::downScaleAndCropNv12Image(m_dest, m_src, dest_w, dest_h, dest_stride, src_w, src_h, src_stride, src_skip_lines_top, src_skip_lines_bottom); break; } case V4L2_PIX_FMT_YUYV: { ImageScalerCore::downScaleYUY2Image(m_dest, m_src, dest_w, dest_h, dest_stride, src_w, src_h, src_stride); break; } default: { LOGE("no downscale support for format = %d", format); break; } } } void ImageScalerCore::downScaleYUY2Image(unsigned char *dest, const unsigned char *src, const int dest_w, const int dest_h, const int dest_stride, const int src_w, const int src_h, const int src_stride) { if (dest==nullptr || dest_w <=0 || dest_h <=0 || src==nullptr || src_w <=0 || src_h <= 0 ) return; if (dest_w%2 != 0) // if the dest_w is not an even number, exit return; const int scale_w = (src_w<<8) / dest_w; // scale factors const int scale_h = (src_h<<8) / dest_h; int dx, dy; int macro_pixel_width = dest_w >> 1; int src_i, src_j; // the left up coordinates of src that correspond to (j,i) in dest unsigned int val_1, val_2; // for bi-linear-interpolation int i,j,k; for(i=0; i < dest_h; ++i) { src_i = i * scale_h; dy = src_i & 0xff; src_i >>= 8; for(j=0; j < macro_pixel_width; ++j) { src_j = j * scale_w; dx = src_j & 0xff; src_j = src_j >> 8; for(k = 0; k < 4; ++k) { // bi-linear-interpolation if(dx == 0 && dy == 0) { dest[i * 2 * dest_stride + 4 * j + k] = src[src_i * 2 * src_stride + src_j * 4 + k]; } else if(dx == 0 && dy != 0){ val_1 = (unsigned int)src[src_i * 2 * src_stride + src_j * 4 + k]; val_2 = (unsigned int)src[(src_i + 1) * 2 * src_stride + src_j * 4 + k]; val_1 = (val_1 * (256 - dy) + val_2 * dy) >> 8; dest[i * 2 * dest_stride + 4 * j + k] = ((val_1 <= 255) ? val_1: 255); } else if(dx != 0 && dy == 0) { val_1 = ((unsigned int)src[src_i * 2 * src_stride + src_j * 4 + k] * (256 - dx) + (unsigned int)src[src_i * 2 * src_stride + (src_j +1) * 4 + k] * dx) >> 8; dest[i * 2 * dest_stride + 4 * j + k] = ((val_1 <= 255) ? val_1: 255); } else { val_1 = ((unsigned int)src[src_i * 2 * src_stride + src_j * 4 + k] * (256 - dx) + (unsigned int)src[src_i * 2 * src_stride + (src_j +1) * 4 + k] * dx) >> 8; val_2 = ((unsigned int)src[(src_i + 1) * 2 * src_stride + src_j * 4 + k] * (256 - dx) + (unsigned int)src[(src_i + 1) * 2 * src_stride + (src_j+1) * 4 + k] * dx) >> 8; val_1 = (val_1 * (256 - dy) + val_2 * dy) >> 8; dest[i * 2 * dest_stride + 4 * j + k] = ((val_1 <= 255) ? val_1: 255); } } } } } void ImageScalerCore::downScaleAndCropNv12Image(unsigned char *dest, const unsigned char *src, const int dest_w, const int dest_h, const int dest_stride, const int src_w, const int src_h, const int src_stride, const int src_skip_lines_top, // number of lines that are skipped from src image start pointer const int src_skip_lines_bottom) // number of lines that are skipped after reading src_h (should be set always to reach full image height) { LOGI("@%s: dest_w: %d, dest_h: %d, dest_stride: %d, src_w: %d, src_h: %d, src_stride: %d, skip_top: %d, skip_bottom: %d, dest: %p, src: %p", __FUNCTION__, dest_w, dest_h, dest_stride, src_w, src_h, src_stride, src_skip_lines_top, src_skip_lines_bottom, dest, src); int total_height = src_skip_lines_top + src_h + src_skip_lines_bottom; int width = src_w, height = src_h; int left = 0, top = src_skip_lines_top; int proper_source_width = (int)((float)dest_w / dest_h * src_h); // Aligned to 4 proper_source_width = (proper_source_width + 3) & ~3; int proper_source_height = (int)((float)dest_h / dest_w * src_w); if (src_w != dest_w || src_h != dest_h) { // Crop width/height if needed if (proper_source_width < src_w) { width = proper_source_width; left = (src_w - width) / 2; left = (left + 1) & ~1; // Aligned for uv } else if (proper_source_height < src_h) { height = proper_source_height; top += (src_h - height) / 2; } } int offset = top * src_stride + left; uint8_t *src_y = (uint8_t *)src + offset; uint8_t *src_uv = (uint8_t *)src + total_height * src_stride + offset/2; libyuv::ScalePlane(src_y, src_stride, width, height, dest, dest_stride, dest_w, dest_h, libyuv::kFilterNone); dest += dest_stride * dest_h; libyuv::ScalePlane_16((uint16_t *)src_uv, src_stride/2, width/2, height/2, (uint16_t *)dest, dest_stride/2, dest_w/2, dest_h/2, libyuv::kFilterNone); } void ImageScalerCore::cropComposeCopy(void *src, void *dst, unsigned int size) { STDCOPY((int8_t *) dst, (int8_t *) src, size); } // Bilinear scaling, chrominance with nearest neighbor void ImageScalerCore::cropComposeUpscaleNV12_bl( void *src, unsigned int srcH, unsigned int srcStride, unsigned int srcCropLeft, unsigned int srcCropTop, unsigned int srcCropW, unsigned int srcCropH, void *dst, unsigned int dstH, unsigned int dstStride, unsigned int dstCropLeft, unsigned int dstCropTop, unsigned int dstCropW, unsigned int dstCropH) { static const int BILINEAR = 1; static const unsigned int FP_1 = 1 << MFP; // Fixed point 1.0 static const unsigned int FRACT = (1 << MFP) - 1; // Fractional part mask unsigned int dx, dy, sx, sy; unsigned char *s = (unsigned char *)src; unsigned char *d = (unsigned char *)dst; unsigned int sx0, sy0, dx0, dy0, dx1, dy1; unsigned int sxd = ((srcCropW<>1)) / dstCropW; unsigned int syd = ((srcCropH<>1)) / dstCropH; if (!src || !dst) { LOGE("buffer pointer is nullptr"); return; } // Upscale luminance sx0 = srcCropLeft << MFP; sy0 = srcCropTop << MFP; dx0 = dstCropLeft; dy0 = dstCropTop; dx1 = dstCropLeft + dstCropW; dy1 = dstCropTop + dstCropH; for (dy = dy0, sy = sy0; dy < dy1; dy++, sy += syd) { for (dx = dx0, sx = sx0; dx < dx1; dx++, sx += sxd) { unsigned int sxi = sx >> MFP; unsigned int syi = sy >> MFP; unsigned int s0 = s[srcStride*syi+sxi]; if (BILINEAR) { unsigned int fx = sx & FRACT; // Fractional part unsigned int fy = sy & FRACT; unsigned int fx1 = FP_1 - fx; // 1 - fractional part unsigned int fy1 = FP_1 - fy; unsigned int s1 = s[srcStride*syi+sxi+1]; unsigned int s2 = s[srcStride*(syi+1)+sxi]; unsigned int s3 = s[srcStride*(syi+1)+sxi+1]; unsigned int s4 = (s0 * fx1 + s1 * fx) >> MFP; unsigned int s5 = (s2 * fx1 + s3 * fx) >> MFP; s0 = (s4 * fy1 + s5 * fy) >> MFP; } d[dstStride*dy+dx] = s0; } } // Upscale chrominance s = (unsigned char *)src + srcStride*srcH; d = (unsigned char *)dst + dstStride*dstH; sx0 = srcCropLeft << (MFP - 1); sy0 = srcCropTop << (MFP - 1); dx0 = dstCropLeft >> 1; dy0 = dstCropTop >> 1; dx1 = (dstCropLeft + dstCropW) >> 1; dy1 = (dstCropTop + dstCropH) >> 1; for (dy = dy0, sy = sy0; dy < dy1; dy++, sy += syd) { for (dx = dx0, sx = sx0; dx < dx1; dx++, sx += sxd) { unsigned int sxi = sx >> MFP; unsigned int syi = sy >> MFP; d[dstStride*dy+dx*2+0] = s[srcStride*syi+sxi*2+0]; d[dstStride*dy+dx*2+1] = s[srcStride*syi+sxi*2+1]; } } } } NAMESPACE_DECLARATION_END