blob: 907b72ea930c768c89e08b4698b1a6c6fc1e37f8 [file] [log] [blame]
// Copyright 2019 Google LLC
//
// 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.
// -----------------------------------------------------------------------------
//
// Convenience color types and conversion functions.
//
// Authors: Yannis Guyon (yguyon@google.com)
#ifndef WP2_COMMON_COLOR_PRECISION_H_
#define WP2_COMMON_COLOR_PRECISION_H_
#include <cassert>
#include <cstdint>
#include "src/dsp/math.h"
#include "src/wp2/base.h"
#include "src/wp2/format_constants.h"
namespace WP2 {
//------------------------------------------------------------------------------
// Color structs
// Y, Co, Cg are not literally premultiplied by alpha but the RGB equivalent is.
struct AYCoCg19b {
static constexpr uint8_t kAlphaMax = 1u;
static constexpr uint8_t kYCoCgBitDepth = 6;
static constexpr uint8_t kYCoCgMax = (1u << kYCoCgBitDepth) - 1u;
uint8_t a; // Precision: 1 bit
uint8_t y, co, cg; // Precision: 6 bits
};
//------------------------------------------------------------------------------
namespace Internal {
template <typename TypeIn, int NumBitsIn, typename TypeOut, int NumBitsOut>
constexpr TypeOut Quantize(TypeIn x) {
static_assert(NumBitsIn > NumBitsOut, "Cannot quantize to wider range");
return (TypeOut)(x >> (NumBitsIn - NumBitsOut));
}
template <typename TypeIn, int NumBitsIn, typename TypeOut, int NumBitsOut>
constexpr TypeOut Dequantize(TypeIn x) {
static_assert(NumBitsIn < NumBitsOut, "Cannot dequantize to smaller range");
static_assert(2 * NumBitsIn >= NumBitsOut,
"Cannot dequantize more than twice the input range");
return (TypeOut)(((uint32_t)x << (NumBitsOut - NumBitsIn)) |
((uint32_t)x >> (2 * NumBitsIn - NumBitsOut)));
}
} // namespace Internal
//------------------------------------------------------------------------------
// Argb32b <-> uint32_t
constexpr uint32_t ToUInt32(Argb32b color) {
return ((uint32_t)color.a << 24) | (color.r << 16) | (color.g << 8) | color.b;
}
constexpr Argb32b ToArgb32b(uint32_t color) {
return {(uint8_t)((color >> 24) & 0xFFu), (uint8_t)((color >> 16) & 0xFFu),
(uint8_t)((color >> 8) & 0xFFu), (uint8_t)((color >> 0) & 0xFFu)};
}
//------------------------------------------------------------------------------
// Argb32b <-> uint8_t
inline void ToUInt8(Argb32b color, uint8_t argb[4]) {
argb[0] = color.a;
argb[1] = color.r;
argb[2] = color.g;
argb[3] = color.b;
}
inline Argb32b ToArgb32b(const uint8_t argb[4]) {
return {argb[0], argb[1], argb[2], argb[3]};
}
//------------------------------------------------------------------------------
// RGB12b <-> uint32_t
constexpr uint32_t ToUInt32(RGB12b color) {
return (((uint32_t)color.r & 0xFu) << 8) | (((uint32_t)color.g & 0xFu) << 4) |
(((uint32_t)color.b & 0xFu) << 0);
}
constexpr RGB12b ToRGB12b(uint32_t color) {
return {(uint8_t)((color >> 8) & 0xFu), (uint8_t)((color >> 4) & 0xFu),
(uint8_t)((color >> 0) & 0xFu)};
}
//------------------------------------------------------------------------------
// Argb32b <-> Argb38b
constexpr Argb38b ToArgb38b(Argb32b color) {
return {color.a, Internal::Dequantize<uint8_t, 8, uint16_t, 10>(color.r),
Internal::Dequantize<uint8_t, 8, uint16_t, 10>(color.g),
Internal::Dequantize<uint8_t, 8, uint16_t, 10>(color.b)};
}
constexpr Argb32b ToArgb32b(Argb38b color) {
return {color.a, Internal::Quantize<uint16_t, 10, uint8_t, 8>(color.r),
Internal::Quantize<uint16_t, 10, uint8_t, 8>(color.g),
Internal::Quantize<uint16_t, 10, uint8_t, 8>(color.b)};
}
//------------------------------------------------------------------------------
// Argb32b <-> RGB12b
constexpr RGB12b ToRGB12b(Argb32b color) {
return (color.a == 0) ? RGB12b{0xFu, 0xFu, 0xFu}
: RGB12b{Internal::Quantize<uint32_t, 8, uint8_t, 4>(
color.r * WP2::kAlphaMax / color.a),
Internal::Quantize<uint32_t, 8, uint8_t, 4>(
color.g * WP2::kAlphaMax / color.a),
Internal::Quantize<uint32_t, 8, uint8_t, 4>(
color.b * WP2::kAlphaMax / color.a)};
}
constexpr Argb32b ToArgb32b(RGB12b color) {
return {WP2::kAlphaMax, Internal::Dequantize<uint8_t, 4, uint8_t, 8>(color.r),
Internal::Dequantize<uint8_t, 4, uint8_t, 8>(color.g),
Internal::Dequantize<uint8_t, 4, uint8_t, 8>(color.b)};
}
//------------------------------------------------------------------------------
// Argb32b <-> AYCoCg19b (stable after three conversions)
// Does not use Quantize() functions to limit max loss from RGB per channel to 7
constexpr AYCoCg19b ToAYCoCg19b(Argb32b color) {
return {(uint8_t)(color.a >> 7),
(uint8_t)(((uint32_t)1u + color.g * 2 + color.r + color.b) >> 4),
(uint8_t)(((uint32_t)256u + color.r - color.b) >> 3),
(uint8_t)(((uint32_t)512u + color.g * 2 - color.r - color.b) >> 4)};
}
constexpr Argb32b ToArgb32b(AYCoCg19b color) {
return {(uint8_t)((color.a) ? WP2::kAlphaMax : 0u),
(uint8_t)Clamp(((int32_t)color.y - color.cg + color.co) * 4, 0,
(int32_t)WP2::kAlphaMax),
(uint8_t)Clamp(((int32_t)color.y + color.cg) * 4 - 128, 0,
(int32_t)WP2::kAlphaMax),
(uint8_t)Clamp(((int32_t)color.y - color.cg - color.co) * 4 + 256, 0,
(int32_t)WP2::kAlphaMax)};
}
//------------------------------------------------------------------------------
inline bool CheckPremultiplied(Argb32b color) {
return (color.r <= color.a && color.g <= color.a && color.b <= color.a);
}
inline bool CheckPremultiplied(Argb38b color) {
const uint16_t alpha_10b =
Internal::Dequantize<uint8_t, 8, uint16_t, 10>(color.a);
return (color.r <= alpha_10b && color.g <= alpha_10b && color.b <= alpha_10b);
}
//------------------------------------------------------------------------------
// Returns the maximum value for a given channel.
// 'channel' is the index of the channel in order ARGB.
static inline uint32_t FormatMax(WP2SampleFormat format, uint32_t channel) {
if (channel == 0) {
// Alpha
switch (format) {
case WP2_Argb_32:
case WP2_ARGB_32:
case WP2_XRGB_32:
case WP2_rgbA_32:
case WP2_RGBA_32:
case WP2_RGBX_32:
case WP2_bgrA_32:
case WP2_BGRA_32:
case WP2_BGRX_32:
case WP2_RGB_24:
case WP2_BGR_24:
case WP2_Argb_38: // Alpha in WP2_Argb_38 uses only 8 bits.
return WP2::kAlphaMax;
case WP2_Argb_64:
case WP2_ARGB_64:
case WP2_rgbA_64:
case WP2_RGBA_64:
case WP2_bgrA_64:
case WP2_BGRA_64:
case WP2_RGB_48:
return 65535;
case WP2_FORMAT_NUM:
break;
}
assert(false);
return 0;
} else {
// RGB
return (1u << WP2Formatbpc(format)) - 1u;
}
}
} // namespace WP2
#endif // WP2_COMMON_COLOR_PRECISION_H_