// 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.
// Shuffles or interleaves bytes, for example with width 2, turns "ABCDabcd" // into "AaBbCcDd". Transposes a matrix of ceil(size / width) columns and // width rows. There are size elements, size may be < width * height, if so the // last elements of the rightmost column are missing, the missing spots are // transposed along with the filled spots, and the result has the missing // elements at the end of the bottom row. The input is the input matrix in // scanline order but with missing elements skipped (which may occur in multiple // locations), the output is the result matrix in scanline order (with // no need to skip missing elements as they are past the end of the data).
Status Shuffle(JxlMemoryManager* memory_manager, uint8_t* data, size_t size,
size_t width) {
size_t height = (size + width - 1) / width; // amount of rows of output
PaddedBytes result(memory_manager);
JXL_ASSIGN_OR_RETURN(result,
PaddedBytes::WithInitialSpace(memory_manager, size)); // i = output index, j input index
size_t s = 0;
size_t j = 0; for (size_t i = 0; i < size; i++) {
result[i] = data[j];
j += height; if (j >= size) j = ++s;
}
for (size_t i = 0; i < size; i++) {
data[i] = result[i];
} returntrue;
}
// TODO(eustas): should be 20, or even 18, once DecodeVarInt is improved; // currently DecodeVarInt does not signal the errors, and marks // 11 bytes as used even if only 10 are used (and 9 is enough for // 63-bit values).
constexpr const size_t kPreambleSize = 22; // enough for reading 2 VarInts
uint64_t DecodeVarInt(const uint8_t* input, size_t inputSize, size_t* pos) {
size_t i;
uint64_t ret = 0; for (i = 0; *pos + i < inputSize && i < 10; ++i) {
ret |= static_cast<uint64_t>(input[*pos + i] & 127)
<< static_cast<uint64_t>(7 * i); // If the next-byte flag is not set, stop if ((input[*pos + i] & 128) == 0) break;
} // TODO(user): Return a decoding error if i == 10.
*pos += i + 1; return ret;
}
} // namespace
// Mimics the beginning of UnpredictICC for quick validity check. // At least kPreambleSize bytes of data should be valid at invocation time.
Status CheckPreamble(const PaddedBytes& data, size_t enc_size) { const uint8_t* enc = data.data();
size_t size = data.size();
size_t pos = 0;
uint64_t osize = DecodeVarInt(enc, size, &pos);
JXL_RETURN_IF_ERROR(CheckIs32Bit(osize)); if (pos >= size) return JXL_FAILURE("Out of bounds");
uint64_t csize = DecodeVarInt(enc, size, &pos);
JXL_RETURN_IF_ERROR(CheckIs32Bit(csize));
JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, csize, size)); // We expect that UnpredictICC inflates input, not the other way round. if (osize + 65536 < enc_size) return JXL_FAILURE("Malformed ICC");
// NB(eustas): 64 MiB ICC should be enough for everything!? const size_t output_limit = 1 << 28; if (output_limit && osize > output_limit) { return JXL_FAILURE("Decoded ICC is too large");
} returntrue;
}
// Decodes the result of PredictICC back to a valid ICC profile.
Status UnpredictICC(const uint8_t* enc, size_t size, PaddedBytes* result) { if (!result->empty()) return JXL_FAILURE("result must be empty initially");
JxlMemoryManager* memory_manager = result->memory_manager();
size_t pos = 0; // TODO(lode): technically speaking we need to check that the entire varint // decoding never goes out of bounds, not just the first byte. This requires // a DecodeVarInt function that returns an error code. It is safe to use // DecodeVarInt with out of bounds values, it silently returns, but the // specification requires an error. Idem for all DecodeVarInt below. if (pos >= size) return JXL_FAILURE("Out of bounds");
uint64_t osize = DecodeVarInt(enc, size, &pos); // Output size
JXL_RETURN_IF_ERROR(CheckIs32Bit(osize)); if (pos >= size) return JXL_FAILURE("Out of bounds");
uint64_t csize = DecodeVarInt(enc, size, &pos); // Commands size // Every command is translated to at least on byte.
JXL_RETURN_IF_ERROR(CheckIs32Bit(csize));
size_t cpos = pos; // pos in commands stream
JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, csize, size));
size_t commands_end = cpos + csize;
pos = commands_end; // pos in data stream
// Header
PaddedBytes header{memory_manager};
JXL_RETURN_IF_ERROR(header.append(ICCInitialHeaderPrediction(osize))); for (size_t i = 0; i <= kICCHeaderSize; i++) { if (result->size() == osize) { if (cpos != commands_end) return JXL_FAILURE("Not all commands used"); if (pos != size) return JXL_FAILURE("Not all data used"); returntrue; // Valid end
} if (i == kICCHeaderSize) break; // Done
ICCPredictHeader(result->data(), result->size(), header.data(), i); if (pos >= size) return JXL_FAILURE("Out of bounds");
JXL_RETURN_IF_ERROR(result->push_back(enc[pos++] + header[i]));
} if (cpos >= commands_end) return JXL_FAILURE("Out of bounds");
// Tag list
uint64_t numtags = DecodeVarInt(enc, size, &cpos);
if (numtags != 0) {
numtags--;
JXL_RETURN_IF_ERROR(CheckIs32Bit(numtags));
JXL_RETURN_IF_ERROR(AppendUint32(numtags, result));
uint64_t prevtagstart = kICCHeaderSize + numtags * 12;
uint64_t prevtagsize = 0; for (;;) { if (result->size() > osize) return JXL_FAILURE("Invalid result size"); if (cpos > commands_end) return JXL_FAILURE("Out of bounds"); if (cpos == commands_end) break; // Valid end
uint8_t command = enc[cpos++];
uint8_t tagcode = command & 63;
Tag tag; if (tagcode == 0) { break;
} elseif (tagcode == kCommandTagUnknown) {
JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, 4, size));
tag = DecodeKeyword(enc, size, pos);
pos += 4;
} elseif (tagcode == kCommandTagTRC) {
tag = kRtrcTag;
} elseif (tagcode == kCommandTagXYZ) {
tag = kRxyzTag;
} else { if (tagcode - kCommandTagStringFirst >= kNumTagStrings) { return JXL_FAILURE("Unknown tagcode");
}
tag = *kTagStrings[tagcode - kCommandTagStringFirst];
}
JXL_RETURN_IF_ERROR(AppendKeyword(tag, result));
uint64_t tagstart;
uint64_t tagsize = prevtagsize; if (tag == kRxyzTag || tag == kGxyzTag || tag == kBxyzTag ||
tag == kKxyzTag || tag == kWtptTag || tag == kBkptTag ||
tag == kLumiTag) {
tagsize = 20;
}
if (command & kFlagBitOffset) { if (cpos >= commands_end) return JXL_FAILURE("Out of bounds");
tagstart = DecodeVarInt(enc, size, &cpos);
} else {
JXL_RETURN_IF_ERROR(CheckIs32Bit(prevtagstart));
tagstart = prevtagstart + prevtagsize;
}
JXL_RETURN_IF_ERROR(CheckIs32Bit(tagstart));
JXL_RETURN_IF_ERROR(AppendUint32(tagstart, result)); if (command & kFlagBitSize) { if (cpos >= commands_end) return JXL_FAILURE("Out of bounds");
tagsize = DecodeVarInt(enc, size, &cpos);
}
JXL_RETURN_IF_ERROR(CheckIs32Bit(tagsize));
JXL_RETURN_IF_ERROR(AppendUint32(tagsize, result));
prevtagstart = tagstart;
prevtagsize = tagsize;
int order = (flags & 12) >> 2; if (order == 3) return JXL_FAILURE("Invalid order");
uint64_t stride = width; if (flags & 16) { if (cpos >= commands_end) return JXL_FAILURE("Out of bounds");
stride = DecodeVarInt(enc, size, &cpos); if (stride < width) { return JXL_FAILURE("Invalid stride");
}
} // If stride * 4 >= result->size(), return failure. The check // "size == 0 || ((size - 1) >> 2) < stride" corresponds to // "stride * 4 >= size", but does not suffer from integer overflow. // This check is more strict than necessary but follows the specification // and the encoder should ensure this is followed. if (result->empty() || ((result->size() - 1u) >> 2u) < stride) { return JXL_FAILURE("Invalid stride");
}
if (cpos >= commands_end) return JXL_FAILURE("Out of bounds");
uint64_t num = DecodeVarInt(enc, size, &cpos); // in bytes
JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, num, size));
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.