blob: 59b53e750e12203bd5f34837881a45da9af09785 [file] [log] [blame]
// After editing this file, run "go generate" in the parent directory.
// Copyright 2017 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.
// ---------------- Images
const uint32_t wuffs_base__pixel_format__bits_per_channel[16] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x0A, 0x0C, 0x10, 0x18, 0x20, 0x30, 0x40,
};
// --------
static inline uint32_t //
wuffs_base__swap_u32_argb_abgr(uint32_t u) {
uint32_t o = u & 0xFF00FF00;
uint32_t r = u & 0x00FF0000;
uint32_t b = u & 0x000000FF;
return o | (r >> 16) | (b << 16);
}
// --------
WUFFS_BASE__MAYBE_STATIC wuffs_base__color_u32_argb_premul //
wuffs_base__pixel_buffer__color_u32_at(const wuffs_base__pixel_buffer* pb,
uint32_t x,
uint32_t y) {
if (!pb || (x >= pb->pixcfg.private_impl.width) ||
(y >= pb->pixcfg.private_impl.height)) {
return 0;
}
if (wuffs_base__pixel_format__is_planar(&pb->pixcfg.private_impl.pixfmt)) {
// TODO: support planar formats.
return 0;
}
size_t stride = pb->private_impl.planes[0].stride;
uint8_t* row = pb->private_impl.planes[0].ptr + (stride * ((size_t)y));
switch (pb->pixcfg.private_impl.pixfmt.repr) {
case WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL:
case WUFFS_BASE__PIXEL_FORMAT__BGRA_BINARY:
return wuffs_base__load_u32le__no_bounds_check(row + (4 * ((size_t)x)));
case WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_PREMUL:
case WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_BINARY: {
uint8_t* palette = pb->private_impl.planes[3].ptr;
return wuffs_base__load_u32le__no_bounds_check(palette +
(4 * ((size_t)row[x])));
}
// Common formats above. Rarer formats below.
case WUFFS_BASE__PIXEL_FORMAT__Y:
return 0xFF000000 | (0x00010101 * ((uint32_t)(row[x])));
case WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_NONPREMUL: {
uint8_t* palette = pb->private_impl.planes[3].ptr;
return wuffs_base__color_u32_argb_nonpremul__as__color_u32_argb_premul(
wuffs_base__load_u32le__no_bounds_check(palette +
(4 * ((size_t)row[x]))));
}
case WUFFS_BASE__PIXEL_FORMAT__BGR_565:
return wuffs_base__color_u16_rgb_565__as__color_u32_argb_premul(
wuffs_base__load_u16le__no_bounds_check(row + (2 * ((size_t)x))));
case WUFFS_BASE__PIXEL_FORMAT__BGR:
return 0xFF000000 |
wuffs_base__load_u24le__no_bounds_check(row + (3 * ((size_t)x)));
case WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL:
return wuffs_base__color_u32_argb_nonpremul__as__color_u32_argb_premul(
wuffs_base__load_u32le__no_bounds_check(row + (4 * ((size_t)x))));
case WUFFS_BASE__PIXEL_FORMAT__BGRX:
return 0xFF000000 |
wuffs_base__load_u32le__no_bounds_check(row + (4 * ((size_t)x)));
case WUFFS_BASE__PIXEL_FORMAT__RGB:
return wuffs_base__swap_u32_argb_abgr(
0xFF000000 |
wuffs_base__load_u24le__no_bounds_check(row + (3 * ((size_t)x))));
case WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL:
return wuffs_base__swap_u32_argb_abgr(
wuffs_base__color_u32_argb_nonpremul__as__color_u32_argb_premul(
wuffs_base__load_u32le__no_bounds_check(row +
(4 * ((size_t)x)))));
case WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL:
case WUFFS_BASE__PIXEL_FORMAT__RGBA_BINARY:
return wuffs_base__swap_u32_argb_abgr(
wuffs_base__load_u32le__no_bounds_check(row + (4 * ((size_t)x))));
case WUFFS_BASE__PIXEL_FORMAT__RGBX:
return wuffs_base__swap_u32_argb_abgr(
0xFF000000 |
wuffs_base__load_u32le__no_bounds_check(row + (4 * ((size_t)x))));
default:
// TODO: support more formats.
break;
}
return 0;
}
WUFFS_BASE__MAYBE_STATIC wuffs_base__status //
wuffs_base__pixel_buffer__set_color_u32_at(
wuffs_base__pixel_buffer* pb,
uint32_t x,
uint32_t y,
wuffs_base__color_u32_argb_premul color) {
if (!pb) {
return wuffs_base__make_status(wuffs_base__error__bad_receiver);
}
if ((x >= pb->pixcfg.private_impl.width) ||
(y >= pb->pixcfg.private_impl.height)) {
return wuffs_base__make_status(wuffs_base__error__bad_argument);
}
if (wuffs_base__pixel_format__is_planar(&pb->pixcfg.private_impl.pixfmt)) {
// TODO: support planar formats.
return wuffs_base__make_status(wuffs_base__error__unsupported_option);
}
size_t stride = pb->private_impl.planes[0].stride;
uint8_t* row = pb->private_impl.planes[0].ptr + (stride * ((size_t)y));
switch (pb->pixcfg.private_impl.pixfmt.repr) {
case WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL:
case WUFFS_BASE__PIXEL_FORMAT__BGRX:
wuffs_base__store_u32le__no_bounds_check(row + (4 * ((size_t)x)), color);
break;
// Common formats above. Rarer formats below.
case WUFFS_BASE__PIXEL_FORMAT__Y:
wuffs_base__store_u8__no_bounds_check(
row + ((size_t)x),
wuffs_base__color_u32_argb_premul__as__color_u8_gray(color));
break;
case WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_BINARY:
wuffs_base__store_u8__no_bounds_check(
row + ((size_t)x), wuffs_base__pixel_palette__closest_element(
wuffs_base__pixel_buffer__palette(pb),
pb->pixcfg.private_impl.pixfmt, color));
break;
case WUFFS_BASE__PIXEL_FORMAT__BGR_565:
wuffs_base__store_u16le__no_bounds_check(
row + (2 * ((size_t)x)),
wuffs_base__color_u32_argb_premul__as__color_u16_rgb_565(color));
break;
case WUFFS_BASE__PIXEL_FORMAT__BGR:
wuffs_base__store_u24le__no_bounds_check(row + (3 * ((size_t)x)), color);
break;
case WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL:
wuffs_base__store_u32le__no_bounds_check(
row + (4 * ((size_t)x)),
wuffs_base__color_u32_argb_premul__as__color_u32_argb_nonpremul(
color));
break;
case WUFFS_BASE__PIXEL_FORMAT__RGB:
wuffs_base__store_u24le__no_bounds_check(
row + (3 * ((size_t)x)), wuffs_base__swap_u32_argb_abgr(color));
break;
case WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL:
wuffs_base__store_u32le__no_bounds_check(
row + (4 * ((size_t)x)),
wuffs_base__color_u32_argb_premul__as__color_u32_argb_nonpremul(
wuffs_base__swap_u32_argb_abgr(color)));
break;
case WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL:
case WUFFS_BASE__PIXEL_FORMAT__RGBX:
wuffs_base__store_u32le__no_bounds_check(
row + (4 * ((size_t)x)), wuffs_base__swap_u32_argb_abgr(color));
break;
default:
// TODO: support more formats.
return wuffs_base__make_status(wuffs_base__error__unsupported_option);
}
return wuffs_base__make_status(NULL);
}
// --------
WUFFS_BASE__MAYBE_STATIC uint8_t //
wuffs_base__pixel_palette__closest_element(
wuffs_base__slice_u8 palette_slice,
wuffs_base__pixel_format palette_format,
wuffs_base__color_u32_argb_premul c) {
size_t n = palette_slice.len / 4;
if (n > 256) {
n = 256;
}
size_t best_index = 0;
uint64_t best_score = 0xFFFFFFFFFFFFFFFF;
// Work in 16-bit color.
uint32_t ca = 0x101 * (0xFF & (c >> 24));
uint32_t cr = 0x101 * (0xFF & (c >> 16));
uint32_t cg = 0x101 * (0xFF & (c >> 8));
uint32_t cb = 0x101 * (0xFF & (c >> 0));
switch (palette_format.repr) {
case WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_NONPREMUL:
case WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_PREMUL:
case WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_BINARY: {
bool nonpremul = palette_format.repr ==
WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_NONPREMUL;
size_t i;
for (i = 0; i < n; i++) {
// Work in 16-bit color.
uint32_t pb = 0x101 * ((uint32_t)(palette_slice.ptr[(4 * i) + 0]));
uint32_t pg = 0x101 * ((uint32_t)(palette_slice.ptr[(4 * i) + 1]));
uint32_t pr = 0x101 * ((uint32_t)(palette_slice.ptr[(4 * i) + 2]));
uint32_t pa = 0x101 * ((uint32_t)(palette_slice.ptr[(4 * i) + 3]));
// Convert to premultiplied alpha.
if (nonpremul && (pa != 0xFFFF)) {
pb = (pb * pa) / 0xFFFF;
pg = (pg * pa) / 0xFFFF;
pr = (pr * pa) / 0xFFFF;
}
// These deltas are conceptually int32_t (signed) but after squaring,
// it's equivalent to work in uint32_t (unsigned).
pb -= cb;
pg -= cg;
pr -= cr;
pa -= ca;
uint64_t score = ((uint64_t)(pb * pb)) + ((uint64_t)(pg * pg)) +
((uint64_t)(pr * pr)) + ((uint64_t)(pa * pa));
if (best_score > score) {
best_score = score;
best_index = i;
}
}
break;
}
}
return (uint8_t)best_index;
}