| 107 | int Image::c() { return c_; } |
| 108 | |
| 109 | Image Image::crop(int left, int upper, int right, int lower) { |
| 110 | // Validate input dimensions and ensure source image is loaded |
| 111 | MLLM_RT_ASSERT(image_ptr_ != nullptr); |
| 112 | MLLM_RT_ASSERT(right > left && lower > upper); |
| 113 | |
| 114 | const int crop_w = right - left; |
| 115 | const int crop_h = lower - upper; |
| 116 | |
| 117 | Image new_img; |
| 118 | new_img.w_ = crop_w; |
| 119 | new_img.h_ = crop_h; |
| 120 | new_img.c_ = 3; // Force RGB, consistent with Image::open |
| 121 | |
| 122 | // Allocate output buffer; stbi_image_free uses free, so malloc is compatible |
| 123 | unsigned char* output = static_cast<unsigned char*>(malloc(static_cast<size_t>(crop_w) * crop_h * new_img.c_)); |
| 124 | MLLM_RT_ASSERT(output != nullptr); |
| 125 | |
| 126 | const unsigned char* src = static_cast<const unsigned char*>(image_ptr_->ptr_); |
| 127 | |
| 128 | // PIL-style crop: pad out-of-bounds with zeros |
| 129 | for (int y = 0; y < crop_h; ++y) { |
| 130 | const int sy = upper + y; |
| 131 | for (int x = 0; x < crop_w; ++x) { |
| 132 | const int sx = left + x; |
| 133 | unsigned char* dst_px = output + (static_cast<size_t>(y) * crop_w + x) * new_img.c_; |
| 134 | if (sx >= 0 && sx < w_ && sy >= 0 && sy < h_) { |
| 135 | const unsigned char* src_px = src + (static_cast<size_t>(sy) * w_ + sx) * c_; |
| 136 | dst_px[0] = src_px[0]; |
| 137 | dst_px[1] = src_px[1]; |
| 138 | dst_px[2] = src_px[2]; |
| 139 | } else { |
| 140 | dst_px[0] = 0; |
| 141 | dst_px[1] = 0; |
| 142 | dst_px[2] = 0; |
| 143 | } |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | new_img.image_ptr_ = std::make_shared<_ImagePtr>(); |
| 148 | new_img.image_ptr_->ptr_ = output; |
| 149 | |
| 150 | return new_img; |
| 151 | } |
| 152 | |
| 153 | // Pad the image to target size with given RGB color. |
| 154 | // Semantics mirror PIL ImageOps.pad: resize to fit within target (keeping aspect ratio) |
no outgoing calls
no test coverage detected