image.cc (3464B)
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/image.h" 7 8 #include <algorithm> // fill, swap 9 #include <cstddef> 10 #include <cstdint> 11 12 #include "lib/jxl/base/status.h" 13 #include "lib/jxl/cache_aligned.h" 14 #include "lib/jxl/simd_util.h" 15 16 #if defined(MEMORY_SANITIZER) 17 #include "lib/jxl/base/common.h" 18 #include "lib/jxl/sanitizers.h" 19 #endif 20 21 namespace jxl { 22 namespace detail { 23 24 namespace { 25 26 // Initializes the minimum bytes required to suppress MSAN warnings from 27 // legitimate vector loads/stores on the right border, where some lanes are 28 // uninitialized and assumed to be unused. 29 void InitializePadding(PlaneBase& plane, const size_t sizeof_t) { 30 #if defined(MEMORY_SANITIZER) 31 size_t xsize = plane.xsize(); 32 size_t ysize = plane.ysize(); 33 if (xsize == 0 || ysize == 0) return; 34 35 const size_t vec_size = MaxVectorSize(); 36 if (vec_size == 0) return; // Scalar mode: no padding needed 37 38 const size_t valid_size = xsize * sizeof_t; 39 const size_t initialize_size = RoundUpTo(valid_size, vec_size); 40 if (valid_size == initialize_size) return; 41 42 for (size_t y = 0; y < ysize; ++y) { 43 uint8_t* JXL_RESTRICT row = plane.bytes() + y * plane.bytes_per_row(); 44 #if defined(__clang__) && \ 45 ((!defined(__apple_build_version__) && __clang_major__ <= 6) || \ 46 (defined(__apple_build_version__) && \ 47 __apple_build_version__ <= 10001145)) 48 // There's a bug in MSAN in clang-6 when handling AVX2 operations. This 49 // workaround allows tests to pass on MSAN, although it is slower and 50 // prevents MSAN warnings from uninitialized images. 51 std::fill(row, msan::kSanitizerSentinelByte, initialize_size); 52 #else 53 memset(row + valid_size, msan::kSanitizerSentinelByte, 54 initialize_size - valid_size); 55 #endif // clang6 56 } 57 #endif // MEMORY_SANITIZER 58 } 59 60 } // namespace 61 62 PlaneBase::PlaneBase(const size_t xsize, const size_t ysize, 63 const size_t sizeof_t) 64 : xsize_(static_cast<uint32_t>(xsize)), 65 ysize_(static_cast<uint32_t>(ysize)), 66 orig_xsize_(static_cast<uint32_t>(xsize)), 67 orig_ysize_(static_cast<uint32_t>(ysize)), 68 bytes_per_row_(BytesPerRow(xsize_, sizeof_t)), 69 bytes_(nullptr), 70 sizeof_t_(sizeof_t) { 71 // TODO(eustas): turn to error instead of abort. 72 JXL_CHECK(xsize == xsize_); 73 JXL_CHECK(ysize == ysize_); 74 75 JXL_ASSERT(sizeof_t == 1 || sizeof_t == 2 || sizeof_t == 4 || sizeof_t == 8); 76 } 77 78 Status PlaneBase::Allocate() { 79 JXL_CHECK(!bytes_.get()); 80 81 // Dimensions can be zero, e.g. for lazily-allocated images. Only allocate 82 // if nonzero, because "zero" bytes still have padding/bookkeeping overhead. 83 if (xsize_ == 0 || ysize_ == 0) { 84 return true; 85 } 86 87 bytes_ = AllocateArray(bytes_per_row_ * ysize_); 88 if (!bytes_.get()) { 89 // TODO(eustas): use specialized OOM error code 90 return JXL_FAILURE("Failed to allocate memory for image surface"); 91 } 92 InitializePadding(*this, sizeof_t_); 93 94 return true; 95 } 96 97 void PlaneBase::Swap(PlaneBase& other) { 98 std::swap(xsize_, other.xsize_); 99 std::swap(ysize_, other.ysize_); 100 std::swap(orig_xsize_, other.orig_xsize_); 101 std::swap(orig_ysize_, other.orig_ysize_); 102 std::swap(bytes_per_row_, other.bytes_per_row_); 103 std::swap(bytes_, other.bytes_); 104 } 105 106 } // namespace detail 107 } // namespace jxl