blob: 670569237102db22b33d2a5c49f847195dc3583f [file] [log] [blame]
// Copyright 2023 The Wuffs Authors.
//
// 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
//
// https://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.
// --------
static inline uint32_t //
wuffs_base__u32__max_of_4(uint32_t a, uint32_t b, uint32_t c, uint32_t d) {
return wuffs_base__u32__max( //
wuffs_base__u32__max(a, b), //
wuffs_base__u32__max(c, d));
}
static inline uint32_t //
wuffs_base__u32__min_of_5(uint32_t a,
uint32_t b,
uint32_t c,
uint32_t d,
uint32_t e) {
return wuffs_base__u32__min( //
wuffs_base__u32__min( //
wuffs_base__u32__min(a, b), //
wuffs_base__u32__min(c, d)), //
e);
}
// Preconditions: see all the checks made in
// wuffs_base__pixel_swizzler__swizzle_ycck before calling this function. For
// example, (width > 0) is a precondition, but there are many more.
static void //
wuffs_base__pixel_swizzler__swizzle_ycc__general__box_filter(
const wuffs_base__pixel_swizzler* p,
wuffs_base__pixel_buffer* dst,
wuffs_base__slice_u8 dst_palette,
uint32_t width,
uint32_t height,
const uint8_t* src_ptr0,
const uint8_t* src_ptr1,
const uint8_t* src_ptr2,
uint32_t stride0,
uint32_t stride1,
uint32_t stride2,
uint32_t h0_out_of_12,
uint32_t h1_out_of_12,
uint32_t h2_out_of_12,
uint32_t v0_out_of_12,
uint32_t v1_out_of_12,
uint32_t v2_out_of_12) {
uint32_t iy0 = 0;
uint32_t iy1 = 0;
uint32_t iy2 = 0;
uint32_t y = 0;
while (true) {
const uint8_t* src_iter0 = src_ptr0;
const uint8_t* src_iter1 = src_ptr1;
const uint8_t* src_iter2 = src_ptr2;
uint32_t ix0 = 0;
uint32_t ix1 = 0;
uint32_t ix2 = 0;
uint32_t x = 0;
while (true) {
wuffs_base__pixel_buffer__set_color_u32_at(
dst, x, y,
wuffs_base__color_ycc__as__color_u32(*src_iter0, *src_iter1,
*src_iter2));
if ((x + 1) == width) {
break;
}
x = x + 1;
ix0 += h0_out_of_12;
if (ix0 >= 12) {
ix0 = 0;
src_iter0++;
}
ix1 += h1_out_of_12;
if (ix1 >= 12) {
ix1 = 0;
src_iter1++;
}
ix2 += h2_out_of_12;
if (ix2 >= 12) {
ix2 = 0;
src_iter2++;
}
}
if ((y + 1) == height) {
break;
}
y = y + 1;
iy0 += v0_out_of_12;
if (iy0 >= 12) {
iy0 = 0;
src_ptr0 += stride0;
}
iy1 += v1_out_of_12;
if (iy1 >= 12) {
iy1 = 0;
src_ptr1 += stride1;
}
iy2 += v2_out_of_12;
if (iy2 >= 12) {
iy2 = 0;
src_ptr2 += stride2;
}
}
}
// wuffs_base__pixel_swizzler__flattened_length is like
// wuffs_base__table__flattened_length but returns uint64_t (not size_t) and
// also accounts for subsampling.
static uint64_t //
wuffs_base__pixel_swizzler__flattened_length(uint32_t width,
uint32_t height,
uint32_t stride,
uint32_t inv_h,
uint32_t inv_v) {
uint64_t scaled_width = (((uint64_t)width) + (inv_h - 1)) / inv_h;
uint64_t scaled_height = (((uint64_t)height) + (inv_v - 1)) / inv_v;
if (scaled_height <= 0) {
return 0;
}
return ((scaled_height - 1) * stride) + scaled_width;
}
WUFFS_BASE__MAYBE_STATIC wuffs_base__status //
wuffs_base__pixel_swizzler__swizzle_ycck(const wuffs_base__pixel_swizzler* p,
wuffs_base__pixel_buffer* dst,
wuffs_base__slice_u8 dst_palette,
uint32_t width,
uint32_t height,
wuffs_base__slice_u8 src0,
wuffs_base__slice_u8 src1,
wuffs_base__slice_u8 src2,
wuffs_base__slice_u8 src3,
uint32_t width0,
uint32_t width1,
uint32_t width2,
uint32_t width3,
uint32_t height0,
uint32_t height1,
uint32_t height2,
uint32_t height3,
uint32_t stride0,
uint32_t stride1,
uint32_t stride2,
uint32_t stride3,
uint8_t h0,
uint8_t h1,
uint8_t h2,
uint8_t h3,
uint8_t v0,
uint8_t v1,
uint8_t v2,
uint8_t v3,
bool triangle_filter_for_2to1) {
if (!p) {
return wuffs_base__make_status(wuffs_base__error__bad_receiver);
} else if ((h3 != 0) || (v3 != 0) || triangle_filter_for_2to1) {
// TODO: support the K in YCCK and support triangle_filter_for_2to1.
return wuffs_base__make_status(
wuffs_base__error__unsupported_pixel_swizzler_option);
} else if (!dst || (width > 0xFFFF) || (height > 0xFFFF) || //
(4 <= (h0 - 1)) || (4 <= (v0 - 1)) || //
(4 <= (h1 - 1)) || (4 <= (v1 - 1)) || //
(4 <= (h2 - 1)) || (4 <= (v2 - 1))) {
return wuffs_base__make_status(wuffs_base__error__bad_argument);
}
uint32_t max_incl_h = wuffs_base__u32__max_of_4(h0, h1, h2, h3);
uint32_t max_incl_v = wuffs_base__u32__max_of_4(v0, v1, v2, v3);
uint32_t inv_h0 = max_incl_h / h0;
uint32_t inv_h1 = max_incl_h / h1;
uint32_t inv_h2 = max_incl_h / h2;
uint32_t inv_v0 = max_incl_v / v0;
uint32_t inv_v1 = max_incl_v / v1;
uint32_t inv_v2 = max_incl_v / v2;
width = wuffs_base__u32__min_of_5( //
width, //
width0 * inv_h0, //
width1 * inv_h1, //
width2 * inv_h2, //
wuffs_base__pixel_config__width(&dst->pixcfg));
height = wuffs_base__u32__min_of_5( //
height, //
height0 * inv_v0, //
height1 * inv_v1, //
height2 * inv_v2, //
wuffs_base__pixel_config__height(&dst->pixcfg));
if (((h0 * inv_h0) != max_incl_h) || //
((h1 * inv_h1) != max_incl_h) || //
((h2 * inv_h2) != max_incl_h) || //
((v0 * inv_v0) != max_incl_v) || //
((v1 * inv_v1) != max_incl_v) || //
((v2 * inv_v2) != max_incl_v) || //
(src0.len < wuffs_base__pixel_swizzler__flattened_length(
width, height, stride0, inv_h0, inv_v0)) ||
(src1.len < wuffs_base__pixel_swizzler__flattened_length(
width, height, stride1, inv_h1, inv_v1)) ||
(src2.len < wuffs_base__pixel_swizzler__flattened_length(
width, height, stride2, inv_h2, inv_v2))) {
return wuffs_base__make_status(wuffs_base__error__bad_argument);
}
if (wuffs_base__pixel_format__is_planar(&dst->pixcfg.private_impl.pixfmt)) {
// TODO: see wuffs_base__pixel_buffer__set_color_u32_at's TODO.
return wuffs_base__make_status(
wuffs_base__error__unsupported_pixel_swizzler_option);
}
switch (dst->pixcfg.private_impl.pixfmt.repr) {
case WUFFS_BASE__PIXEL_FORMAT__Y:
case WUFFS_BASE__PIXEL_FORMAT__Y_16LE:
case WUFFS_BASE__PIXEL_FORMAT__Y_16BE:
case WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_NONPREMUL:
case WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_PREMUL:
case WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_BINARY:
case WUFFS_BASE__PIXEL_FORMAT__BGR_565:
case WUFFS_BASE__PIXEL_FORMAT__BGR:
case WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL:
case WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL_4X16LE:
case WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL:
case WUFFS_BASE__PIXEL_FORMAT__BGRX:
case WUFFS_BASE__PIXEL_FORMAT__RGB:
case WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL:
case WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL:
case WUFFS_BASE__PIXEL_FORMAT__RGBX:
break;
default:
// TODO: see wuffs_base__pixel_buffer__set_color_u32_at's TODO.
return wuffs_base__make_status(
wuffs_base__error__unsupported_pixel_swizzler_option);
}
if ((width <= 0) || (height <= 0)) {
return wuffs_base__make_status(NULL);
}
wuffs_base__pixel_swizzler__swizzle_ycc__general__box_filter(
p, dst, dst_palette, width, height, //
src0.ptr, src1.ptr, src2.ptr, //
stride0, stride1, stride2, //
(h0 * 12) / max_incl_h, //
(h1 * 12) / max_incl_h, //
(h2 * 12) / max_incl_h, //
(v0 * 12) / max_incl_v, //
(v1 * 12) / max_incl_v, //
(v2 * 12) / max_incl_v);
return wuffs_base__make_status(NULL);
}