2018-03-09 04:40:12 +00:00
|
|
|
/* SPI Master example: jpeg decoder.
|
|
|
|
|
|
|
|
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, this
|
|
|
|
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
|
|
CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2019-05-22 12:21:11 +00:00
|
|
|
The image used for the effect on the LCD in the SPI master example is stored in flash
|
|
|
|
as a jpeg file. This file contains the decode_image routine, which uses the tiny JPEG
|
|
|
|
decoder library to decode this JPEG into a format that can be sent to the display.
|
2018-03-09 04:40:12 +00:00
|
|
|
|
2019-05-22 12:21:11 +00:00
|
|
|
Keep in mind that the decoder library cannot handle progressive files (will give
|
2018-03-09 04:40:12 +00:00
|
|
|
``Image decoder: jd_prepare failed (8)`` as an error) so make sure to save in the correct
|
|
|
|
format if you want to use a different image file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "decode_image.h"
|
2019-05-22 12:21:11 +00:00
|
|
|
#include "tjpgd.h"
|
2018-03-09 04:40:12 +00:00
|
|
|
#include "esp_log.h"
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
//Reference the binary-included jpeg file
|
2019-05-22 12:21:11 +00:00
|
|
|
extern const uint8_t image_jpg_start[] asm("_binary_image_jpg_start");
|
|
|
|
extern const uint8_t image_jpg_end[] asm("_binary_image_jpg_end");
|
2018-03-09 04:40:12 +00:00
|
|
|
//Define the height and width of the jpeg file. Make sure this matches the actual jpeg
|
|
|
|
//dimensions.
|
|
|
|
#define IMAGE_W 336
|
|
|
|
#define IMAGE_H 256
|
|
|
|
|
2019-05-22 12:21:11 +00:00
|
|
|
const char *TAG = "ImageDec";
|
2018-03-09 04:40:12 +00:00
|
|
|
|
|
|
|
//Data that is passed from the decoder function to the infunc/outfunc functions.
|
|
|
|
typedef struct {
|
2019-05-22 12:21:11 +00:00
|
|
|
const unsigned char *inData; //Pointer to jpeg data
|
|
|
|
uint16_t inPos; //Current position in jpeg data
|
|
|
|
uint16_t **outData; //Array of IMAGE_H pointers to arrays of IMAGE_W 16-bit pixel values
|
|
|
|
int outW; //Width of the resulting file
|
|
|
|
int outH; //Height of the resulting file
|
2018-03-09 04:40:12 +00:00
|
|
|
} JpegDev;
|
|
|
|
|
|
|
|
//Input function for jpeg decoder. Just returns bytes from the inData field of the JpegDev structure.
|
2019-05-22 12:21:11 +00:00
|
|
|
static uint16_t infunc(JDEC *decoder, uint8_t *buf, uint16_t len)
|
2018-03-09 04:40:12 +00:00
|
|
|
{
|
|
|
|
//Read bytes from input file
|
2019-05-22 12:21:11 +00:00
|
|
|
JpegDev *jd = (JpegDev *)decoder->device;
|
|
|
|
if (buf != NULL) {
|
|
|
|
memcpy(buf, jd->inData + jd->inPos, len);
|
|
|
|
}
|
|
|
|
jd->inPos += len;
|
2018-03-09 04:40:12 +00:00
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Output function. Re-encodes the RGB888 data from the decoder as big-endian RGB565 and
|
|
|
|
//stores it in the outData array of the JpegDev structure.
|
2019-05-22 12:21:11 +00:00
|
|
|
static uint16_t outfunc(JDEC *decoder, void *bitmap, JRECT *rect)
|
2018-03-09 04:40:12 +00:00
|
|
|
{
|
2019-05-22 12:21:11 +00:00
|
|
|
JpegDev *jd = (JpegDev *)decoder->device;
|
|
|
|
uint8_t *in = (uint8_t *)bitmap;
|
|
|
|
for (int y = rect->top; y <= rect->bottom; y++) {
|
|
|
|
for (int x = rect->left; x <= rect->right; x++) {
|
2018-03-09 04:40:12 +00:00
|
|
|
//We need to convert the 3 bytes in `in` to a rgb565 value.
|
2019-05-22 12:21:11 +00:00
|
|
|
uint16_t v = 0;
|
|
|
|
v |= ((in[0] >> 3) << 11);
|
|
|
|
v |= ((in[1] >> 2) << 5);
|
|
|
|
v |= ((in[2] >> 3) << 0);
|
2018-03-09 04:40:12 +00:00
|
|
|
//The LCD wants the 16-bit value in big-endian, so swap bytes
|
2019-05-22 12:21:11 +00:00
|
|
|
v = (v >> 8) | (v << 8);
|
|
|
|
jd->outData[y][x] = v;
|
|
|
|
in += 3;
|
2018-03-09 04:40:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Size of the work space for the jpeg decoder.
|
|
|
|
#define WORKSZ 3100
|
|
|
|
|
|
|
|
//Decode the embedded image into pixel lines that can be used with the rest of the logic.
|
2019-05-22 12:21:11 +00:00
|
|
|
esp_err_t decode_image(uint16_t ***pixels)
|
2018-03-09 04:40:12 +00:00
|
|
|
{
|
2019-05-22 12:21:11 +00:00
|
|
|
char *work = NULL;
|
2018-03-09 04:40:12 +00:00
|
|
|
int r;
|
|
|
|
JDEC decoder;
|
|
|
|
JpegDev jd;
|
2019-05-22 12:21:11 +00:00
|
|
|
*pixels = NULL;
|
|
|
|
esp_err_t ret = ESP_OK;
|
2018-03-09 04:40:12 +00:00
|
|
|
|
|
|
|
//Alocate pixel memory. Each line is an array of IMAGE_W 16-bit pixels; the `*pixels` array itself contains pointers to these lines.
|
2019-05-22 12:21:11 +00:00
|
|
|
*pixels = calloc(IMAGE_H, sizeof(uint16_t *));
|
|
|
|
if (*pixels == NULL) {
|
2018-03-09 04:40:12 +00:00
|
|
|
ESP_LOGE(TAG, "Error allocating memory for lines");
|
2019-05-22 12:21:11 +00:00
|
|
|
ret = ESP_ERR_NO_MEM;
|
2018-03-09 04:40:12 +00:00
|
|
|
goto err;
|
|
|
|
}
|
2019-05-22 12:21:11 +00:00
|
|
|
for (int i = 0; i < IMAGE_H; i++) {
|
|
|
|
(*pixels)[i] = malloc(IMAGE_W * sizeof(uint16_t));
|
|
|
|
if ((*pixels)[i] == NULL) {
|
2018-03-09 04:40:12 +00:00
|
|
|
ESP_LOGE(TAG, "Error allocating memory for line %d", i);
|
2019-05-22 12:21:11 +00:00
|
|
|
ret = ESP_ERR_NO_MEM;
|
2018-03-09 04:40:12 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Allocate the work space for the jpeg decoder.
|
2019-05-22 12:21:11 +00:00
|
|
|
work = calloc(WORKSZ, 1);
|
|
|
|
if (work == NULL) {
|
2018-03-09 04:40:12 +00:00
|
|
|
ESP_LOGE(TAG, "Cannot allocate workspace");
|
2019-05-22 12:21:11 +00:00
|
|
|
ret = ESP_ERR_NO_MEM;
|
2018-03-09 04:40:12 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Populate fields of the JpegDev struct.
|
2019-05-22 12:21:11 +00:00
|
|
|
jd.inData = image_jpg_start;
|
|
|
|
jd.inPos = 0;
|
|
|
|
jd.outData = *pixels;
|
|
|
|
jd.outW = IMAGE_W;
|
|
|
|
jd.outH = IMAGE_H;
|
|
|
|
|
2018-03-09 04:40:12 +00:00
|
|
|
//Prepare and decode the jpeg.
|
2019-05-22 12:21:11 +00:00
|
|
|
r = jd_prepare(&decoder, infunc, work, WORKSZ, (void *)&jd);
|
|
|
|
if (r != JDR_OK) {
|
2018-03-09 04:40:12 +00:00
|
|
|
ESP_LOGE(TAG, "Image decoder: jd_prepare failed (%d)", r);
|
2019-05-22 12:21:11 +00:00
|
|
|
ret = ESP_ERR_NOT_SUPPORTED;
|
2018-03-09 04:40:12 +00:00
|
|
|
goto err;
|
|
|
|
}
|
2019-05-22 12:21:11 +00:00
|
|
|
r = jd_decomp(&decoder, outfunc, 0);
|
|
|
|
if (r != JDR_OK && r != JDR_FMT1) {
|
2018-03-09 04:40:12 +00:00
|
|
|
ESP_LOGE(TAG, "Image decoder: jd_decode failed (%d)", r);
|
2019-05-22 12:21:11 +00:00
|
|
|
ret = ESP_ERR_NOT_SUPPORTED;
|
2018-03-09 04:40:12 +00:00
|
|
|
goto err;
|
|
|
|
}
|
2019-05-22 12:21:11 +00:00
|
|
|
|
2018-03-09 04:40:12 +00:00
|
|
|
//All done! Free the work area (as we don't need it anymore) and return victoriously.
|
|
|
|
free(work);
|
|
|
|
return ret;
|
|
|
|
err:
|
|
|
|
//Something went wrong! Exit cleanly, de-allocating everything we allocated.
|
2019-05-22 12:21:11 +00:00
|
|
|
if (*pixels != NULL) {
|
|
|
|
for (int i = 0; i < IMAGE_H; i++) {
|
2018-03-09 04:40:12 +00:00
|
|
|
free((*pixels)[i]);
|
|
|
|
}
|
|
|
|
free(*pixels);
|
|
|
|
}
|
|
|
|
free(work);
|
|
|
|
return ret;
|
|
|
|
}
|