void encodeJpegIntoOutputStream( JNIEnv* env, DecodedImage& decoded_image, jobject os, int quality) { // jpeg does not support alpha channel THROW_AND_RETURN_IF( decoded_image.getPixelFormat() != PixelFormat::RGB, "Wrong pixel format for jpeg encoding"); struct jpeg_compress_struct cinfo; // set up error handling JpegErrorHandler error_handler{env}; error_handler.setCompressStruct(cinfo); if (setjmp(error_handler.setjmpBuffer)) { return; } // set up OutputStream as jpeg codec destination jpeg_create_compress(&cinfo); JpegOutputStreamWrapper os_wrapper{env, os}; cinfo.dest = &(os_wrapper.public_fields); // set up image properties cinfo.image_width = decoded_image.getWidth(); cinfo.image_height = decoded_image.getHeight(); cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, quality, TRUE); jpeg_start_compress(&cinfo, TRUE); writeMetadata(cinfo, decoded_image); // write all pixels, row by row JSAMPROW row_pointer = decoded_image.getPixelsPtr(); const int stride = decoded_image.getStride(); while (cinfo.next_scanline < cinfo.image_height) { if (jpeg_write_scanlines(&cinfo, &row_pointer, 1) != 1) { jpegSafeThrow( (j_common_ptr) &cinfo, "Could not write scanline"); } std::advance(row_pointer, stride); } jpeg_finish_compress(&cinfo); jpeg_destroy_compress(&cinfo); }
/** * initialize input stream */ static void isInitSource(j_decompress_ptr dinfo) { JpegInputStreamWrapper* src = (JpegInputStreamWrapper*) dinfo->src; JNIEnv* env = src->env; src->start = true; src->javaBuffer = env->NewByteArray(kStreamBufferSize); jpegJumpOnException((j_common_ptr) dinfo); src->buffer = (JOCTET*) (*dinfo->mem->alloc_small)( (j_common_ptr) dinfo, JPOOL_PERMANENT, kStreamBufferSize * sizeof(JOCTET)); if (src->buffer == nullptr) { jpegSafeThrow( (j_common_ptr) dinfo, "Failed to allocate memory for read buffer"); } }
/** * Initialize output stream */ static void osInitDestination(j_compress_ptr cinfo) { JpegOutputStreamWrapper* dest = (JpegOutputStreamWrapper*) cinfo->dest; JNIEnv* env = dest->env; // allocate java byte array dest->javaBuffer = env->NewByteArray(kStreamBufferSize); jpegJumpOnException((j_common_ptr) cinfo); // allocate the output buffer --- it will be released when done with image dest->buffer = (JOCTET *) (*cinfo->mem->alloc_small)( (j_common_ptr) cinfo, JPOOL_IMAGE, kStreamBufferSize * sizeof(JOCTET)); if (dest->buffer == NULL) { jpegSafeThrow( (j_common_ptr) cinfo, "Failed to allcoate memory for byte buffer."); } dest->public_fields.next_output_byte = dest->buffer; dest->public_fields.free_in_buffer = kStreamBufferSize; }