// Copyright (c) the JPEG XL Project Authors. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file.
// These templates are not found via ADL. using hwy::HWY_NAMESPACE::Mul; using hwy::HWY_NAMESPACE::MulAdd; using hwy::HWY_NAMESPACE::MulSub; using hwy::HWY_NAMESPACE::Sqrt; using hwy::HWY_NAMESPACE::Sub;
// Given a set of DCT coefficients, this returns the result of performing cosine // interpolation on the original samples. float ContinuousIDCT(const Dct32& dct, constfloat t) { // We compute here the DCT-3 of the `dct` vector, rescaled by a factor of // sqrt(32). This is such that an input vector vector {x, 0, ..., 0} produces // a constant result of x. dct[0] was scaled in Dequantize() to allow uniform // treatment of all the coefficients.
constexpr float kMultipliers[32] = {
kPi / 32 * 0, kPi / 32 * 1, kPi / 32 * 2, kPi / 32 * 3, kPi / 32 * 4,
kPi / 32 * 5, kPi / 32 * 6, kPi / 32 * 7, kPi / 32 * 8, kPi / 32 * 9,
kPi / 32 * 10, kPi / 32 * 11, kPi / 32 * 12, kPi / 32 * 13, kPi / 32 * 14,
kPi / 32 * 15, kPi / 32 * 16, kPi / 32 * 17, kPi / 32 * 18, kPi / 32 * 19,
kPi / 32 * 20, kPi / 32 * 21, kPi / 32 * 22, kPi / 32 * 23, kPi / 32 * 24,
kPi / 32 * 25, kPi / 32 * 26, kPi / 32 * 27, kPi / 32 * 28, kPi / 32 * 29,
kPi / 32 * 30, kPi / 32 * 31,
};
HWY_CAPPED(float, 32) df; auto result = Zero(df); constauto tandhalf = Set(df, t + 0.5f); for (int i = 0; i < 32; i += Lanes(df)) { auto cos_arg = Mul(LoadU(df, kMultipliers + i), tandhalf); auto cos = FastCosf(df, cos_arg); auto local_res = Mul(LoadU(df, dct.data() + i), cos);
result = MulAdd(Set(df, kSqrt2), local_res, result);
} return GetLane(SumOfLanes(df, result));
}
// It is not in spec, but reasonable limit to avoid overflows. template <typename T>
Status ValidateSplinePointPos(const T& x, const T& y) {
constexpr T kSplinePosLimit = 1u << 23; if ((x >= kSplinePosLimit) || (x <= -kSplinePosLimit) ||
(y >= kSplinePosLimit) || (y <= -kSplinePosLimit)) { return JXL_FAILURE("Spline coordinates out of bounds");
} returntrue;
}
// Maximum number of spline control points per frame is // std::min(kMaxNumControlPoints, xsize * ysize / 2)
constexpr size_t kMaxNumControlPoints = 1u << 20u;
constexpr size_t kMaxNumControlPointsPerPixelRatio = 2;
// TODO(eustas): avoid making a copy of "points". void DrawCentripetalCatmullRomSpline(std::vector<Spline::Point> points,
std::vector<Spline::Point>& result) { if (points.empty()) return; if (points.size() == 1) {
result.push_back(points[0]); return;
} // Number of points to compute between each control point. static constexpr int kNumPoints = 16;
result.reserve((points.size() - 1) * kNumPoints + 1);
points.insert(points.begin(), points[0] + (points[0] - points[1]));
points.push_back(points[points.size() - 1] +
(points[points.size() - 1] - points[points.size() - 2])); // points has at least 4 elements at this point. for (size_t start = 0; start < points.size() - 3; ++start) { // 4 of them are used, and we draw from p[1] to p[2]. const Spline::Point* const p = &points[start];
result.push_back(p[1]); float d[3]; float t[4];
t[0] = 0; for (int k = 0; k < 3; ++k) { // TODO(eustas): for each segment delta is calculated 3 times... // TODO(eustas): restrict d[k] with reasonable limit and spec it.
d[k] = std::sqrt(hypotf(p[k + 1].x - p[k].x, p[k + 1].y - p[k].y));
t[k + 1] = t[k] + d[k];
} for (int i = 1; i < kNumPoints; ++i) { constfloat tt = d[0] + (static_cast<float>(i) / kNumPoints) * d[1];
Spline::Point a[3]; for (int k = 0; k < 3; ++k) { // TODO(eustas): reciprocal multiplication would be faster.
a[k] = p[k] + ((tt - t[k]) / d[k]) * (p[k + 1] - p[k]);
}
Spline::Point b[2]; for (int k = 0; k < 2; ++k) {
b[k] = a[k] + ((tt - t[k]) / (d[k] + d[k + 1])) * (a[k + 1] - a[k]);
}
result.push_back(b[0] + ((tt - t[1]) / d[1]) * (b[1] - b[0]));
}
}
result.push_back(points[points.size() - 2]);
}
// Move along the line segments defined by `points`, `kDesiredRenderingDistance` // pixels at a time, and call `functor` with each point and the actual distance // to the previous point (which will always be kDesiredRenderingDistance except // possibly for the very last point). // TODO(eustas): this method always adds the last point, but never the first // (unless those are one); I believe both ends matter. template <typename Points, typename Functor>
Status ForEachEquallySpacedPoint(const Points& points, const Functor& functor) {
JXL_ENSURE(!points.empty());
Spline::Point current = points.front();
functor(current, kDesiredRenderingDistance); auto next = points.begin(); while (next != points.end()) { const Spline::Point* previous = ¤t; float arclength_from_previous = 0.f; for (;;) { if (next == points.end()) {
functor(*previous, arclength_from_previous); returntrue;
} constfloat arclength_to_next =
std::sqrt((*next - *previous).SquaredNorm()); if (arclength_from_previous + arclength_to_next >=
kDesiredRenderingDistance) {
current =
*previous + ((kDesiredRenderingDistance - arclength_from_previous) /
arclength_to_next) *
(*next - *previous);
functor(current, kDesiredRenderingDistance); break;
}
arclength_from_previous += arclength_to_next;
previous = &*next;
++next;
}
} returntrue;
}
constauto inv_quant = InvAdjustedQuant(quantization_adjustment); for (int c = 0; c < 3; ++c) { for (int i = 0; i < 32; ++i) { constfloat inv_dct_factor = (i == 0) ? kSqrt0_5 : 1.0f;
result.color_dct[c][i] =
color_dct_[c][i] * inv_dct_factor * kChannelWeight[c] * inv_quant;
}
} for (int i = 0; i < 32; ++i) {
result.color_dct[0][i] += y_to_x * result.color_dct[1][i];
result.color_dct[2][i] += y_to_b * result.color_dct[1][i];
}
uint64_t width_estimate = 0;
uint64_t color[3] = {}; for (int c = 0; c < 3; ++c) { for (int i = 0; i < 32; ++i) {
color[c] += static_cast<uint64_t>(
std::ceil(inv_quant * std::abs(color_dct_[c][i])));
}
}
color[0] += static_cast<uint64_t>(std::ceil(std::abs(y_to_x))) * color[1];
color[2] += static_cast<uint64_t>(std::ceil(std::abs(y_to_b))) * color[1]; // This is not taking kChannelWeight into account, but up to constant factors // it gives an indication of the influence of the color values on the area // that will need to be rendered. const uint64_t max_color = std::max({color[1], color[0], color[2]});
uint64_t logcolor =
std::max(kOne, static_cast<uint64_t>(CeilLog2Nonzero(kOne + max_color)));
for (int i = 0; i < 32; ++i) { constfloat inv_dct_factor = (i == 0) ? kSqrt0_5 : 1.0f;
result.sigma_dct[i] =
sigma_dct_[i] * inv_dct_factor * kChannelWeight[3] * inv_quant; // If we include the factor kChannelWeight[3]=.3333f here, we get a // realistic area estimate. We leave it out to simplify the calculations, // and understand that this way we underestimate the area by a factor of // 1/(0.3333*0.3333). This is taken into account in the limits below. float weight_f = std::ceil(inv_quant * std::abs(sigma_dct_[i]));
uint64_t weight = static_cast<uint64_t>(std::min(weight_limit, std::max(1.0f, weight_f)));
width_estimate += weight * weight * logcolor;
}
*total_estimated_area_reached += (width_estimate * manhattan_distance); if (*total_estimated_area_reached > area_limit) { return JXL_FAILURE("Too large total_estimated_area eached: %" PRIu64,
*total_estimated_area_reached);
}
returntrue;
}
Status QuantizedSpline::Decode(const std::vector<uint8_t>& context_map,
ANSSymbolReader* const decoder,
BitReader* const br, const size_t max_control_points,
size_t* total_num_control_points) { const size_t num_control_points =
decoder->ReadHybridUint(kNumControlPointsContext, br, context_map); if (num_control_points > max_control_points) { return JXL_FAILURE("Too many control points: %" PRIuS, num_control_points);
}
*total_num_control_points += num_control_points; if (*total_num_control_points > max_control_points) { return JXL_FAILURE("Too many control points: %" PRIuS,
*total_num_control_points);
}
control_points_.resize(num_control_points); // Maximal image dimension.
constexpr int64_t kDeltaLimit = 1u << 30; for (std::pair<int64_t, int64_t>& control_point : control_points_) {
control_point.first = UnpackSigned(
decoder->ReadHybridUint(kControlPointsContext, br, context_map));
control_point.second = UnpackSigned(
decoder->ReadHybridUint(kControlPointsContext, br, context_map)); // Check delta-deltas are not outrageous; it is not in spec, but there is // no reason to allow larger values. if ((control_point.first >= kDeltaLimit) ||
(control_point.first <= -kDeltaLimit) ||
(control_point.second >= kDeltaLimit) ||
(control_point.second <= -kDeltaLimit)) { return JXL_FAILURE("Spline delta-delta is out of bounds");
}
}
constauto decode_dct = [decoder, br, &context_map](int dct[32]) -> Status {
constexpr int kWeirdNumber = std::numeric_limits<int>::min(); for (int i = 0; i < 32; ++i) {
dct[i] =
UnpackSigned(decoder->ReadHybridUint(kDCTContext, br, context_map)); if (dct[i] == kWeirdNumber) { return JXL_FAILURE("The weird number in spline DCT");
}
} returntrue;
}; for (auto& dct : color_dct_) {
JXL_RETURN_IF_ERROR(decode_dct(dct));
}
JXL_RETURN_IF_ERROR(decode_dct(sigma_dct_)); returntrue;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.