1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include "jpegutil.h" 17 #include <memory.h> 18 #include <array> 19 #include <vector> 20 #include <cstring> 21 #include <cstdio> 22 23 #include <setjmp.h> 24 25 extern "C" { 26 #include "jpeglib.h" 27 } 28 29 using namespace std; 30 31 template <typename T> 32 void safeDelete(T& t) { 33 if (t != nullptr) { 34 delete t; 35 t = nullptr; 36 } 37 } 38 39 template <typename T> 40 void safeDeleteArray(T& t) { 41 if (t != nullptr) { 42 delete[] t; 43 t = nullptr; 44 } 45 } 46 47 jpegutil::Plane::RowIterator::RowIterator(const Plane* plane) : plane_(plane) { 48 // We must be able to supply up to 8 * 2 lines at a time to libjpeg. 49 // 8 = vertical size of blocks transformed with DCT. 50 // 2 = scaling factor for Y vs UV planes. 51 bufRowCount_ = 16; 52 53 // Rows must be padded to the next multiple of 16 54 // TODO OPTIMIZE Cb and Cr components only need to be padded to a multiple of 55 // 8. 56 rowPadding_ = (16 - (plane_->planeWidth_ % 16)) % 16; 57 bufRowStride_ = plane_->planeWidth_ + rowPadding_; 58 59 // Round up to the nearest multiple of 64 for cache alignment 60 bufRowStride_ = (bufRowStride_ + 63) & ~63; 61 62 // Allocate an extra 64 bytes to allow for cache alignment 63 size_t bufSize = bufRowStride_ * bufRowCount_ + 64; 64 65 // TODO OPTIMIZE if the underlying data has a pixel-stride of 1, and an image 66 // width which is a multiple of 16, we can avoid this allocation and simply 67 // return pointers into the underlying data in operator()(int) instead of 68 // copying the data. 69 buffer_ = unique_ptr<unsigned char[]>(new unsigned char[bufSize]); 70 71 // Find the start of the 64-byte aligned buffer we allocated. 72 size_t bufStart = reinterpret_cast<size_t>(&buffer_[0]); 73 size_t alignedBufStart = (bufStart + 63) & ~63; 74 alignedBuffer_ = reinterpret_cast<unsigned char*>(alignedBufStart); 75 76 bufCurRow_ = 0; 77 } 78 79 unsigned char* jpegutil::Plane::RowIterator::operator()(int y) { 80 unsigned char* bufCurRowPtr = alignedBuffer_ + bufRowStride_ * bufCurRow_; 81 82 unsigned char* srcPtr = &plane_->data_[y * plane_->rowStride_]; 83 unsigned char* dstPtr = bufCurRowPtr; 84 85 // Use memcpy when possible. 86 if (plane_->pixelStride_ == 1) { 87 memcpy(dstPtr, srcPtr, plane_->planeWidth_); 88 } else { 89 int pixelStride = plane_->pixelStride_; 90 91 for (int i = 0; i < plane_->planeWidth_; i++) { 92 *dstPtr = *srcPtr; 93 94 srcPtr += pixelStride; 95 dstPtr++; 96 } 97 } 98 99 // Add padding to the right side by replicating the rightmost column of 100 // (actual) image values into the padding bytes. 101 memset(&bufCurRowPtr[plane_->planeWidth_], 102 bufCurRowPtr[plane_->planeWidth_ - 1], rowPadding_); 103 104 bufCurRow_++; 105 // Wrap within ring buffer. 106 bufCurRow_ %= bufRowCount_; 107 108 return bufCurRowPtr; 109 } 110 111 jpegutil::Plane::Plane(int imgWidth, int imgHeight, int planeWidth, 112 int planeHeight, unsigned char* data, int pixelStride, 113 int rowStride) 114 : imgWidth_(imgWidth), 115 imgHeight_(imgHeight), 116 planeWidth_(planeWidth), 117 planeHeight_(planeHeight), 118 data_(data), 119 rowStride_(rowStride), 120 pixelStride_(pixelStride) {} 121 122 int jpegutil::compress(const Plane& yPlane, const Plane& cbPlane, 123 const Plane& crPlane, unsigned char* outBuf, 124 size_t outBufCapacity, std::function<void(size_t)> flush, 125 int quality) { 126 int imgWidth = yPlane.imgWidth(); 127 int imgHeight = yPlane.imgHeight(); 128 129 // libjpeg requires the use of setjmp/longjmp to recover from errors. Since 130 // this doesn't play well with RAII, we must use pointers and manually call 131 // delete. See POSIX documentation for longjmp() for details on why the 132 // volatile keyword is necessary. 133 volatile jpeg_compress_struct cinfov; 134 135 jpeg_compress_struct& cinfo = 136 *const_cast<struct jpeg_compress_struct*>(&cinfov); 137 138 JSAMPROW* volatile yArr = nullptr; 139 JSAMPROW* volatile cbArr = nullptr; 140 JSAMPROW* volatile crArr = nullptr; 141 142 Plane::RowIterator* volatile yRowGenerator = nullptr; 143 Plane::RowIterator* volatile cbRowGenerator = nullptr; 144 Plane::RowIterator* volatile crRowGenerator = nullptr; 145 146 JSAMPARRAY imgArr[3]; 147 148 // Error handling 149 150 struct my_error_mgr { 151 struct jpeg_error_mgr pub; 152 jmp_buf setjmp_buffer; 153 } err; 154 155 cinfo.err = jpeg_std_error(&err.pub); 156 157 // Default error_exit will call exit(), so override 158 // to return control via setjmp/longjmp. 159 err.pub.error_exit = [](j_common_ptr cinfo) { 160 my_error_mgr* myerr = reinterpret_cast<my_error_mgr*>(cinfo->err); 161 162 (*cinfo->err->output_message)(cinfo); 163 164 // Return control to the setjmp point (see call to setjmp()). 165 longjmp(myerr->setjmp_buffer, 1); 166 }; 167 168 cinfo.err = (struct jpeg_error_mgr*)&err; 169 170 // Set the setjmp point to return to in case of error. 171 if (setjmp(err.setjmp_buffer)) { 172 // If libjpeg hits an error, control will jump to this point (see call to 173 // longjmp()). 174 jpeg_destroy_compress(&cinfo); 175 176 safeDeleteArray(yArr); 177 safeDeleteArray(cbArr); 178 safeDeleteArray(crArr); 179 safeDelete(yRowGenerator); 180 safeDelete(cbRowGenerator); 181 safeDelete(crRowGenerator); 182 183 return -1; 184 } 185 186 // Create jpeg compression context 187 jpeg_create_compress(&cinfo); 188 189 // Stores data needed by our c-style callbacks into libjpeg 190 struct ClientData { 191 unsigned char* outBuf; 192 size_t outBufCapacity; 193 std::function<void(size_t)> flush; 194 int totalOutputBytes; 195 } clientData{outBuf, outBufCapacity, flush, 0}; 196 197 cinfo.client_data = &clientData; 198 199 // Initialize destination manager 200 jpeg_destination_mgr dest; 201 202 dest.init_destination = [](j_compress_ptr cinfo) { 203 ClientData& cdata = *reinterpret_cast<ClientData*>(cinfo->client_data); 204 205 cinfo->dest->next_output_byte = cdata.outBuf; 206 cinfo->dest->free_in_buffer = cdata.outBufCapacity; 207 }; 208 209 dest.empty_output_buffer = [](j_compress_ptr cinfo) -> boolean { 210 ClientData& cdata = *reinterpret_cast<ClientData*>(cinfo->client_data); 211 212 size_t numBytesInBuffer = cdata.outBufCapacity; 213 cdata.flush(numBytesInBuffer); 214 cdata.totalOutputBytes += numBytesInBuffer; 215 216 // Reset the buffer 217 cinfo->dest->next_output_byte = cdata.outBuf; 218 cinfo->dest->free_in_buffer = cdata.outBufCapacity; 219 220 return true; 221 }; 222 223 dest.term_destination = [](j_compress_ptr cinfo) { 224 // do nothing to terminate the output buffer 225 }; 226 227 cinfo.dest = &dest; 228 229 // Set jpeg parameters 230 cinfo.image_width = imgWidth; 231 cinfo.image_height = imgHeight; 232 cinfo.input_components = 3; 233 234 // Set defaults based on the above values 235 jpeg_set_defaults(&cinfo); 236 237 jpeg_set_quality(&cinfo, quality, true); 238 239 cinfo.dct_method = JDCT_IFAST; 240 241 cinfo.raw_data_in = true; 242 243 jpeg_set_colorspace(&cinfo, JCS_YCbCr); 244 245 cinfo.comp_info[0].h_samp_factor = 2; 246 cinfo.comp_info[0].v_samp_factor = 2; 247 cinfo.comp_info[1].h_samp_factor = 1; 248 cinfo.comp_info[1].v_samp_factor = 1; 249 cinfo.comp_info[2].h_samp_factor = 1; 250 cinfo.comp_info[2].v_samp_factor = 1; 251 252 jpeg_start_compress(&cinfo, true); 253 254 yArr = new JSAMPROW[cinfo.comp_info[0].v_samp_factor * DCTSIZE]; 255 cbArr = new JSAMPROW[cinfo.comp_info[1].v_samp_factor * DCTSIZE]; 256 crArr = new JSAMPROW[cinfo.comp_info[2].v_samp_factor * DCTSIZE]; 257 258 imgArr[0] = const_cast<JSAMPARRAY>(yArr); 259 imgArr[1] = const_cast<JSAMPARRAY>(cbArr); 260 imgArr[2] = const_cast<JSAMPARRAY>(crArr); 261 262 yRowGenerator = new Plane::RowIterator(&yPlane); 263 cbRowGenerator = new Plane::RowIterator(&cbPlane); 264 crRowGenerator = new Plane::RowIterator(&crPlane); 265 266 Plane::RowIterator& yRG = *const_cast<Plane::RowIterator*>(yRowGenerator); 267 Plane::RowIterator& cbRG = *const_cast<Plane::RowIterator*>(cbRowGenerator); 268 Plane::RowIterator& crRG = *const_cast<Plane::RowIterator*>(crRowGenerator); 269 270 for (int y = 0; y < imgHeight; y += DCTSIZE * 2) { 271 for (int row = 0; row < DCTSIZE * 2; row++) { 272 yArr[row] = yRG(y + row); 273 } 274 275 for (int row = 0; row < DCTSIZE; row++) { 276 // The y-index within the subsampled chroma planes to send to libjpeg. 277 const int chY = y / 2 + row; 278 279 if (chY < imgHeight / 2) { 280 cbArr[row] = cbRG(chY); 281 crArr[row] = crRG(chY); 282 } else { 283 // When we have run out of rows in the chroma planes to compress, send 284 // the last row as padding. 285 cbArr[row] = cbRG(imgHeight / 2 - 1); 286 crArr[row] = crRG(imgHeight / 2 - 1); 287 } 288 } 289 290 jpeg_write_raw_data(&cinfo, imgArr, DCTSIZE * 2); 291 } 292 293 jpeg_finish_compress(&cinfo); 294 295 int numBytesInBuffer = cinfo.dest->next_output_byte - outBuf; 296 297 flush(numBytesInBuffer); 298 299 clientData.totalOutputBytes += numBytesInBuffer; 300 301 safeDeleteArray(yArr); 302 safeDeleteArray(cbArr); 303 safeDeleteArray(crArr); 304 safeDelete(yRowGenerator); 305 safeDelete(cbRowGenerator); 306 safeDelete(crRowGenerator); 307 308 return clientData.totalOutputBytes; 309 } 310