// Assumes that an initial valid guess of change rectangle 'rect' is passed. static void MinimizeChangeRectangle(const WebPPicture* const src, const WebPPicture* const dst, FrameRect* const rect) { int i, j; // Sanity checks. assert(src->width == dst->width && src->height == dst->height); assert(rect->x_offset_ + rect->width_ <= dst->width); assert(rect->y_offset_ + rect->height_ <= dst->height); // Left boundary. for (i = rect->x_offset_; i < rect->x_offset_ + rect->width_; ++i) { const uint32_t* const src_argb = &src->argb[rect->y_offset_ * src->argb_stride + i]; const uint32_t* const dst_argb = &dst->argb[rect->y_offset_ * dst->argb_stride + i]; if (ComparePixels(src_argb, src->argb_stride, dst_argb, dst->argb_stride, rect->height_)) { --rect->width_; // Redundant column. ++rect->x_offset_; } else { break; } } if (rect->width_ == 0) goto NoChange; // Right boundary. for (i = rect->x_offset_ + rect->width_ - 1; i >= rect->x_offset_; --i) { const uint32_t* const src_argb = &src->argb[rect->y_offset_ * src->argb_stride + i]; const uint32_t* const dst_argb = &dst->argb[rect->y_offset_ * dst->argb_stride + i]; if (ComparePixels(src_argb, src->argb_stride, dst_argb, dst->argb_stride, rect->height_)) { --rect->width_; // Redundant column. } else { break; } } if (rect->width_ == 0) goto NoChange; // Top boundary. for (j = rect->y_offset_; j < rect->y_offset_ + rect->height_; ++j) { const uint32_t* const src_argb = &src->argb[j * src->argb_stride + rect->x_offset_]; const uint32_t* const dst_argb = &dst->argb[j * dst->argb_stride + rect->x_offset_]; if (ComparePixels(src_argb, 1, dst_argb, 1, rect->width_)) { --rect->height_; // Redundant row. ++rect->y_offset_; } else { break; } } if (rect->height_ == 0) goto NoChange; // Bottom boundary. for (j = rect->y_offset_ + rect->height_ - 1; j >= rect->y_offset_; --j) { const uint32_t* const src_argb = &src->argb[j * src->argb_stride + rect->x_offset_]; const uint32_t* const dst_argb = &dst->argb[j * dst->argb_stride + rect->x_offset_]; if (ComparePixels(src_argb, 1, dst_argb, 1, rect->width_)) { --rect->height_; // Redundant row. } else { break; } } if (rect->height_ == 0) goto NoChange; if (IsEmptyRect(rect)) { NoChange: rect->x_offset_ = 0; rect->y_offset_ = 0; rect->width_ = 0; rect->height_ = 0; } }
// Compare rectangular region specified within an image and return number of pixels mismatching size_t CompareImage(vx_image image, vx_rectangle_t * rectRegion, vx_uint8 * refImage, float errLimitMin, float errLimitMax, int frameNumber, const char * fileNameRef) { // get number of planes, image format, and pixel type vx_df_image format = VX_DF_IMAGE_VIRT; vx_size num_planes = 0; vx_uint32 image_width = 0, image_height = 0; ERROR_CHECK(vxQueryImage(image, VX_IMAGE_ATTRIBUTE_WIDTH, &image_width, sizeof(image_width))); ERROR_CHECK(vxQueryImage(image, VX_IMAGE_ATTRIBUTE_HEIGHT, &image_height, sizeof(image_height))); ERROR_CHECK(vxQueryImage(image, VX_IMAGE_ATTRIBUTE_FORMAT, &format, sizeof(format))); ERROR_CHECK(vxQueryImage(image, VX_IMAGE_ATTRIBUTE_PLANES, &num_planes, sizeof(num_planes))); // set pixel type and compute frame size in bytes vx_enum pixelType = VX_TYPE_UINT8; // default if (format == VX_DF_IMAGE_S16) pixelType = VX_TYPE_INT16; else if (format == VX_DF_IMAGE_U16) pixelType = VX_TYPE_UINT16; else if (format == VX_DF_IMAGE_S32) pixelType = VX_TYPE_INT32; else if (format == VX_DF_IMAGE_U32) pixelType = VX_TYPE_UINT32; else if (format == VX_DF_IMAGE_F32_AMD || format == VX_DF_IMAGE_F32x3_AMD) pixelType = VX_TYPE_FLOAT32; // compare plane by plane vx_size errorPixelCountTotal = 0; vx_uint8 * pRefPlane = refImage; for (vx_uint32 plane = 0; plane < (vx_uint32)num_planes; plane++) { vx_imagepatch_addressing_t addr = { 0 }; vx_uint8 * base_ptr = nullptr; ERROR_CHECK(vxAccessImagePatch(image, rectRegion, plane, &addr, (void **)&base_ptr, VX_READ_ONLY)); vx_uint32 region_width = ((addr.dim_x * addr.scale_x) / VX_SCALE_UNITY); vx_uint32 region_height = (addr.dim_y * addr.scale_y) / VX_SCALE_UNITY; vx_uint32 plane_width = ((image_width * addr.scale_x) / VX_SCALE_UNITY); vx_uint32 plane_height = ((image_height * addr.scale_y) / VX_SCALE_UNITY); vx_uint32 plane_width_in_bytes = (format == VX_DF_IMAGE_U1_AMD) ? ((plane_width + 7) >> 3) : (plane_width * addr.stride_x); vx_uint32 start_x = ((rectRegion->start_x * addr.scale_x) / VX_SCALE_UNITY); vx_uint32 start_y = ((rectRegion->start_y * addr.scale_y) / VX_SCALE_UNITY); vx_uint8 * pRef = pRefPlane + start_y * plane_width_in_bytes + start_x * addr.stride_x; vx_size errorPixelCount = 0; if (pixelType == VX_TYPE_INT16) { errorPixelCount = ComparePixels((vx_int16 *)base_ptr, addr.stride_y, (vx_int16 *)pRef, plane_width_in_bytes, region_width, region_height, (vx_int32)errLimitMin, (vx_int32)errLimitMax); } else if (pixelType == VX_TYPE_UINT16) { errorPixelCount = ComparePixels((vx_uint16 *)base_ptr, addr.stride_y, (vx_uint16 *)pRef, plane_width_in_bytes, region_width, region_height, (vx_int32)errLimitMin, (vx_int32)errLimitMax); } else if (pixelType == VX_TYPE_INT32) { errorPixelCount = ComparePixels((vx_int32 *)base_ptr, addr.stride_y, (vx_int32 *)pRef, plane_width_in_bytes, region_width, region_height, (vx_int64)errLimitMin, (vx_int64)errLimitMax); } else if (pixelType == VX_TYPE_UINT32) { errorPixelCount = ComparePixels((vx_uint32 *)base_ptr, addr.stride_y, (vx_uint32 *)pRef, plane_width_in_bytes, region_width, region_height, (vx_int64)errLimitMin, (vx_int64)errLimitMax); } else if (pixelType == VX_TYPE_FLOAT32) { errorPixelCount = ComparePixels((vx_float32 *)base_ptr, addr.stride_y, (vx_float32 *)pRef, plane_width_in_bytes, region_width, region_height, (vx_float32)errLimitMin, (vx_float32)errLimitMax); } else if (format == VX_DF_IMAGE_U1_AMD) { errorPixelCount = ComparePixelsU001((vx_uint8 *)base_ptr, addr.stride_y, (vx_uint8 *)pRef, plane_width_in_bytes, region_width, region_height); } else { errorPixelCount = ComparePixels((vx_uint8 *)base_ptr, addr.stride_y, (vx_uint8 *)pRef, plane_width_in_bytes, region_width, region_height, (vx_int32)errLimitMin, (vx_int32)errLimitMax); } ERROR_CHECK(vxCommitImagePatch(image, rectRegion, plane, &addr, base_ptr)); // report results errorPixelCountTotal += errorPixelCount; if (errorPixelCount > 0) { char name[64]; vxGetReferenceName((vx_reference)image, name, sizeof(name)); printf("ERROR: Image COMPARE MISMATCHED %s plane#%d " VX_FMT_SIZE "-pixel(s) with frame#%d of %s\n", name, plane, errorPixelCount, frameNumber, fileNameRef ? fileNameRef : "???"); } // skip to begnning of next plane pRefPlane += plane_height * plane_width_in_bytes; } return errorPixelCountTotal; }
// Assumes that an initial valid guess of change rectangle 'rect' is passed. static void MinimizeChangeRectangle(const WebPPicture* const src, const WebPPicture* const dst, WebPFrameRect* const rect) { int i, j; // Sanity checks. assert(src->width == dst->width && src->height == dst->height); assert(rect->x_offset + rect->width <= dst->width); assert(rect->y_offset + rect->height <= dst->height); // Left boundary. for (i = rect->x_offset; i < rect->x_offset + rect->width; ++i) { const uint32_t* const src_argb = &src->argb[rect->y_offset * src->argb_stride + i]; const uint32_t* const dst_argb = &dst->argb[rect->y_offset * dst->argb_stride + i]; if (ComparePixels(src_argb, src->argb_stride, dst_argb, dst->argb_stride, rect->height)) { --rect->width; // Redundant column. ++rect->x_offset; } else { break; } } if (rect->width == 0) goto End; // Right boundary. for (i = rect->x_offset + rect->width - 1; i >= rect->x_offset; --i) { const uint32_t* const src_argb = &src->argb[rect->y_offset * src->argb_stride + i]; const uint32_t* const dst_argb = &dst->argb[rect->y_offset * dst->argb_stride + i]; if (ComparePixels(src_argb, src->argb_stride, dst_argb, dst->argb_stride, rect->height)) { --rect->width; // Redundant column. } else { break; } } if (rect->width == 0) goto End; // Top boundary. for (j = rect->y_offset; j < rect->y_offset + rect->height; ++j) { const uint32_t* const src_argb = &src->argb[j * src->argb_stride + rect->x_offset]; const uint32_t* const dst_argb = &dst->argb[j * dst->argb_stride + rect->x_offset]; if (ComparePixels(src_argb, 1, dst_argb, 1, rect->width)) { --rect->height; // Redundant row. ++rect->y_offset; } else { break; } } if (rect->height == 0) goto End; // Bottom boundary. for (j = rect->y_offset + rect->height - 1; j >= rect->y_offset; --j) { const uint32_t* const src_argb = &src->argb[j * src->argb_stride + rect->x_offset]; const uint32_t* const dst_argb = &dst->argb[j * dst->argb_stride + rect->x_offset]; if (ComparePixels(src_argb, 1, dst_argb, 1, rect->width)) { --rect->height; // Redundant row. } else { break; } } if (rect->height == 0) goto End; if (rect->width == 0 || rect->height == 0) { End: // TODO(later): This rare case can happen for a bad GIF. In such a case, the // frame should not be encoded at all and the duration of prev frame should // be increased instead. For now, we just create a 1x1 frame at zero offset. rect->x_offset = 0; rect->y_offset = 0; rect->width = 1; rect->height = 1; } }