Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/jpeg-xl/lib/extras/enc/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 20 kB image not shown  

Quelle  jpegli.cc   Sprache: C

 
// 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.

#include "lib/extras/enc/jpegli.h"

#include <jxl/cms.h>
#include <jxl/codestream_header.h>
#include <jxl/types.h>
#include <setjmp.h>

#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <hwy/aligned_allocator.h>
#include <limits>
#include <string>
#include <utility>
#include <vector>

#include "lib/extras/enc/encode.h"
#include "lib/extras/packed_image.h"
#include "lib/jpegli/common.h"
#include "lib/jpegli/encode.h"
#include "lib/jpegli/types.h"
#include "lib/jxl/base/byte_order.h"
#include "lib/jxl/base/common.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/enc_xyb.h"
#include "lib/jxl/simd_util.h"

namespace jxl {
namespace extras {

namespace {

void MyErrorExit(j_common_ptr cinfo) {
  jmp_buf* env = static_cast<jmp_buf*>(cinfo->client_data);
  (*cinfo->err->output_message)(cinfo);
  jpegli_destroy_compress(reinterpret_cast<j_compress_ptr>(cinfo));
  longjmp(*env, 1);
}

Status VerifyInput(const PackedPixelFile& ppf) {
  const JxlBasicInfo& info = ppf.info;
  JXL_RETURN_IF_ERROR(Encoder::VerifyBasicInfo(info));
  if (ppf.frames.size() != 1) {
    return JXL_FAILURE("JPEG input must have exactly one frame.");
  }
  if (info.num_color_channels != 1 && info.num_color_channels != 3) {
    return JXL_FAILURE("Invalid number of color channels %d",
                       info.num_color_channels);
  }
  const PackedImage& image = ppf.frames[0].color;
  JXL_RETURN_IF_ERROR(Encoder::VerifyImageSize(image, info));
  if (image.format.data_type == JXL_TYPE_FLOAT16) {
    return JXL_FAILURE("FLOAT16 input is not supported.");
  }
  JXL_RETURN_IF_ERROR(Encoder::VerifyBitDepth(image.format.data_type,
                                              info.bits_per_sample,
                                              info.exponent_bits_per_sample));
  if ((image.format.data_type == JXL_TYPE_UINT8 && info.bits_per_sample != 8) ||
      (image.format.data_type == JXL_TYPE_UINT16 &&
       info.bits_per_sample != 16)) {
    return JXL_FAILURE("Only full bit depth unsigned types are supported.");
  }
  return true;
}

Status GetColorEncoding(const PackedPixelFile& ppf,
                        ColorEncoding* color_encoding) {
  if (ppf.primary_color_representation == PackedPixelFile::kIccIsPrimary) {
    IccBytes icc = ppf.icc;
    JXL_RETURN_IF_ERROR(
        color_encoding->SetICC(std::move(icc), JxlGetDefaultCms()));
  } else {
    JXL_RETURN_IF_ERROR(color_encoding->FromExternal(ppf.color_encoding));
  }
  if (color_encoding->ICC().empty()) {
    return JXL_FAILURE("Invalid color encoding.");
  }
  return true;
}

bool HasICCProfile(const std::vector<uint8_t>& app_data) {
  size_t pos = 0;
  while (pos < app_data.size()) {
    if (pos + 16 > app_data.size()) return false;
    uint8_t marker = app_data[pos + 1];
    size_t marker_len = (app_data[pos + 2] << 8) + app_data[pos + 3] + 2;
    if (marker == 0xe2 && memcmp(&app_data[pos + 4], "ICC_PROFILE", 12) == 0) {
      return true;
    }
    pos += marker_len;
  }
  return false;
}

Status WriteAppData(j_compress_ptr cinfo,
                    const std::vector<uint8_t>& app_data) {
  size_t pos = 0;
  while (pos < app_data.size()) {
    if (pos + 4 > app_data.size()) {
      return JXL_FAILURE("Incomplete APP header.");
    }
    uint8_t marker = app_data[pos + 1];
    size_t marker_len = (app_data[pos + 2] << 8) + app_data[pos + 3] + 2;
    if (app_data[pos] != 0xff || marker < 0xe0 || marker > 0xef) {
      return JXL_FAILURE("Invalid APP marker %02x %02x", app_data[pos], marker);
    }
    if (marker_len <= 4) {
      return JXL_FAILURE("Invalid APP marker length.");
    }
    if (pos + marker_len > app_data.size()) {
      return JXL_FAILURE("Incomplete APP data");
    }
    jpegli_write_marker(cinfo, marker, &app_data[pos + 4], marker_len - 4);
    pos += marker_len;
  }
  return true;
}

constexpr int kICCMarker = 0xe2;
constexpr unsigned char kICCSignature[12] = {
    0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00};
constexpr uint8_t kUnknownTf = 2;
constexpr unsigned char kCICPTagSignature[4] = {0x63, 0x69, 0x63, 0x70};
constexpr size_t kCICPTagSize = 12;

bool FindCICPTag(const uint8_t* icc_data, size_t len, bool is_first_chunk,
                 size_t* cicp_offset, size_t* cicp_length, uint8_t* cicp_tag,
                 size_t* cicp_pos) {
  if (is_first_chunk) {
    // Look up the offset of the CICP tag from the first chunk of ICC data.
    if (len < 132) {
      return false;
    }
    uint32_t tag_count = LoadBE32(&icc_data[128]);
    if (len < 132 + 12 * tag_count) {
      return false;
    }
    for (uint32_t i = 0; i < tag_count; ++i) {
      if (memcmp(&icc_data[132 + 12 * i], kCICPTagSignature, 4) == 0) {
        *cicp_offset = LoadBE32(&icc_data[136 + 12 * i]);
        *cicp_length = LoadBE32(&icc_data[140 + 12 * i]);
      }
    }
    if (*cicp_length < kCICPTagSize) {
      return false;
    }
  }
  if (*cicp_offset < len) {
    size_t n_bytes = std::min(len - *cicp_offset, kCICPTagSize - *cicp_pos);
    memcpy(&cicp_tag[*cicp_pos], &icc_data[*cicp_offset], n_bytes);
    *cicp_pos += n_bytes;
    *cicp_offset = 0;
  } else {
    *cicp_offset -= len;
  }
  return true;
}

uint8_t LookupCICPTransferFunctionFromAppData(const uint8_t* app_data,
                                              size_t len) {
  size_t last_index = 0;
  size_t cicp_offset = 0;
  size_t cicp_length = 0;
  uint8_t cicp_tag[kCICPTagSize] = {};
  size_t cicp_pos = 0;
  size_t pos = 0;
  while (pos < len) {
    const uint8_t* marker = &app_data[pos];
    if (pos + 4 > len) {
      return kUnknownTf;
    }
    size_t marker_size = (marker[2] << 8) + marker[3] + 2;
    if (pos + marker_size > len) {
      return kUnknownTf;
    }
    if (marker_size < 18 || marker[0] != 0xff || marker[1] != kICCMarker ||
        memcmp(&marker[4], kICCSignature, 12) != 0) {
      pos += marker_size;
      continue;
    }
    uint8_t index = marker[16];
    uint8_t total = marker[17];
    const uint8_t* payload = marker + 18;
    const size_t payload_size = marker_size - 18;
    if (index != last_index + 1 || index > total) {
      return kUnknownTf;
    }
    if (!FindCICPTag(payload, payload_size, last_index == 0, &cicp_offset,
                     &cicp_length, &cicp_tag[0], &cicp_pos)) {
      return kUnknownTf;
    }
    if (cicp_pos == kCICPTagSize) {
      break;
    }
    ++last_index;
  }
  if (cicp_pos >= kCICPTagSize && memcmp(cicp_tag, kCICPTagSignature, 4) == 0) {
    return cicp_tag[9];
  }
  return kUnknownTf;
}

uint8_t LookupCICPTransferFunctionFromICCProfile(const uint8_t* icc_data,
                                                 size_t len) {
  size_t cicp_offset = 0;
  size_t cicp_length = 0;
  uint8_t cicp_tag[kCICPTagSize] = {};
  size_t cicp_pos = 0;
  if (!FindCICPTag(icc_data, len, true, &cicp_offset, &cicp_length,
                   &cicp_tag[0], &cicp_pos)) {
    return kUnknownTf;
  }
  if (cicp_pos >= kCICPTagSize && memcmp(cicp_tag, kCICPTagSignature, 4) == 0) {
    return cicp_tag[9];
  }
  return kUnknownTf;
}

JpegliDataType ConvertDataType(JxlDataType type) {
  switch (type) {
    case JXL_TYPE_UINT8:
      return JPEGLI_TYPE_UINT8;
    case JXL_TYPE_UINT16:
      return JPEGLI_TYPE_UINT16;
    case JXL_TYPE_FLOAT:
      return JPEGLI_TYPE_FLOAT;
    default:
      return JPEGLI_TYPE_UINT8;
  }
}

JpegliEndianness ConvertEndianness(JxlEndianness endianness) {
  switch (endianness) {
    case JXL_NATIVE_ENDIAN:
      return JPEGLI_NATIVE_ENDIAN;
    case JXL_LITTLE_ENDIAN:
      return JPEGLI_LITTLE_ENDIAN;
    case JXL_BIG_ENDIAN:
      return JPEGLI_BIG_ENDIAN;
    default:
      return JPEGLI_NATIVE_ENDIAN;
  }
}

void ToFloatRow(const uint8_t* row_in, JxlPixelFormat format, size_t xsize,
                size_t c_out, float* row_out) {
  bool is_little_endian =
      (format.endianness == JXL_LITTLE_ENDIAN ||
       (format.endianness == JXL_NATIVE_ENDIAN && IsLittleEndian()));
  static constexpr double kMul8 = 1.0 / 255.0;
  static constexpr double kMul16 = 1.0 / 65535.0;
  const size_t c_in = format.num_channels;
  if (format.data_type == JXL_TYPE_UINT8) {
    for (size_t x = 0; x < xsize; ++x) {
      for (size_t c = 0; c < c_out; ++c) {
        const size_t ix = c_in * x + c;
        row_out[c_out * x + c] = row_in[ix] * kMul8;
      }
    }
  } else if (format.data_type == JXL_TYPE_UINT16 && is_little_endian) {
    for (size_t x = 0; x < xsize; ++x) {
      for (size_t c = 0; c < c_out; ++c) {
        const size_t ix = c_in * x + c;
        row_out[c_out * x + c] = LoadLE16(&row_in[2 * ix]) * kMul16;
      }
    }
  } else if (format.data_type == JXL_TYPE_UINT16 && !is_little_endian) {
    for (size_t x = 0; x < xsize; ++x) {
      for (size_t c = 0; c < c_out; ++c) {
        const size_t ix = c_in * x + c;
        row_out[c_out * x + c] = LoadBE16(&row_in[2 * ix]) * kMul16;
      }
    }
  } else if (format.data_type == JXL_TYPE_FLOAT && is_little_endian) {
    for (size_t x = 0; x < xsize; ++x) {
      for (size_t c = 0; c < c_out; ++c) {
        const size_t ix = c_in * x + c;
        row_out[c_out * x + c] = LoadLEFloat(&row_in[4 * ix]);
      }
    }
  } else if (format.data_type == JXL_TYPE_FLOAT && !is_little_endian) {
    for (size_t x = 0; x < xsize; ++x) {
      for (size_t c = 0; c < c_out; ++c) {
        const size_t ix = c_in * x + c;
        row_out[c_out * x + c] = LoadBEFloat(&row_in[4 * ix]);
      }
    }
  }
}

Status EncodeJpegToTargetSize(const PackedPixelFile& ppf,
                              const JpegSettings& jpeg_settings,
                              size_t target_size, ThreadPool* pool,
                              std::vector<uint8_t>* output) {
  output->clear();
  size_t best_error = std::numeric_limits<size_t>::max();
  float distance0 = -1.0f;
  float distance1 = -1.0f;
  float distance = 1.0f;
  for (int step = 0; step < 15; ++step) {
    JpegSettings settings = jpeg_settings;
    settings.libjpeg_quality = 0;
    settings.distance = distance;
    settings.target_size = 0;
    std::vector<uint8_t> compressed;
    JXL_RETURN_IF_ERROR(EncodeJpeg(ppf, settings, pool, &compressed));
    size_t size = compressed.size();
    // prefer being under the target size to being over it
    size_t error = size < target_size
                       ? target_size - size
                       : static_cast<size_t>(1.2f * (size - target_size));
    if (error < best_error) {
      best_error = error;
      std::swap(*output, compressed);
    }
    float rel_error = size * 1.0f / target_size;
    if (std::abs(rel_error - 1.0f) < 0.002f) {
      break;
    }
    if (size < target_size) {
      distance1 = distance;
    } else {
      distance0 = distance;
    }
    if (distance1 == -1) {
      distance *= std::pow(rel_error, 1.5) * 1.05;
    } else if (distance0 == -1) {
      distance *= std::pow(rel_error, 1.5) * 0.95;
    } else {
      distance = 0.5 * (distance0 + distance1);
    }
  }
  return true;
}

}  // namespace

Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings,
                  ThreadPool* pool, std::vector<uint8_t>* compressed) {
  if (jpeg_settings.libjpeg_quality > 0) {
    auto encoder = Encoder::FromExtension(".jpg");
    encoder->SetOption("q", std::to_string(jpeg_settings.libjpeg_quality));
    if (!jpeg_settings.libjpeg_chroma_subsampling.empty()) {
      encoder->SetOption("chroma_subsampling",
                         jpeg_settings.libjpeg_chroma_subsampling);
    }
    EncodedImage encoded;
    JXL_RETURN_IF_ERROR(encoder->Encode(ppf, &encoded, pool));
    size_t target_size = encoded.bitstreams[0].size();
    return EncodeJpegToTargetSize(ppf, jpeg_settings, target_size, pool,
                                  compressed);
  }
  if (jpeg_settings.target_size > 0) {
    return EncodeJpegToTargetSize(ppf, jpeg_settings, jpeg_settings.target_size,
                                  pool, compressed);
  }
  JXL_RETURN_IF_ERROR(VerifyInput(ppf));

  ColorEncoding color_encoding;
  JXL_RETURN_IF_ERROR(GetColorEncoding(ppf, &color_encoding));

  ColorSpaceTransform c_transform(*JxlGetDefaultCms());
  ColorEncoding xyb_encoding;
  if (jpeg_settings.xyb) {
    if (HasICCProfile(jpeg_settings.app_data)) {
      return JXL_FAILURE("APP data ICC profile is not supported in XYB mode.");
    }
    const ColorEncoding& c_desired = ColorEncoding::LinearSRGB(false);
    JXL_RETURN_IF_ERROR(
        c_transform.Init(color_encoding, c_desired, 255.0f, ppf.info.xsize, 1));
    xyb_encoding.SetColorSpace(jxl::ColorSpace::kXYB);
    xyb_encoding.SetRenderingIntent(jxl::RenderingIntent::kPerceptual);
    JXL_RETURN_IF_ERROR(xyb_encoding.CreateICC());
  }
  const ColorEncoding& output_encoding =
      jpeg_settings.xyb ? xyb_encoding : color_encoding;

  // We need to declare all the non-trivial destructor local variables
  // before the call to setjmp().
  std::vector<uint8_t> pixels;
  unsigned char* output_buffer = nullptr;
  unsigned long output_size = 0;  // NOLINT
  std::vector<uint8_t> row_bytes;
  const size_t max_vector_size = MaxVectorSize();
  size_t rowlen = RoundUpTo(ppf.info.xsize, max_vector_size);
  hwy::AlignedFreeUniquePtr<float[]> xyb_tmp =
      hwy::AllocateAligned<float>(6 * rowlen);
  hwy::AlignedFreeUniquePtr<float[]> premul_absorb =
      hwy::AllocateAligned<float>(max_vector_size * 12);
  ComputePremulAbsorb(255.0f, premul_absorb.get());

  jpeg_compress_struct cinfo;
  const auto try_catch_block = [&]() -> bool {
    jpeg_error_mgr jerr;
    jmp_buf env;
    cinfo.err = jpegli_std_error(&jerr);
    jerr.error_exit = &MyErrorExit;
    if (setjmp(env)) {
      return false;
    }
    cinfo.client_data = static_cast<void*>(&env);
    jpegli_create_compress(&cinfo);
    jpegli_mem_dest(&cinfo, &output_buffer, &output_size);
    const JxlBasicInfo& info = ppf.info;
    cinfo.image_width = info.xsize;
    cinfo.image_height = info.ysize;
    cinfo.input_components = info.num_color_channels;
    cinfo.in_color_space =
        cinfo.input_components == 1 ? JCS_GRAYSCALE : JCS_RGB;
    if (jpeg_settings.xyb) {
      jpegli_set_xyb_mode(&cinfo);
      cinfo.input_components = 3;
      cinfo.in_color_space = JCS_RGB;
    } else if (jpeg_settings.use_std_quant_tables) {
      jpegli_use_standard_quant_tables(&cinfo);
    }
    uint8_t cicp_tf = kUnknownTf;
    if (!jpeg_settings.app_data.empty()) {
      cicp_tf = LookupCICPTransferFunctionFromAppData(
          jpeg_settings.app_data.data(), jpeg_settings.app_data.size());
    } else if (!output_encoding.IsSRGB()) {
      cicp_tf = LookupCICPTransferFunctionFromICCProfile(
          output_encoding.ICC().data(), output_encoding.ICC().size());
    }
    jpegli_set_cicp_transfer_function(&cinfo, cicp_tf);
    jpegli_set_defaults(&cinfo);
    if (!jpeg_settings.chroma_subsampling.empty()) {
      if (jpeg_settings.chroma_subsampling == "444") {
        cinfo.comp_info[0].h_samp_factor = 1;
        cinfo.comp_info[0].v_samp_factor = 1;
      } else if (jpeg_settings.chroma_subsampling == "440") {
        cinfo.comp_info[0].h_samp_factor = 1;
        cinfo.comp_info[0].v_samp_factor = 2;
      } else if (jpeg_settings.chroma_subsampling == "422") {
        cinfo.comp_info[0].h_samp_factor = 2;
        cinfo.comp_info[0].v_samp_factor = 1;
      } else if (jpeg_settings.chroma_subsampling == "420") {
        cinfo.comp_info[0].h_samp_factor = 2;
        cinfo.comp_info[0].v_samp_factor = 2;
      } else {
        return false;
      }
      for (int i = 1; i < cinfo.num_components; ++i) {
        cinfo.comp_info[i].h_samp_factor = 1;
        cinfo.comp_info[i].v_samp_factor = 1;
      }
    } else if (!jpeg_settings.xyb) {
      // Default is no chroma subsampling.
      cinfo.comp_info[0].h_samp_factor = 1;
      cinfo.comp_info[0].v_samp_factor = 1;
    }
    jpegli_enable_adaptive_quantization(
        &cinfo, TO_JXL_BOOL(jpeg_settings.use_adaptive_quantization));
    if (jpeg_settings.psnr_target > 0.0) {
      jpegli_set_psnr(&cinfo, jpeg_settings.psnr_target,
                      jpeg_settings.search_tolerance,
                      jpeg_settings.min_distance, jpeg_settings.max_distance);
    } else if (jpeg_settings.quality > 0.0) {
      float distance = jpegli_quality_to_distance(jpeg_settings.quality);
      jpegli_set_distance(&cinfo, distance, TRUE);
    } else {
      jpegli_set_distance(&cinfo, jpeg_settings.distance, TRUE);
    }
    jpegli_set_progressive_level(&cinfo, jpeg_settings.progressive_level);
    cinfo.optimize_coding = TO_JXL_BOOL(jpeg_settings.optimize_coding);
    if (!jpeg_settings.app_data.empty()) {
      // Make sure jpegli_start_compress() does not write any APP markers.
      cinfo.write_JFIF_header = JXL_FALSE;
      cinfo.write_Adobe_marker = JXL_FALSE;
    }
    const PackedImage& image = ppf.frames[0].color;
    if (jpeg_settings.xyb) {
      jpegli_set_input_format(&cinfo, JPEGLI_TYPE_FLOAT, JPEGLI_NATIVE_ENDIAN);
    } else {
      jpegli_set_input_format(&cinfo, ConvertDataType(image.format.data_type),
                              ConvertEndianness(image.format.endianness));
    }
    jpegli_start_compress(&cinfo, TRUE);
    if (!jpeg_settings.app_data.empty()) {
      JXL_RETURN_IF_ERROR(WriteAppData(&cinfo, jpeg_settings.app_data));
    }
    if ((jpeg_settings.app_data.empty() && !output_encoding.IsSRGB()) ||
        jpeg_settings.xyb) {
      jpegli_write_icc_profile(&cinfo, output_encoding.ICC().data(),
                               output_encoding.ICC().size());
    }
    const uint8_t* pixels = reinterpret_cast<const uint8_t*>(image.pixels());
    if (jpeg_settings.xyb) {
      float* src_buf = c_transform.BufSrc(0);
      float* dst_buf = c_transform.BufDst(0);
      for (size_t y = 0; y < image.ysize; ++y) {
        // convert to float
        ToFloatRow(&pixels[y * image.stride], image.format, image.xsize,
                   info.num_color_channels, src_buf);
        // convert to linear srgb
        if (!c_transform.Run(0, src_buf, dst_buf, image.xsize)) {
          return false;
        }
        // deinterleave channels
        float* row0 = &xyb_tmp[0];
        float* row1 = &xyb_tmp[rowlen];
        float* row2 = &xyb_tmp[2 * rowlen];
        for (size_t x = 0; x < image.xsize; ++x) {
          row0[x] = dst_buf[3 * x + 0];
          row1[x] = dst_buf[3 * x + 1];
          row2[x] = dst_buf[3 * x + 2];
        }
        // convert to xyb
        LinearRGBRowToXYB(row0, row1, row2, premul_absorb.get(), image.xsize);
        // scale xyb
        ScaleXYBRow(row0, row1, row2, image.xsize);
        // interleave channels
        float* row_out = &xyb_tmp[3 * rowlen];
        for (size_t x = 0; x < image.xsize; ++x) {
          row_out[3 * x + 0] = row0[x];
          row_out[3 * x + 1] = row1[x];
          row_out[3 * x + 2] = row2[x];
        }
        // feed to jpegli as native endian floats
        JSAMPROW row[] = {reinterpret_cast<uint8_t*>(row_out)};
        jpegli_write_scanlines(&cinfo, row, 1);
      }
    } else {
      row_bytes.resize(image.stride);
      if (cinfo.num_components == static_cast<int>(image.format.num_channels)) {
        for (size_t y = 0; y < info.ysize; ++y) {
          memcpy(row_bytes.data(), pixels + y * image.stride, image.stride);
          JSAMPROW row[] = {row_bytes.data()};
          jpegli_write_scanlines(&cinfo, row, 1);
        }
      } else {
        for (size_t y = 0; y < info.ysize; ++y) {
          JXL_RETURN_IF_ERROR(
              PackedImage::ValidateDataType(image.format.data_type));
          int bytes_per_channel =
              PackedImage::BitsPerChannel(image.format.data_type) / 8;
          int bytes_per_pixel = cinfo.num_components * bytes_per_channel;
          for (size_t x = 0; x < info.xsize; ++x) {
            memcpy(&row_bytes[x * bytes_per_pixel],
                   &pixels[y * image.stride + x * image.pixel_stride()],
                   bytes_per_pixel);
          }
          JSAMPROW row[] = {row_bytes.data()};
          jpegli_write_scanlines(&cinfo, row, 1);
        }
      }
    }
    jpegli_finish_compress(&cinfo);
    compressed->resize(output_size);
    std::copy_n(output_buffer, output_size, compressed->data());
    return true;
  };
  bool success = try_catch_block();
  jpegli_destroy_compress(&cinfo);
  if (output_buffer) free(output_buffer);
  return success;
}

}  // namespace extras
}  // namespace jxl

Messung V0.5
C=93 H=84 G=88

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.