tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

enc_external_image.cc (10307B)


      1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style
      4 // license that can be found in the LICENSE file.
      5 
      6 #include "lib/jxl/enc_external_image.h"
      7 
      8 #include <jxl/memory_manager.h>
      9 #include <jxl/types.h>
     10 
     11 #include <cstring>
     12 #include <utility>
     13 
     14 #include "lib/jxl/base/byte_order.h"
     15 #include "lib/jxl/base/common.h"
     16 #include "lib/jxl/base/float.h"
     17 #include "lib/jxl/base/printf_macros.h"
     18 #include "lib/jxl/base/status.h"
     19 
     20 namespace jxl {
     21 namespace {
     22 
     23 size_t JxlDataTypeBytes(JxlDataType data_type) {
     24  switch (data_type) {
     25    case JXL_TYPE_UINT8:
     26      return 1;
     27    case JXL_TYPE_UINT16:
     28      return 2;
     29    case JXL_TYPE_FLOAT16:
     30      return 2;
     31    case JXL_TYPE_FLOAT:
     32      return 4;
     33    default:
     34      return 0;
     35  }
     36 }
     37 
     38 }  // namespace
     39 
     40 Status ConvertFromExternalNoSizeCheck(const uint8_t* data, size_t xsize,
     41                                      size_t ysize, size_t stride,
     42                                      size_t bits_per_sample,
     43                                      JxlPixelFormat format, size_t c,
     44                                      ThreadPool* pool, ImageF* channel) {
     45  if (format.data_type == JXL_TYPE_UINT8) {
     46    JXL_RETURN_IF_ERROR(bits_per_sample > 0 && bits_per_sample <= 8);
     47  } else if (format.data_type == JXL_TYPE_UINT16) {
     48    JXL_RETURN_IF_ERROR(bits_per_sample > 8 && bits_per_sample <= 16);
     49  } else if (format.data_type != JXL_TYPE_FLOAT16 &&
     50             format.data_type != JXL_TYPE_FLOAT) {
     51    JXL_FAILURE("unsupported pixel format data type %d", format.data_type);
     52  }
     53 
     54  JXL_ENSURE(channel->xsize() == xsize);
     55  JXL_ENSURE(channel->ysize() == ysize);
     56 
     57  size_t bytes_per_channel = JxlDataTypeBytes(format.data_type);
     58  size_t bytes_per_pixel = format.num_channels * bytes_per_channel;
     59  size_t pixel_offset = c * bytes_per_channel;
     60  // Only for uint8/16.
     61  float scale = 1.0f / ((1ull << bits_per_sample) - 1);
     62 
     63  const bool little_endian =
     64      format.endianness == JXL_LITTLE_ENDIAN ||
     65      (format.endianness == JXL_NATIVE_ENDIAN && IsLittleEndian());
     66 
     67  const auto convert_row = [&](const uint32_t task,
     68                               size_t /*thread*/) -> Status {
     69    const size_t y = task;
     70    size_t offset = y * stride + pixel_offset;
     71    float* JXL_RESTRICT row_out = channel->Row(y);
     72    const auto save_value = [&](size_t index, float value) {
     73      row_out[index] = value;
     74    };
     75    JXL_RETURN_IF_ERROR(LoadFloatRow(data + offset, xsize, bytes_per_pixel,
     76                                     format.data_type, little_endian, scale,
     77                                     save_value));
     78    return true;
     79  };
     80  JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, static_cast<uint32_t>(ysize),
     81                                ThreadPool::NoInit, convert_row,
     82                                "ConvertExtraChannel"));
     83  return true;
     84 }
     85 
     86 Status ConvertFromExternalNoSizeCheck(const uint8_t* data, size_t xsize,
     87                                      size_t ysize, size_t stride,
     88                                      const ColorEncoding& c_current,
     89                                      size_t color_channels,
     90                                      size_t bits_per_sample,
     91                                      JxlPixelFormat format, ThreadPool* pool,
     92                                      ImageBundle* ib) {
     93  JxlMemoryManager* memory_manager = ib->memory_manager();
     94  bool has_alpha = format.num_channels == 2 || format.num_channels == 4;
     95  if (format.num_channels < color_channels) {
     96    return JXL_FAILURE("Expected %" PRIuS
     97                       " color channels, received only %u channels",
     98                       color_channels, format.num_channels);
     99  }
    100 
    101  JXL_ASSIGN_OR_RETURN(Image3F color,
    102                       Image3F::Create(memory_manager, xsize, ysize));
    103  for (size_t c = 0; c < color_channels; ++c) {
    104    JXL_RETURN_IF_ERROR(ConvertFromExternalNoSizeCheck(
    105        data, xsize, ysize, stride, bits_per_sample, format, c, pool,
    106        &color.Plane(c)));
    107  }
    108  if (color_channels == 1) {
    109    JXL_RETURN_IF_ERROR(CopyImageTo(color.Plane(0), &color.Plane(1)));
    110    JXL_RETURN_IF_ERROR(CopyImageTo(color.Plane(0), &color.Plane(2)));
    111  }
    112  JXL_RETURN_IF_ERROR(ib->SetFromImage(std::move(color), c_current));
    113 
    114  // Passing an interleaved image with an alpha channel to an image that doesn't
    115  // have alpha channel just discards the passed alpha channel.
    116  if (has_alpha && ib->HasAlpha()) {
    117    JXL_ASSIGN_OR_RETURN(ImageF alpha,
    118                         ImageF::Create(memory_manager, xsize, ysize));
    119    JXL_RETURN_IF_ERROR(ConvertFromExternalNoSizeCheck(
    120        data, xsize, ysize, stride, bits_per_sample, format,
    121        format.num_channels - 1, pool, &alpha));
    122    JXL_RETURN_IF_ERROR(ib->SetAlpha(std::move(alpha)));
    123  } else if (!has_alpha && ib->HasAlpha()) {
    124    // if alpha is not passed, but it is expected, then assume
    125    // it is all-opaque
    126    JXL_ASSIGN_OR_RETURN(ImageF alpha,
    127                         ImageF::Create(memory_manager, xsize, ysize));
    128    FillImage(1.0f, &alpha);
    129    JXL_RETURN_IF_ERROR(ib->SetAlpha(std::move(alpha)));
    130  }
    131 
    132  return true;
    133 }
    134 
    135 Status ConvertFromExternal(const uint8_t* data, size_t size, size_t xsize,
    136                           size_t ysize, size_t bits_per_sample,
    137                           JxlPixelFormat format, size_t c, ThreadPool* pool,
    138                           ImageF* channel) {
    139  size_t bytes_per_channel = JxlDataTypeBytes(format.data_type);
    140  size_t bytes_per_pixel = format.num_channels * bytes_per_channel;
    141  const size_t last_row_size = xsize * bytes_per_pixel;
    142  const size_t align = format.align;
    143  const size_t row_size =
    144      (align > 1 ? jxl::DivCeil(last_row_size, align) * align : last_row_size);
    145  const size_t bytes_to_read = row_size * (ysize - 1) + last_row_size;
    146  if (xsize == 0 || ysize == 0) return JXL_FAILURE("Empty image");
    147  if (size > 0 && size < bytes_to_read) {
    148    return JXL_FAILURE("Buffer size is too small, expected: %" PRIuS
    149                       " got: %" PRIuS " (Image: %" PRIuS "x%" PRIuS
    150                       "x%u, bytes_per_channel: %" PRIuS ")",
    151                       bytes_to_read, size, xsize, ysize, format.num_channels,
    152                       bytes_per_channel);
    153  }
    154  // Too large buffer is likely an application bug, so also fail for that.
    155  // Do allow padding to stride in last row though.
    156  if (size > row_size * ysize) {
    157    return JXL_FAILURE("Buffer size is too large");
    158  }
    159  return ConvertFromExternalNoSizeCheck(
    160      data, xsize, ysize, row_size, bits_per_sample, format, c, pool, channel);
    161 }
    162 Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
    163                           size_t ysize, const ColorEncoding& c_current,
    164                           size_t color_channels, size_t bits_per_sample,
    165                           JxlPixelFormat format, ThreadPool* pool,
    166                           ImageBundle* ib) {
    167  JxlMemoryManager* memory_manager = ib->memory_manager();
    168  bool has_alpha = format.num_channels == 2 || format.num_channels == 4;
    169  if (format.num_channels < color_channels) {
    170    return JXL_FAILURE("Expected %" PRIuS
    171                       " color channels, received only %u channels",
    172                       color_channels, format.num_channels);
    173  }
    174 
    175  JXL_ASSIGN_OR_RETURN(Image3F color,
    176                       Image3F::Create(memory_manager, xsize, ysize));
    177  for (size_t c = 0; c < color_channels; ++c) {
    178    JXL_RETURN_IF_ERROR(ConvertFromExternal(bytes.data(), bytes.size(), xsize,
    179                                            ysize, bits_per_sample, format, c,
    180                                            pool, &color.Plane(c)));
    181  }
    182  if (color_channels == 1) {
    183    JXL_RETURN_IF_ERROR(CopyImageTo(color.Plane(0), &color.Plane(1)));
    184    JXL_RETURN_IF_ERROR(CopyImageTo(color.Plane(0), &color.Plane(2)));
    185  }
    186  JXL_RETURN_IF_ERROR(ib->SetFromImage(std::move(color), c_current));
    187 
    188  // Passing an interleaved image with an alpha channel to an image that doesn't
    189  // have alpha channel just discards the passed alpha channel.
    190  if (has_alpha && ib->HasAlpha()) {
    191    JXL_ASSIGN_OR_RETURN(ImageF alpha,
    192                         ImageF::Create(memory_manager, xsize, ysize));
    193    JXL_RETURN_IF_ERROR(ConvertFromExternal(
    194        bytes.data(), bytes.size(), xsize, ysize, bits_per_sample, format,
    195        format.num_channels - 1, pool, &alpha));
    196    JXL_RETURN_IF_ERROR(ib->SetAlpha(std::move(alpha)));
    197  } else if (!has_alpha && ib->HasAlpha()) {
    198    // if alpha is not passed, but it is expected, then assume
    199    // it is all-opaque
    200    JXL_ASSIGN_OR_RETURN(ImageF alpha,
    201                         ImageF::Create(memory_manager, xsize, ysize));
    202    FillImage(1.0f, &alpha);
    203    JXL_RETURN_IF_ERROR(ib->SetAlpha(std::move(alpha)));
    204  }
    205 
    206  return true;
    207 }
    208 
    209 Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
    210                           size_t ysize, const ColorEncoding& c_current,
    211                           size_t bits_per_sample, JxlPixelFormat format,
    212                           ThreadPool* pool, ImageBundle* ib) {
    213  return ConvertFromExternal(bytes, xsize, ysize, c_current,
    214                             c_current.Channels(), bits_per_sample, format,
    215                             pool, ib);
    216 }
    217 
    218 Status BufferToImageF(const JxlPixelFormat& pixel_format, size_t xsize,
    219                      size_t ysize, const void* buffer, size_t size,
    220                      ThreadPool* pool, ImageF* channel) {
    221  size_t bitdepth = JxlDataTypeBytes(pixel_format.data_type) * kBitsPerByte;
    222  return ConvertFromExternal(reinterpret_cast<const uint8_t*>(buffer), size,
    223                             xsize, ysize, bitdepth, pixel_format, 0, pool,
    224                             channel);
    225 }
    226 
    227 Status BufferToImageBundle(const JxlPixelFormat& pixel_format, uint32_t xsize,
    228                           uint32_t ysize, const void* buffer, size_t size,
    229                           jxl::ThreadPool* pool,
    230                           const jxl::ColorEncoding& c_current,
    231                           jxl::ImageBundle* ib) {
    232  size_t bitdepth = JxlDataTypeBytes(pixel_format.data_type) * kBitsPerByte;
    233  JXL_RETURN_IF_ERROR(ConvertFromExternal(
    234      jxl::Bytes(static_cast<const uint8_t*>(buffer), size), xsize, ysize,
    235      c_current, bitdepth, pixel_format, pool, ib));
    236  JXL_RETURN_IF_ERROR(ib->VerifyMetadata());
    237 
    238  return true;
    239 }
    240 
    241 }  // namespace jxl