645 lines
23 KiB
C
645 lines
23 KiB
C
/**
|
|
* @file lv_libjpeg_turbo.c
|
|
*
|
|
*/
|
|
|
|
/*********************
|
|
* INCLUDES
|
|
*********************/
|
|
#include "../../draw/lv_image_decoder_private.h"
|
|
#include "../../../lvgl.h"
|
|
#if LV_USE_LIBJPEG_TURBO
|
|
|
|
#include "lv_libjpeg_turbo.h"
|
|
#include <stdio.h>
|
|
#include <jpeglib.h>
|
|
#include <jpegint.h>
|
|
#include <setjmp.h>
|
|
#include "../../core/lv_global.h"
|
|
|
|
/*********************
|
|
* DEFINES
|
|
*********************/
|
|
|
|
#define DECODER_NAME "JPEG_TURBO"
|
|
|
|
#define image_cache_draw_buf_handlers &(LV_GLOBAL_DEFAULT()->image_cache_draw_buf_handlers)
|
|
|
|
#define JPEG_PIXEL_SIZE 3 /* RGB888 */
|
|
#define JPEG_SIGNATURE 0xFFD8FF
|
|
#define IS_JPEG_SIGNATURE(x) (((x) & 0x00FFFFFF) == JPEG_SIGNATURE)
|
|
#define ORIENTATION_TAG 0x112 /* Exif tag for orientation */
|
|
#define APP1_MARKER JPEG_APP0 + 1 /* APP1 Marker code https://www.media.mit.edu/pia/Research/deepview/exif.html */
|
|
#define MARKER_DATA_LIMIT 0xFFFF /* APP1 Marker limit */
|
|
|
|
/**********************
|
|
* TYPEDEFS
|
|
**********************/
|
|
/**
|
|
* according to the Exif specification(http://www.cipa.jp/std/documents/e/DC-008-Translation-2019-E.pdf)
|
|
* Relationship between image data and orientation on a display screen according to an orientation tag
|
|
*/
|
|
typedef enum {
|
|
/* Orientation = 0 is created when the image data in the Exif is not rotated */
|
|
IMAGE_CLOCKWISE_NONE = 0,
|
|
/* Orientation = 1 is created when Oth row of the coded image data stored in the Exif image file
|
|
* and the visual top of the display screen, and Oth column and visual left, will each be matched for display
|
|
*/
|
|
IMAGE_CLOCKWISE_0 = 1,
|
|
/* Orientation = 2 is equivalent to an arrangement that is reversed Orientation = 1 horizontally */
|
|
IMAGE_FLIP_HOR = 2,
|
|
/* Orientation = 3 is equivalent to an arrangement that is turned Orientation = 6 90 degrees clockwise */
|
|
IMAGE_CLOCKWISE_180 = 3,
|
|
/* Orientation = 4 is equivalent to an arrangement that is reversed Orientation = 3 horizontally */
|
|
IMAGE_FLIP_VER = 4,
|
|
/* Orientation = 5 is equivalent to an arrangement that is reversed Orientation = 6 horizontally */
|
|
IMAGE_TRANSPOSE = 5,
|
|
/* Orientation = 6 is equivalent to an arrangement that is turned Orientation = 1 90 degrees clockwise */
|
|
IMAGE_CLOCKWISE_90 = 6,
|
|
/* Orientation = 7 is equivalent to an arrangement that is reversed Orientation = 8 horizontally */
|
|
IMAGE_TRANSVERSE = 7,
|
|
/* Orientation = 8 is equivalent to an arrangement that is turned Orientation = 3 90 degrees clockwise */
|
|
IMAGE_CLOCKWISE_270 = 8,
|
|
} image_orientation_t;
|
|
|
|
typedef struct error_mgr_s {
|
|
struct jpeg_error_mgr pub;
|
|
jmp_buf jb;
|
|
} error_mgr_t;
|
|
|
|
/**********************
|
|
* STATIC PROTOTYPES
|
|
**********************/
|
|
static lv_result_t decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc, lv_image_header_t * header);
|
|
static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
|
|
static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
|
|
static void convert_size_with_orientation(image_orientation_t image_orientation, uint32_t * width, uint32_t * height);
|
|
static lv_draw_buf_t * decode_jpeg_file(const char * filename);
|
|
static bool get_jpeg_head_info(const char * filename, uint32_t * width, uint32_t * height);
|
|
static bool get_jpeg_size(uint8_t * data, uint32_t data_size, uint32_t * width, uint32_t * height);
|
|
static image_orientation_t get_jpeg_direction(uint8_t * data, uint32_t data_size);
|
|
static inline void process_buffer_orientation(image_orientation_t op, uint8_t * cur_pos, lv_image_header_t * header,
|
|
uint32_t line_index, uint8_t * buffer, uint32_t buf_stride);
|
|
static image_orientation_t jpeg_markers_reader(struct jpeg_decompress_struct * cinfo);
|
|
static void jpeg_cmyk_to_bgrx(uint8_t * cmyk_data, uint32_t pixel_count);
|
|
static void error_exit(j_common_ptr cinfo);
|
|
/**********************
|
|
* STATIC VARIABLES
|
|
**********************/
|
|
const int JPEG_EXIF = 0x45786966; /* Exif data structure tag */
|
|
const int JPEG_BIG_ENDIAN_TAG = 0x4d4d;
|
|
const int JPEG_LITTLE_ENDIAN_TAG = 0x4949;
|
|
|
|
/**********************
|
|
* MACROS
|
|
**********************/
|
|
#define TRANS_32_VALUE(big_endian, data) big_endian ? \
|
|
((*(data) << 24) | (*((data) + 1) << 16) | (*((data) + 2) << 8) | *((data) + 3)) : \
|
|
(*(data) | (*((data) + 1) << 8) | (*((data) + 2) << 16) | (*((data) + 3) << 24))
|
|
#define TRANS_16_VALUE(big_endian, data) big_endian ? \
|
|
((*(data) << 8) | *((data) + 1)) : (*(data) | (*((data) + 1) << 8))
|
|
|
|
/**********************
|
|
* GLOBAL FUNCTIONS
|
|
**********************/
|
|
|
|
/**
|
|
* Register the JPEG decoder functions in LVGL
|
|
*/
|
|
void lv_libjpeg_turbo_init(void)
|
|
{
|
|
lv_image_decoder_t * dec = lv_image_decoder_create();
|
|
lv_image_decoder_set_info_cb(dec, decoder_info);
|
|
lv_image_decoder_set_open_cb(dec, decoder_open);
|
|
lv_image_decoder_set_close_cb(dec, decoder_close);
|
|
|
|
dec->name = DECODER_NAME;
|
|
}
|
|
|
|
void lv_libjpeg_turbo_deinit(void)
|
|
{
|
|
lv_image_decoder_t * dec = NULL;
|
|
while((dec = lv_image_decoder_get_next(dec)) != NULL) {
|
|
if(dec->info_cb == decoder_info) {
|
|
lv_image_decoder_delete(dec);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/**********************
|
|
* STATIC FUNCTIONS
|
|
**********************/
|
|
|
|
/**
|
|
* Get info about a JPEG image
|
|
* @param dsc image descriptor containing the source and type of the image and other info.
|
|
* @param header store the info here
|
|
* @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't get the info
|
|
*/
|
|
static lv_result_t decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc, lv_image_header_t * header)
|
|
{
|
|
LV_UNUSED(decoder); /*Unused*/
|
|
lv_image_src_t src_type = dsc->src_type; /*Get the source type*/
|
|
|
|
/*If it's a JPEG file...*/
|
|
if(src_type == LV_IMAGE_SRC_FILE) {
|
|
const char * src = dsc->src;
|
|
uint32_t jpg_signature = 0;
|
|
uint32_t rn;
|
|
lv_fs_read(&dsc->file, &jpg_signature, sizeof(jpg_signature), &rn);
|
|
|
|
if(rn != sizeof(jpg_signature)) {
|
|
LV_LOG_WARN("file: %s signature len = %" LV_PRIu32 " error", src, rn);
|
|
return LV_RESULT_INVALID;
|
|
}
|
|
|
|
const char * ext = lv_fs_get_ext(src);
|
|
bool is_jpeg_ext = (lv_strcmp(ext, "jpg") == 0)
|
|
|| (lv_strcmp(ext, "jpeg") == 0);
|
|
|
|
if(!IS_JPEG_SIGNATURE(jpg_signature)) {
|
|
if(is_jpeg_ext) {
|
|
LV_LOG_WARN("file: %s signature = 0X%" LV_PRIX32 " error", src, jpg_signature);
|
|
}
|
|
return LV_RESULT_INVALID;
|
|
}
|
|
|
|
uint32_t width;
|
|
uint32_t height;
|
|
|
|
if(!get_jpeg_head_info(src, &width, &height)) {
|
|
return LV_RESULT_INVALID;
|
|
}
|
|
|
|
/*Save the data in the header*/
|
|
header->cf = LV_COLOR_FORMAT_RGB888;
|
|
header->w = width;
|
|
header->h = height;
|
|
|
|
return LV_RESULT_OK;
|
|
}
|
|
|
|
return LV_RESULT_INVALID; /*If didn't succeeded earlier then it's an error*/
|
|
}
|
|
|
|
/**
|
|
* Open a JPEG image and return the decided image
|
|
* @param decoder pointer to the decoder
|
|
* @param dsc pointer to the decoder descriptor
|
|
* @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't open the image
|
|
*/
|
|
static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
|
{
|
|
LV_UNUSED(decoder); /*Unused*/
|
|
|
|
/*If it's a JPEG file...*/
|
|
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
|
|
const char * fn = dsc->src;
|
|
lv_draw_buf_t * decoded = decode_jpeg_file(fn);
|
|
if(decoded == NULL) {
|
|
LV_LOG_WARN("decode jpeg file failed");
|
|
return LV_RESULT_INVALID;
|
|
}
|
|
|
|
dsc->decoded = decoded;
|
|
|
|
if(dsc->args.no_cache) return LV_RESULT_OK;
|
|
|
|
/*If the image cache is disabled, just return the decoded image*/
|
|
if(!lv_image_cache_is_enabled()) return LV_RESULT_OK;
|
|
|
|
/*Add the decoded image to the cache*/
|
|
lv_image_cache_data_t search_key;
|
|
search_key.src_type = dsc->src_type;
|
|
search_key.src = dsc->src;
|
|
search_key.slot.size = decoded->data_size;
|
|
|
|
lv_cache_entry_t * entry = lv_image_decoder_add_to_cache(decoder, &search_key, decoded, NULL);
|
|
|
|
if(entry == NULL) {
|
|
lv_draw_buf_destroy(decoded);
|
|
return LV_RESULT_INVALID;
|
|
}
|
|
dsc->cache_entry = entry;
|
|
return LV_RESULT_OK; /*If not returned earlier then it failed*/
|
|
}
|
|
|
|
return LV_RESULT_INVALID; /*If not returned earlier then it failed*/
|
|
}
|
|
|
|
/**
|
|
* Free the allocated resources
|
|
*/
|
|
static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
|
{
|
|
LV_UNUSED(decoder); /*Unused*/
|
|
|
|
if(dsc->args.no_cache ||
|
|
!lv_image_cache_is_enabled()) lv_draw_buf_destroy((lv_draw_buf_t *)dsc->decoded);
|
|
}
|
|
|
|
static void convert_size_with_orientation(image_orientation_t image_orientation, uint32_t * width, uint32_t * height)
|
|
{
|
|
if(image_orientation == IMAGE_CLOCKWISE_NONE || image_orientation == IMAGE_CLOCKWISE_0
|
|
|| image_orientation == IMAGE_CLOCKWISE_180 || image_orientation == IMAGE_FLIP_VER ||
|
|
image_orientation == IMAGE_FLIP_HOR) {
|
|
return;
|
|
}
|
|
|
|
uint32_t tmp = *width;
|
|
*width = *height;
|
|
*height = tmp;
|
|
}
|
|
|
|
static lv_draw_buf_t * decode_jpeg_file(const char * filename)
|
|
{
|
|
/* This struct contains the JPEG decompression parameters and pointers to
|
|
* working space (which is allocated as needed by the JPEG library).
|
|
*/
|
|
struct jpeg_decompress_struct cinfo;
|
|
/* We use our private extension JPEG error handler.
|
|
* Note that this struct must live as long as the main JPEG parameter
|
|
* struct, to avoid dangling-pointer problems.
|
|
*/
|
|
error_mgr_t jerr;
|
|
|
|
/* More stuff */
|
|
JSAMPARRAY buffer; /* Output row buffer */
|
|
|
|
uint32_t row_stride; /* physical row width in output buffer */
|
|
|
|
lv_draw_buf_t * decoded = NULL;
|
|
|
|
/* In this example we want to open the input file before doing anything else,
|
|
* so that the setjmp() error recovery below can assume the file is open.
|
|
* VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
|
|
* requires it in order to read binary files.
|
|
*/
|
|
|
|
uint32_t data_size;
|
|
uint8_t * data = lv_fs_load_with_alloc(filename, &data_size);
|
|
if(data == NULL) {
|
|
LV_LOG_WARN("can't load file %s", filename);
|
|
return NULL;
|
|
}
|
|
|
|
/* read jpeg exif orientation */
|
|
image_orientation_t image_orientation = get_jpeg_direction(data, data_size);
|
|
|
|
/* allocate and initialize JPEG decompression object */
|
|
|
|
/* We set up the normal JPEG error routines, then override error_exit. */
|
|
cinfo.err = jpeg_std_error(&jerr.pub);
|
|
jerr.pub.error_exit = error_exit;
|
|
/* Establish the setjmp return context for my_error_exit to use. */
|
|
if(setjmp(jerr.jb)) {
|
|
LV_LOG_WARN("decoding error");
|
|
if(decoded) {
|
|
lv_draw_buf_destroy(decoded);
|
|
}
|
|
|
|
/* If we get here, the JPEG code has signaled an error.
|
|
* We need to clean up the JPEG object, close the input file, and return.
|
|
*/
|
|
jpeg_destroy_decompress(&cinfo);
|
|
lv_free(data);
|
|
return NULL;
|
|
}
|
|
|
|
/* Now we can initialize the JPEG decompression object. */
|
|
jpeg_create_decompress(&cinfo);
|
|
|
|
/* specify data source (eg, a file or buffer) */
|
|
jpeg_mem_src(&cinfo, data, data_size);
|
|
|
|
/* read file parameters with jpeg_read_header() */
|
|
jpeg_read_header(&cinfo, TRUE);
|
|
|
|
/* We can ignore the return value from jpeg_read_header since
|
|
* (a) suspension is not possible with the stdio data source, and
|
|
* (b) we passed TRUE to reject a tables-only JPEG file as an error.
|
|
* See libjpeg.doc for more info.
|
|
*/
|
|
|
|
/* set parameters for decompression */
|
|
if(cinfo.jpeg_color_space == JCS_CMYK || cinfo.jpeg_color_space == JCS_YCCK) {
|
|
cinfo.out_color_space = JCS_CMYK;
|
|
}
|
|
else {
|
|
cinfo.out_color_space = JCS_EXT_BGR;
|
|
}
|
|
|
|
/* In this example, we don't need to change any of the defaults set by
|
|
* jpeg_read_header(), so we do nothing here.
|
|
*/
|
|
|
|
/* Start decompressor */
|
|
jpeg_start_decompress(&cinfo);
|
|
|
|
/* We can ignore the return value since suspension is not possible
|
|
* with the stdio data source.
|
|
*/
|
|
|
|
/* We may need to do some setup of our own at this point before reading
|
|
* the data. After jpeg_start_decompress() we have the correct scaled
|
|
* output image dimensions available, as well as the output colormap
|
|
* if we asked for color quantization.
|
|
* In this example, we need to make an output work buffer of the right size.
|
|
*/
|
|
/* JSAMPLEs per row in output buffer */
|
|
row_stride = cinfo.output_width * cinfo.output_components;
|
|
/* Make a one-row-high sample array that will go away when done with image */
|
|
buffer = (*cinfo.mem->alloc_sarray)
|
|
((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
|
|
|
|
uint32_t width = cinfo.output_width;
|
|
uint32_t height = cinfo.output_height;
|
|
convert_size_with_orientation(image_orientation, &width, &height);
|
|
|
|
lv_image_header_t image_header = { 0 };
|
|
image_header.w = width;
|
|
image_header.h = height;
|
|
image_header.stride = row_stride;
|
|
|
|
lv_color_format_t fm = LV_COLOR_FORMAT_RGB888;
|
|
if(cinfo.out_color_space == JCS_CMYK) {
|
|
fm = LV_COLOR_FORMAT_XRGB8888;
|
|
}
|
|
|
|
/* Allocate the decoded draw buffer */
|
|
decoded = lv_draw_buf_create_ex(image_cache_draw_buf_handlers, width, height, fm, LV_STRIDE_AUTO);
|
|
if(decoded != NULL) {
|
|
uint32_t line_index = 0;
|
|
/* while (scan lines remain to be read) */
|
|
/* jpeg_read_scanlines(...); */
|
|
|
|
/* Here we use the library's state variable cinfo.output_scanline as the
|
|
* loop counter, so that we don't have to keep track ourselves.
|
|
*/
|
|
while(cinfo.output_scanline < cinfo.output_height) {
|
|
/* jpeg_read_scanlines expects an array of pointers to scanlines.
|
|
* Here the array is only one element long, but you could ask for
|
|
* more than one scanline at a time if that's more convenient.
|
|
*/
|
|
jpeg_read_scanlines(&cinfo, buffer, 1);
|
|
|
|
if(cinfo.out_color_space == JCS_CMYK) {
|
|
jpeg_cmyk_to_bgrx(buffer[0], decoded->header.w);
|
|
}
|
|
|
|
/* Assume put_scanline_someplace wants a pointer and sample count. */
|
|
process_buffer_orientation(image_orientation, decoded->data, &image_header, line_index, buffer[0],
|
|
decoded->header.stride);
|
|
line_index++;
|
|
}
|
|
}
|
|
|
|
/* Finish decompression */
|
|
|
|
jpeg_finish_decompress(&cinfo);
|
|
|
|
/* We can ignore the return value since suspension is not possible
|
|
* with the stdio data source.
|
|
*/
|
|
|
|
/* Release JPEG decompression object */
|
|
|
|
/* This is an important step since it will release a good deal of memory. */
|
|
jpeg_destroy_decompress(&cinfo);
|
|
|
|
/* After finish_decompress, we can close the input file.
|
|
* Here we postpone it until after no more JPEG errors are possible,
|
|
* so as to simplify the setjmp error logic above. (Actually, I don't
|
|
* think that jpeg_destroy can do an error exit, but why assume anything...)
|
|
*/
|
|
lv_free(data);
|
|
|
|
return decoded;
|
|
}
|
|
|
|
static bool get_jpeg_head_info(const char * filename, uint32_t * width, uint32_t * height)
|
|
{
|
|
uint8_t * data = NULL;
|
|
uint32_t data_size;
|
|
data = lv_fs_load_with_alloc(filename, &data_size);
|
|
if(data == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if(!get_jpeg_size(data, data_size, width, height)) {
|
|
LV_LOG_WARN("read jpeg size failed.");
|
|
}
|
|
|
|
lv_free(data);
|
|
|
|
return JPEG_HEADER_OK;
|
|
}
|
|
|
|
static void jpeg_decompress_prepare(struct jpeg_decompress_struct * cinfo, uint8_t * data, uint32_t data_size)
|
|
{
|
|
jpeg_create_decompress(cinfo);
|
|
jpeg_mem_src(cinfo, data, data_size);
|
|
jpeg_save_markers(cinfo, APP1_MARKER, MARKER_DATA_LIMIT);
|
|
}
|
|
|
|
static bool get_jpeg_size(uint8_t * data, uint32_t data_size, uint32_t * width, uint32_t * height)
|
|
{
|
|
struct jpeg_decompress_struct cinfo;
|
|
error_mgr_t jerr;
|
|
|
|
cinfo.err = jpeg_std_error(&jerr.pub);
|
|
jerr.pub.error_exit = error_exit;
|
|
|
|
if(setjmp(jerr.jb)) {
|
|
LV_LOG_WARN("read jpeg head failed");
|
|
jpeg_destroy_decompress(&cinfo);
|
|
return false;
|
|
}
|
|
|
|
/* jpeg_create_decompress */
|
|
jpeg_decompress_prepare(&cinfo, data, data_size);
|
|
|
|
int ret = jpeg_read_header(&cinfo, TRUE);
|
|
if(ret != JPEG_HEADER_OK) {
|
|
LV_LOG_WARN("read jpeg header failed: %d", ret);
|
|
jpeg_destroy_decompress(&cinfo);
|
|
return false;
|
|
}
|
|
|
|
/* read file exif orientation */
|
|
image_orientation_t op = jpeg_markers_reader(&cinfo);
|
|
|
|
*width = cinfo.image_width;
|
|
*height = cinfo.image_height;
|
|
convert_size_with_orientation(op, width, height);
|
|
|
|
jpeg_destroy_decompress(&cinfo);
|
|
|
|
return true;
|
|
}
|
|
|
|
static image_orientation_t get_jpeg_direction(uint8_t * data, uint32_t data_size)
|
|
{
|
|
image_orientation_t res = IMAGE_CLOCKWISE_NONE;
|
|
struct jpeg_decompress_struct cinfo;
|
|
error_mgr_t jerr;
|
|
|
|
cinfo.err = jpeg_std_error(&jerr.pub);
|
|
jerr.pub.error_exit = error_exit;
|
|
|
|
if(setjmp(jerr.jb)) {
|
|
LV_LOG_WARN("read jpeg orientation failed");
|
|
jpeg_destroy_decompress(&cinfo);
|
|
return res;
|
|
}
|
|
/* jpeg_create_decompress */
|
|
jpeg_decompress_prepare(&cinfo, data, data_size);
|
|
/* read file exif orientation */
|
|
res = jpeg_markers_reader(&cinfo);
|
|
|
|
jpeg_destroy_decompress(&cinfo);
|
|
|
|
LV_LOG_INFO("read jpeg orientation data : %d", res);
|
|
|
|
return res;
|
|
}
|
|
|
|
static inline void process_buffer_orientation(image_orientation_t op, uint8_t * cur_pos, lv_image_header_t * header,
|
|
uint32_t line_index, uint8_t * buffer, uint32_t buf_stride)
|
|
{
|
|
uint32_t dst_index = 0;
|
|
switch(op) {
|
|
case IMAGE_CLOCKWISE_NONE:
|
|
case IMAGE_CLOCKWISE_0:
|
|
lv_memcpy(cur_pos + line_index * buf_stride, buffer, header->stride);
|
|
break;
|
|
case IMAGE_CLOCKWISE_90:
|
|
for(uint32_t x = 0; x < header->h; x++) {
|
|
dst_index = x * buf_stride + (header->w - line_index - 1) * JPEG_PIXEL_SIZE;
|
|
lv_memcpy(cur_pos + dst_index, buffer + x * JPEG_PIXEL_SIZE, JPEG_PIXEL_SIZE);
|
|
}
|
|
break;
|
|
case IMAGE_CLOCKWISE_180:
|
|
for(uint32_t x = 0; x < header->w; x++) {
|
|
dst_index = (header->h - line_index - 1) * buf_stride + x * JPEG_PIXEL_SIZE;
|
|
lv_memcpy(cur_pos + dst_index, buffer + (header->w - x - 1) * JPEG_PIXEL_SIZE, JPEG_PIXEL_SIZE);
|
|
}
|
|
break;
|
|
case IMAGE_CLOCKWISE_270:
|
|
for(uint32_t x = 0; x < header->h; x++) {
|
|
dst_index = (header->h - x - 1) * buf_stride + line_index * JPEG_PIXEL_SIZE;
|
|
lv_memcpy(cur_pos + dst_index, buffer + x * JPEG_PIXEL_SIZE, JPEG_PIXEL_SIZE);
|
|
}
|
|
break;
|
|
case IMAGE_FLIP_VER:
|
|
dst_index = (header->h - line_index - 1) * buf_stride;
|
|
lv_memcpy(cur_pos + dst_index, buffer, header->stride);
|
|
break;
|
|
case IMAGE_FLIP_HOR:
|
|
for(uint32_t x = 0; x < header->w; x++) {
|
|
dst_index = line_index * buf_stride + (header->w - x - 1) * JPEG_PIXEL_SIZE;
|
|
lv_memcpy(cur_pos + dst_index, buffer + x * JPEG_PIXEL_SIZE, JPEG_PIXEL_SIZE);
|
|
}
|
|
break;
|
|
case IMAGE_TRANSPOSE:
|
|
for(uint32_t x = 0; x < header->h; x++) {
|
|
dst_index = x * buf_stride + line_index * JPEG_PIXEL_SIZE;
|
|
lv_memcpy(cur_pos + dst_index, buffer + x * JPEG_PIXEL_SIZE, JPEG_PIXEL_SIZE);
|
|
}
|
|
break;
|
|
case IMAGE_TRANSVERSE:
|
|
for(uint32_t x = 0; x < header->h; x++) {
|
|
dst_index = (header->h - x - 1) * buf_stride + (header->w - line_index - 1) * JPEG_PIXEL_SIZE;
|
|
lv_memcpy(cur_pos + dst_index, buffer + x * JPEG_PIXEL_SIZE, JPEG_PIXEL_SIZE);
|
|
}
|
|
break;
|
|
default:
|
|
lv_memcpy(cur_pos + line_index * buf_stride, buffer, header->stride);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static image_orientation_t jpeg_markers_reader(struct jpeg_decompress_struct * cinfo)
|
|
{
|
|
image_orientation_t res = IMAGE_CLOCKWISE_NONE;
|
|
|
|
if(cinfo == NULL || cinfo->marker == NULL) {
|
|
return res;
|
|
}
|
|
|
|
cinfo->marker->read_markers(cinfo);
|
|
|
|
jpeg_saved_marker_ptr marker = cinfo->marker_list;
|
|
while(marker != NULL) {
|
|
if(marker->marker == APP1_MARKER) {
|
|
JOCTET FAR * app1_data = marker->data;
|
|
if(TRANS_32_VALUE(true, app1_data) == JPEG_EXIF) {
|
|
uint16_t endian_tag = TRANS_16_VALUE(true, app1_data + 4 + 2);
|
|
if(!(endian_tag == JPEG_LITTLE_ENDIAN_TAG || endian_tag == JPEG_BIG_ENDIAN_TAG)) {
|
|
return res;
|
|
}
|
|
bool is_big_endian = endian_tag == JPEG_BIG_ENDIAN_TAG;
|
|
/* first ifd offset addr : 4bytes(Exif) + 2bytes(0x00) + 2bytes(align) + 2bytes(tag mark) */
|
|
unsigned int offset = TRANS_32_VALUE(is_big_endian, app1_data + 8 + 2);
|
|
/* ifd base : 4bytes(Exif) + 2bytes(0x00) */
|
|
unsigned char * ifd = 0;
|
|
do {
|
|
/* ifd start: 4bytes(Exif) + 2bytes(0x00) + offset value(2bytes(align) + 2bytes(tag mark) + 4bytes(offset size)) */
|
|
unsigned int entry_offset = 4 + 2 + offset + 2;
|
|
if(entry_offset >= marker->data_length) {
|
|
return res;
|
|
}
|
|
ifd = app1_data + entry_offset;
|
|
unsigned short num_entries = TRANS_16_VALUE(is_big_endian, ifd - 2);
|
|
if(entry_offset + num_entries * 12 >= marker->data_length) {
|
|
return res;
|
|
}
|
|
for(int i = 0; i < num_entries; i++) {
|
|
unsigned short tag = TRANS_16_VALUE(is_big_endian, ifd);
|
|
if(tag == ORIENTATION_TAG) {
|
|
/* ifd entry: 12bytes = 2bytes(tag number) + 2bytes(kind of data) + 4bytes(number of components) + 4bytes(data)
|
|
* orientation kind(0x03) of data is unsigned short */
|
|
uint32_t dirc = TRANS_16_VALUE(is_big_endian, ifd + 2 + 2 + 4);
|
|
res = (dirc >= IMAGE_CLOCKWISE_0 && dirc <= IMAGE_CLOCKWISE_270) ? (image_orientation_t)dirc : IMAGE_CLOCKWISE_NONE;
|
|
}
|
|
ifd += 12;
|
|
}
|
|
offset = TRANS_32_VALUE(is_big_endian, ifd);
|
|
} while(offset != 0);
|
|
}
|
|
break;
|
|
}
|
|
marker = marker->next;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static void jpeg_cmyk_to_bgrx(uint8_t * cmyk_data, uint32_t pixel_count)
|
|
{
|
|
uint8_t * ptr = cmyk_data;
|
|
for(uint32_t i = 0; i < pixel_count; i++) {
|
|
uint8_t c = 255 - ptr[0];
|
|
uint8_t m = 255 - ptr[1];
|
|
uint8_t y = 255 - ptr[2];
|
|
uint8_t k = 255 - ptr[3];
|
|
|
|
uint16_t inv_k = 255 - k;
|
|
ptr[0] = (uint8_t)(((inv_k * (255 - y)) + 128) / 255);
|
|
ptr[1] = (uint8_t)(((inv_k * (255 - m)) + 128) / 255);
|
|
ptr[2] = (uint8_t)(((inv_k * (255 - c)) + 128) / 255);
|
|
ptr[3] = 255;
|
|
|
|
ptr += 4;
|
|
}
|
|
}
|
|
|
|
static void error_exit(j_common_ptr cinfo)
|
|
{
|
|
error_mgr_t * myerr = (error_mgr_t *)cinfo->err;
|
|
(*cinfo->err->output_message)(cinfo);
|
|
longjmp(myerr->jb, 1);
|
|
}
|
|
|
|
#endif /*LV_USE_LIBJPEG_TURBO*/
|