libjpeg-turbo提供了两种API:传统的libjpeg
接口和更高效的TurboJPEG
接口。推荐使用TurboJPEG
接口(turbojpeg.h
),因其代码更简洁且性能更高。关键函数为tjCompress2
,其参数包括输入数据、宽高、像素格式、压缩质量等。
以下是实现此功能的核心步骤及代码示例:
static napi_value compress_rgb_to_jpeg(napi_env env, napi_callback_info info) {
napi_value result;
int ret; // 传出
// 传入参数
int rgbWidth, rgbHeight;
size_t argc = 4;
napi_value args[4] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
// 检查参数是否为 Uint8Array
napi_typedarray_type arrayType;
int data_size;
void *rgb_data;
napi_value arrayBuffer;
size_t byteOffset;
napi_get_value_int32(env, args[0], &data_size);
size_t length = data_size;
napi_get_typedarray_info(env, args[1], &arrayType, &length, &rgb_data, &arrayBuffer, &byteOffset);
if (arrayType != napi_uint8_array) {
napi_throw_error(env, nullptr, "Expected Uint8Array");
return nullptr;
}
napi_get_value_int32(env, args[2], &rgbWidth);
napi_get_value_int32(env, args[3], &rgbHeight);
tjhandle handle = tjInitCompress();
if (!handle) {
napi_create_int32(env, -1, &result);
return result;
}
unsigned char *jpeg_data = nullptr;
unsigned long jpeg_size = 0;
// 像素格式设为RGB(TJPF_RGB对应RGB888)
ret = tjCompress2(handle, (unsigned char *)rgb_data, rgbWidth, 0, rgbHeight, TJPF_RGB, &jpeg_data, &jpeg_size,
TJSAMP_444, 90, TJFLAG_FASTDCT);
if (ret == 0) {
// 将jpeg_data写入文件或流(MJPEG流需连续调用此函数处理每一帧)
napi_create_object(env, &result);
napi_value width, height, size, data;
napi_create_int32(env, rgbWidth, &width);
napi_create_int32(env, rgbHeight, &height);
napi_create_int32(env, jpeg_size, &size);
napi_set_named_property(env, result, "width", width);
napi_set_named_property(env, result, "height", height);
napi_set_named_property(env, result, "size", size);
saveFile("/data/storage/el2/base/haps/entry/files/test.jpg", jpeg_data, jpeg_size);
int length = jpeg_size;
void *dataTmp = malloc(length);
napi_value buffer = nullptr;
napi_create_arraybuffer(env, length, &dataTmp, &buffer);
memcpy(dataTmp, (void *)jpeg_data, length);
napi_create_typedarray(env, napi_uint8_array, length, buffer, 0, &data);
napi_set_named_property(env, result, "data", data);
}
tjFree(jpeg_data); // 释放内存
tjDestroy(handle);
return result;
}
预览页面上使用createPixelMap进行绘制
private source_opts: image.SourceOptions = {
sourceDensity: 120,
sourcePixelFormat: image.PixelMapFormat.RGBA_8888,
sourceSize: { width: 480, height: 640 }
};
private decodingOptions?: image.DecodingOptions = {
sampleSize: 1,
editable: true,
desiredSize: { width: 480, height: 640 },
desiredPixelFormat: image.PixelMapFormat.RGBA_8888,
desiredRegion: { size: { height: 640, width: 480 }, x: 0, y: 0 },
index: 0
};
async getColorImageData2(imageData: Uint8Array) {
console.debug(TAG, "getColorImageData buffer:" + imageData);
let imageSource = image.createImageSource(imageData.buffer.slice(0), this.source_opts);
console.debug(TAG, "getColorImageData imageSource:" + imageData.buffer);
console.debug(TAG, "imageSource:" + JSON.stringify(imageSource));
let num = imageSource.getFrameCount();
console.debug(TAG, "num:" + num);
//
try {
if (this.showRgbMutex.tryLock()) {
imageSource.createPixelMap(this.decodingOptions , (err: BusinessError, pixelMap: image.PixelMap) => {
if (err != undefined) {
console.error(TAG, `Failed to create pixelMap.code is ${err.code},message is ${err.message}`);
console.error(TAG, "Failed to create pixelMap.code:" + err);
console.error(TAG, "Failed to create pixelMap :" + JSON.stringify(pixelMap));
} else {
console.info('Succeeded in creating pixelMap object.');
this.canvasContext.drawImage(pixelMap, 0, 0, px2vp(480), px2vp(640));
imageSource.release();
pixelMap.release();
this.showRgbMutex.unlock();
}
});
}
} catch (e) {
console.error(TAG, "createPixelMap error" + e);
}
}