JPEGDEC: Move to libraries, use C++, draw into PicoGraphics.

driver/sh1107
Phil Howard 2022-06-09 16:52:44 +01:00
rodzic 1a9ebb7b6e
commit 7f0fe44881
7 zmienionych plików z 378 dodań i 85 usunięć

Wyświetl plik

@ -0,0 +1,199 @@
//
// JPEG Decoder
//
// written by Larry Bank
// bitbank@pobox.com
// Arduino port started 8/2/2020
// Original JPEG code written 26+ years ago :)
// The goal of this code is to decode baseline JPEG images
// using no more than 18K of RAM (if sent directly to an LCD display)
//
// Copyright 2020 BitBank Software, Inc. All Rights Reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===========================================================================
//
#include "JPEGDEC.h"
// forward references
JPEG_STATIC int JPEGInit(JPEGIMAGE *pJPEG);
JPEG_STATIC int JPEGParseInfo(JPEGIMAGE *pPage, int bExtractThumb);
JPEG_STATIC void JPEGGetMoreData(JPEGIMAGE *pPage);
JPEG_STATIC int DecodeJPEG(JPEGIMAGE *pImage);
// Include the C code which does the actual work
#include "jpeg.inl"
void JPEGDEC::setPixelType(int iType)
{
if (iType >= 0 && iType < INVALID_PIXEL_TYPE)
_jpeg.ucPixelType = (uint8_t)iType;
else
_jpeg.iError = JPEG_INVALID_PARAMETER;
} /* setPixelType() */
void JPEGDEC::setMaxOutputSize(int iMaxMCUs)
{
if (iMaxMCUs < 1)
iMaxMCUs = 1; // don't allow invalid value
_jpeg.iMaxMCUs = iMaxMCUs;
} /* setMaxOutputSize() */
//
// Memory initialization
//
int JPEGDEC::openRAM(uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw)
{
memset(&_jpeg, 0, sizeof(JPEGIMAGE));
_jpeg.ucMemType = JPEG_MEM_RAM;
_jpeg.pfnRead = readRAM;
_jpeg.pfnSeek = seekMem;
_jpeg.pfnDraw = pfnDraw;
_jpeg.pfnOpen = NULL;
_jpeg.pfnClose = NULL;
_jpeg.JPEGFile.iSize = iDataSize;
_jpeg.JPEGFile.pData = pData;
_jpeg.iMaxMCUs = 1000; // set to an unnaturally high value to start
return JPEGInit(&_jpeg);
} /* openRAM() */
int JPEGDEC::openFLASH(uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw)
{
memset(&_jpeg, 0, sizeof(JPEGIMAGE));
_jpeg.ucMemType = JPEG_MEM_FLASH;
_jpeg.pfnRead = readFLASH;
_jpeg.pfnSeek = seekMem;
_jpeg.pfnDraw = pfnDraw;
_jpeg.pfnOpen = NULL;
_jpeg.pfnClose = NULL;
_jpeg.JPEGFile.iSize = iDataSize;
_jpeg.JPEGFile.pData = pData;
_jpeg.iMaxMCUs = 1000; // set to an unnaturally high value to start
return JPEGInit(&_jpeg);
} /* openRAM() */
int JPEGDEC::getOrientation()
{
return (int)_jpeg.ucOrientation;
} /* getOrientation() */
int JPEGDEC::getLastError()
{
return _jpeg.iError;
} /* getLastError() */
int JPEGDEC::getWidth()
{
return _jpeg.iWidth;
} /* getWidth() */
int JPEGDEC::getHeight()
{
return _jpeg.iHeight;
} /* getHeight() */
int JPEGDEC::hasThumb()
{
return (int)_jpeg.ucHasThumb;
} /* hasThumb() */
int JPEGDEC::getThumbWidth()
{
return _jpeg.iThumbWidth;
} /* getThumbWidth() */
int JPEGDEC::getThumbHeight()
{
return _jpeg.iThumbHeight;
} /* getThumbHeight() */
int JPEGDEC::getBpp()
{
return (int)_jpeg.ucBpp;
} /* getBpp() */
int JPEGDEC::getSubSample()
{
return (int)_jpeg.ucSubSample;
} /* getSubSample() */
//
// File (SD/MMC) based initialization
//
int JPEGDEC::open(const char *szFilename, JPEG_OPEN_CALLBACK *pfnOpen, JPEG_CLOSE_CALLBACK *pfnClose, JPEG_READ_CALLBACK *pfnRead, JPEG_SEEK_CALLBACK *pfnSeek, JPEG_DRAW_CALLBACK *pfnDraw)
{
memset(&_jpeg, 0, sizeof(JPEGIMAGE));
_jpeg.pfnRead = pfnRead;
_jpeg.pfnSeek = pfnSeek;
_jpeg.pfnDraw = pfnDraw;
_jpeg.pfnOpen = pfnOpen;
_jpeg.pfnClose = pfnClose;
_jpeg.iMaxMCUs = 1000; // set to an unnaturally high value to start
_jpeg.JPEGFile.fHandle = (*pfnOpen)(szFilename, &_jpeg.JPEGFile.iSize);
if (_jpeg.JPEGFile.fHandle == NULL)
return 0;
return JPEGInit(&_jpeg);
} /* open() */
#ifdef FS_H
static int32_t FileRead(JPEGFILE *handle, uint8_t *buffer, int32_t length)
{
return ((File *)(handle->fHandle))->read(buffer, length);
}
static int32_t FileSeek(JPEGFILE *handle, int32_t position)
{
return ((File *)(handle->fHandle))->seek(position);
}
static void FileClose(void *handle)
{
((File *)handle)->close();
}
int JPEGDEC::open(File &file, JPEG_DRAW_CALLBACK *pfnDraw)
{
if (!file) return 0;
memset(&_jpeg, 0, sizeof(JPEGIMAGE));
_jpeg.pfnRead = FileRead;
_jpeg.pfnSeek = FileSeek;
_jpeg.pfnClose = FileClose;
_jpeg.pfnDraw = pfnDraw;
_jpeg.iMaxMCUs = 1000;
_jpeg.JPEGFile.fHandle = &file;
_jpeg.JPEGFile.iSize = file.size();
return JPEGInit(&_jpeg);
}
#endif // FS_H
void JPEGDEC::close()
{
if (_jpeg.pfnClose)
(*_jpeg.pfnClose)(_jpeg.JPEGFile.fHandle);
} /* close() */
//
// Decode the image
// returns:
// 1 = good result
// 0 = error
//
int JPEGDEC::decode(int x, int y, int iOptions)
{
_jpeg.iXOffset = x;
_jpeg.iYOffset = y;
_jpeg.iOptions = iOptions;
return DecodeJPEG(&_jpeg);
} /* decode() */
int JPEGDEC::decodeDither(uint8_t *pDither, int iOptions)
{
_jpeg.iOptions = iOptions;
_jpeg.pDitherBuffer = pDither;
return DecodeJPEG(&_jpeg);
}

Wyświetl plik

@ -1,90 +1,9 @@
#include "py/runtime.h"
#include "py/objstr.h"
#include "JPEGDEC.h"
#include "jpegdec.h"
const mp_obj_type_t JPEG_type;
typedef struct _JPEG_obj_t {
mp_obj_base_t base;
JPEGIMAGE *jpeg;
mp_obj_t callback;
} _JPEG_obj_t;
mp_obj_t current_callback = mp_const_none;
int JPEGDraw(JPEGDRAW *pDraw) {
for(int y = 0; y < pDraw->iHeight; y++) {
for(int x = 0; x < pDraw->iWidth; x++) {
int i = y * pDraw->iWidth + x;
mp_obj_t args[] = {
mp_obj_new_int(pDraw->x + x),
mp_obj_new_int(pDraw->y + y),
mp_obj_new_int(pDraw->pPixels[i])
};
mp_call_function_n_kw(current_callback, MP_ARRAY_SIZE(args), 0, args);
}
}
return 1;
}
STATIC mp_obj_t _JPEG_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum {
ARG_callback
};
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_callback, MP_ARG_REQUIRED | MP_ARG_OBJ },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
_JPEG_obj_t *self = m_new_obj_with_finaliser(_JPEG_obj_t);
self->base.type = &JPEG_type;
self->jpeg = m_new(JPEGIMAGE, 1);
self->callback = args[ARG_callback].u_obj;
return self;
}
STATIC mp_obj_t _JPEG_del(mp_obj_t self_in) {
_JPEG_obj_t *self = MP_OBJ_TO_PTR(self_in);
m_del(JPEGIMAGE, self->jpeg, 1);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(JPEG_del_obj, _JPEG_del);
// open_RAM
STATIC mp_obj_t _JPEG_openRAM(mp_obj_t self_in, mp_obj_t buffer) {
_JPEG_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(buffer, &bufinfo, MP_BUFFER_READ);
int result = JPEG_openRAM(self->jpeg, bufinfo.buf, bufinfo.len, JPEGDraw);
JPEG_setPixelType(self->jpeg, RGB565_BIG_ENDIAN);
return result == 1 ? mp_const_true : mp_const_false;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(JPEG_openRAM_obj, _JPEG_openRAM);
// decode
STATIC mp_obj_t _JPEG_decode(mp_obj_t self_in, mp_obj_t flags) {
_JPEG_obj_t *self = MP_OBJ_TO_PTR(self_in);
int x = 0;
int y = 0;
int f = mp_obj_get_int(flags);
current_callback = self->callback;
return JPEG_decode(self->jpeg, x, y, f) == 1 ? mp_const_true : mp_const_false;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(JPEG_decode_obj, _JPEG_decode);
// get_width
STATIC mp_obj_t _JPEG_getWidth(mp_obj_t self_in) {
_JPEG_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_int(JPEG_getWidth(self->jpeg));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(JPEG_decode_obj, 1, _JPEG_decode);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(JPEG_getWidth_obj, _JPEG_getWidth);
// get_height
STATIC mp_obj_t _JPEG_getHeight(mp_obj_t self_in) {
_JPEG_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_int(JPEG_getHeight(self->jpeg));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(JPEG_getHeight_obj, _JPEG_getHeight);
// class
@ -112,10 +31,12 @@ STATIC const mp_map_elem_t JPEG_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_jpegdec) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_JPEG), (mp_obj_t)&JPEG_type },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_JPEG_globals, JPEG_globals_table);
const mp_obj_module_t JPEG_user_cmodule = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_JPEG_globals,
};
MP_REGISTER_MODULE(MP_QSTR_jpegdec, JPEG_user_cmodule, MODULE_JPEGDEC_ENABLED);

Wyświetl plik

@ -0,0 +1,161 @@
#include "libraries/jpegdec/JPEGDEC.h"
#include "micropython/modules/util.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
using namespace pimoroni;
extern "C" {
#include "jpegdec.h"
#include "micropython/modules/picographics/picographics.h"
typedef struct _ModPicoGraphics_obj_t {
mp_obj_base_t base;
PicoGraphics *graphics;
DisplayDriver *display;
void *buffer;
} ModPicoGraphics_obj_t;
typedef struct _JPEG_obj_t {
mp_obj_base_t base;
JPEGDEC *jpeg;
mp_obj_t callback;
ModPicoGraphics_obj_t *graphics;
} _JPEG_obj_t;
PicoGraphics *current_graphics = nullptr;
int JPEGDraw(JPEGDRAW *pDraw) {
// "pixel" is slow and clipped,
// guaranteeing we wont draw jpeg data out of the framebuffer..
// Can we clip beforehand and make this faster?
if(pDraw->iBpp == 4) { // TODO 4-bit pixel unpacking isn't working. What's up?
uint8_t *pixels = (uint8_t *)pDraw->pPixels;
for(int y = 0; y < pDraw->iHeight; y++) {
for(int x = 0; x < pDraw->iWidth; x++) {
int i = y * pDraw->iWidth + x;
uint8_t p = pixels[i / 2];
p >>= (i & 0b1) * 4;
p &= 0xf;
current_graphics->set_pen(p);
current_graphics->pixel({pDraw->x + x, pDraw->y + y});
}
}
} else if(pDraw->iBpp == 8) {
uint8_t *pixels = (uint8_t *)pDraw->pPixels;
for(int y = 0; y < pDraw->iHeight; y++) {
for(int x = 0; x < pDraw->iWidth; x++) {
int i = y * pDraw->iWidth + x;
current_graphics->set_pen(pixels[i] >> (current_graphics->pen_type == PicoGraphics::PEN_P4 ? 4 : 0));
current_graphics->pixel({pDraw->x + x, pDraw->y + y});
}
}
} else {
for(int y = 0; y < pDraw->iHeight; y++) {
for(int x = 0; x < pDraw->iWidth; x++) {
int i = y * pDraw->iWidth + x;
if (current_graphics->pen_type == PicoGraphics::PEN_RGB332) {
current_graphics->set_pen(PicoGraphics::rgb565_to_rgb332(pDraw->pPixels[i]));
} else {
current_graphics->set_pen(pDraw->pPixels[i]);
}
current_graphics->pixel({pDraw->x + x, pDraw->y + y});
}
}
}
return 1;
}
mp_obj_t _JPEG_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum {
ARG_picographics
};
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_picographics, MP_ARG_REQUIRED | MP_ARG_OBJ },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!MP_OBJ_IS_TYPE(args[ARG_picographics].u_obj, &ModPicoGraphics_type)) mp_raise_ValueError(MP_ERROR_TEXT("PicoGraphics Object Required"));
_JPEG_obj_t *self = m_new_obj_with_finaliser(_JPEG_obj_t);
self->base.type = &JPEG_type;
self->jpeg = m_new_class(JPEGDEC);
self->graphics = (ModPicoGraphics_obj_t *)MP_OBJ_TO_PTR(args[ARG_picographics].u_obj);
return self;
}
mp_obj_t _JPEG_del(mp_obj_t self_in) {
_JPEG_obj_t *self = MP_OBJ_TO_PTR2(self_in, _JPEG_obj_t);
(void)self;
return mp_const_none;
}
// open_RAM
mp_obj_t _JPEG_openRAM(mp_obj_t self_in, mp_obj_t buffer) {
_JPEG_obj_t *self = MP_OBJ_TO_PTR2(self_in, _JPEG_obj_t);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(buffer, &bufinfo, MP_BUFFER_READ);
int result = self->jpeg->openRAM((uint8_t *)bufinfo.buf, bufinfo.len, JPEGDraw);
if (result == 1) {
switch(self->graphics->graphics->pen_type) {
case PicoGraphics::PEN_RGB332:
case PicoGraphics::PEN_RGB565:
self->jpeg->setPixelType(RGB565_BIG_ENDIAN);
break;
case PicoGraphics::PEN_P8:
self->jpeg->setPixelType(EIGHT_BIT_GRAYSCALE);
break;
// TODO currently uses EIGHT_BIT_GREYSCALE and shifts down should this use a 4-bit dither?
case PicoGraphics::PEN_P4:
self->jpeg->setPixelType(EIGHT_BIT_GRAYSCALE);
break;
// TODO 2-bit is currently unsupported
case PicoGraphics::PEN_P2:
self->jpeg->setPixelType(TWO_BIT_DITHERED);
break;
}
}
return result == 1 ? mp_const_true : mp_const_false;
}
// decode
mp_obj_t _JPEG_decode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_x, ARG_y, ARG_scale };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x, MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_y, MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_scale, MP_ARG_INT, {.u_int = 0} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
_JPEG_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _JPEG_obj_t);
int x = args[ARG_x].u_int;
int y = args[ARG_y].u_int;
int f = args[ARG_scale].u_int;
// We need to store a pointer to the PicoGraphics surface
// since the JPEGDRAW struct has no userdata
current_graphics = self->graphics->graphics;
int result = self->jpeg->decode(x, y, f);
current_graphics = nullptr;
return result == 1 ? mp_const_true : mp_const_false;
}
// get_width
mp_obj_t _JPEG_getWidth(mp_obj_t self_in) {
_JPEG_obj_t *self = MP_OBJ_TO_PTR2(self_in, _JPEG_obj_t);
return mp_obj_new_int(self->jpeg->getWidth());
}
// get_height
mp_obj_t _JPEG_getHeight(mp_obj_t self_in) {
_JPEG_obj_t *self = MP_OBJ_TO_PTR2(self_in, _JPEG_obj_t);
return mp_obj_new_int(self->jpeg->getHeight());
}
}

Wyświetl plik

@ -0,0 +1,11 @@
#include "py/runtime.h"
#include "py/objstr.h"
extern const mp_obj_type_t JPEG_type;
extern mp_obj_t _JPEG_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args);
extern mp_obj_t _JPEG_del(mp_obj_t self_in);
extern mp_obj_t _JPEG_openRAM(mp_obj_t self_in, mp_obj_t buffer);
extern mp_obj_t _JPEG_decode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t _JPEG_getWidth(mp_obj_t self_in);
extern mp_obj_t _JPEG_getHeight(mp_obj_t self_in);

Wyświetl plik

@ -2,7 +2,8 @@ add_library(usermod_jpegdec INTERFACE)
target_sources(usermod_jpegdec INTERFACE
${CMAKE_CURRENT_LIST_DIR}/jpegdec.c
${CMAKE_CURRENT_LIST_DIR}/jpeg.c
${CMAKE_CURRENT_LIST_DIR}/jpegdec.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/jpegdec/JPEGDEC.cpp
)
target_include_directories(usermod_jpegdec INTERFACE
@ -15,4 +16,4 @@ target_compile_definitions(usermod_jpegdec INTERFACE
target_link_libraries(usermod INTERFACE usermod_jpegdec)
set_source_files_properties(${CMAKE_CURRENT_LIST_DIR}/jpeg.c PROPERTIES COMPILE_FLAGS "-Wno-error=unused-function")
set_source_files_properties(${CMAKE_CURRENT_LIST_DIR}/../../../libraries/jpegdec/JPEGDEC.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=unused-function")