Alien-FreeImage
view release on metacpan or search on metacpan
src/Source/LibWebP/src/mux/mux.anim_encode.c view on Meta::CPAN
return enc; // All OK.
Err:
WebPAnimEncoderDelete(enc);
return NULL;
}
// Release the data contained by 'encoded_frame'.
static void FrameRelease(EncodedFrame* const encoded_frame) {
if (encoded_frame != NULL) {
WebPDataClear(&encoded_frame->sub_frame_.bitstream);
WebPDataClear(&encoded_frame->key_frame_.bitstream);
memset(encoded_frame, 0, sizeof(*encoded_frame));
}
}
void WebPAnimEncoderDelete(WebPAnimEncoder* enc) {
if (enc != NULL) {;
WebPPictureFree(&enc->curr_canvas_copy_);
WebPPictureFree(&enc->prev_canvas_);
WebPPictureFree(&enc->prev_canvas_disposed_);
if (enc->encoded_frames_ != NULL) {
size_t i;
for (i = 0; i < enc->size_; ++i) {
FrameRelease(&enc->encoded_frames_[i]);
}
WebPSafeFree(enc->encoded_frames_);
}
WebPMuxDelete(enc->mux_);
WebPSafeFree(enc);
}
}
// -----------------------------------------------------------------------------
// Frame addition.
// Returns cached frame at the given 'position'.
static EncodedFrame* GetFrame(const WebPAnimEncoder* const enc,
size_t position) {
assert(enc->start_ + position < enc->size_);
return &enc->encoded_frames_[enc->start_ + position];
}
// Returns true if 'length' number of pixels in 'src' and 'dst' are identical,
// assuming the given step sizes between pixels.
static WEBP_INLINE int ComparePixels(const uint32_t* src, int src_step,
const uint32_t* dst, int dst_step,
int length) {
assert(length > 0);
while (length-- > 0) {
if (*src != *dst) {
return 0;
}
src += src_step;
dst += dst_step;
}
return 1;
}
// 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 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;
}
}
// Snap rectangle to even offsets (and adjust dimensions if needed).
static WEBP_INLINE void SnapToEvenOffsets(FrameRect* const rect) {
rect->width_ += (rect->x_offset_ & 1);
rect->height_ += (rect->y_offset_ & 1);
rect->x_offset_ &= ~1;
rect->y_offset_ &= ~1;
}
// Given previous and current canvas, picks the optimal rectangle for the
// current frame. The initial guess for 'rect' will be the full canvas.
static int GetSubRect(const WebPPicture* const prev_canvas,
const WebPPicture* const curr_canvas, int is_key_frame,
int is_first_frame, FrameRect* const rect,
WebPPicture* const sub_frame) {
rect->x_offset_ = 0;
rect->y_offset_ = 0;
rect->width_ = curr_canvas->width;
rect->height_ = curr_canvas->height;
if (!is_key_frame || is_first_frame) { // Optimize frame rectangle.
// Note: This behaves as expected for first frame, as 'prev_canvas' is
// initialized to a fully transparent canvas in the beginning.
MinimizeChangeRectangle(prev_canvas, curr_canvas, rect);
}
SnapToEvenOffsets(rect);
return WebPPictureView(curr_canvas, rect->x_offset_, rect->y_offset_,
rect->width_, rect->height_, sub_frame);
}
// TODO: Also used in picture.c. Move to a common location?
// Copy width x height pixels from 'src' to 'dst' honoring the strides.
static void CopyPlane(const uint8_t* src, int src_stride,
uint8_t* dst, int dst_stride, int width, int height) {
while (height-- > 0) {
memcpy(dst, src, width);
src += src_stride;
dst += dst_stride;
}
}
// Copy pixels from 'src' to 'dst' honoring strides. 'src' and 'dst' are assumed
// to be already allocated.
static void CopyPixels(const WebPPicture* const src, WebPPicture* const dst) {
assert(src->width == dst->width && src->height == dst->height);
assert(src->use_argb && dst->use_argb);
CopyPlane((uint8_t*)src->argb, 4 * src->argb_stride, (uint8_t*)dst->argb,
4 * dst->argb_stride, 4 * src->width, src->height);
}
static void DisposeFrameRectangle(int dispose_method,
const FrameRect* const rect,
WebPPicture* const curr_canvas) {
assert(rect != NULL);
if (dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
WebPUtilClearPic(curr_canvas, rect);
}
}
static uint32_t RectArea(const FrameRect* const rect) {
return (uint32_t)rect->width_ * rect->height_;
}
static int IsBlendingPossible(const WebPPicture* const src,
const WebPPicture* const dst,
const FrameRect* const rect) {
int i, j;
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);
for (j = rect->y_offset_; j < rect->y_offset_ + rect->height_; ++j) {
( run in 0.503 second using v1.01-cache-2.11-cpan-411bb0df24b )