updated JPEGDEC v1.8.3 (#23883)

This commit is contained in:
Jason2866 2025-09-07 20:16:53 +02:00 committed by GitHub
parent ede11b1795
commit 23c214bcdc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 23074 additions and 146 deletions

View File

@ -1,8 +1,11 @@
set(srcs set(srcs
"src/JPEGDEC.cpp" "src/JPEGDEC.cpp"
"src/jpeg.inl" "src/jpeg.inl"
"src/s3_simd_420.S"
"src/s3_simd_444.S"
"src/s3_simd_dequant.S"
) )
idf_component_register(SRCS ${srcs} idf_component_register(SRCS ${srcs}
REQUIRES "jpegdec" REQUIRES "jpegdec" "esp-dsp"
INCLUDE_DIRS "src" INCLUDE_DIRS "src"
) )

View File

@ -0,0 +1,284 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 77;
objects = {
/* Begin PBXCopyFilesBuildPhase section */
19B604102D5526BD00B971F2 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
19B604122D5526BD00B971F2 /* JPEGDEC_Test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = JPEGDEC_Test; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedRootGroup section */
19B604142D5526BD00B971F2 /* JPEGDEC_Test */ = {
isa = PBXFileSystemSynchronizedRootGroup;
path = JPEGDEC_Test;
sourceTree = "<group>";
};
/* End PBXFileSystemSynchronizedRootGroup section */
/* Begin PBXFrameworksBuildPhase section */
19B6040F2D5526BD00B971F2 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
19B604092D5526BD00B971F2 = {
isa = PBXGroup;
children = (
19B604142D5526BD00B971F2 /* JPEGDEC_Test */,
19B604132D5526BD00B971F2 /* Products */,
);
sourceTree = "<group>";
};
19B604132D5526BD00B971F2 /* Products */ = {
isa = PBXGroup;
children = (
19B604122D5526BD00B971F2 /* JPEGDEC_Test */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
19B604112D5526BD00B971F2 /* JPEGDEC_Test */ = {
isa = PBXNativeTarget;
buildConfigurationList = 19B604192D5526BD00B971F2 /* Build configuration list for PBXNativeTarget "JPEGDEC_Test" */;
buildPhases = (
19B6040E2D5526BD00B971F2 /* Sources */,
19B6040F2D5526BD00B971F2 /* Frameworks */,
19B604102D5526BD00B971F2 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
fileSystemSynchronizedGroups = (
19B604142D5526BD00B971F2 /* JPEGDEC_Test */,
);
name = JPEGDEC_Test;
packageProductDependencies = (
);
productName = JPEGDEC_Test;
productReference = 19B604122D5526BD00B971F2 /* JPEGDEC_Test */;
productType = "com.apple.product-type.tool";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
19B6040A2D5526BD00B971F2 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastUpgradeCheck = 1620;
TargetAttributes = {
19B604112D5526BD00B971F2 = {
CreatedOnToolsVersion = 16.2;
};
};
};
buildConfigurationList = 19B6040D2D5526BD00B971F2 /* Build configuration list for PBXProject "JPEGDEC_Test" */;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 19B604092D5526BD00B971F2;
minimizedProjectReferenceProxies = 1;
preferredProjectObjectVersion = 77;
productRefGroup = 19B604132D5526BD00B971F2 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
19B604112D5526BD00B971F2 /* JPEGDEC_Test */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
19B6040E2D5526BD00B971F2 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
19B604172D5526BD00B971F2 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 15.2;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
name = Debug;
};
19B604182D5526BD00B971F2 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 15.2;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
};
name = Release;
};
19B6041A2D5526BD00B971F2 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
19B6041B2D5526BD00B971F2 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
19B6040D2D5526BD00B971F2 /* Build configuration list for PBXProject "JPEGDEC_Test" */ = {
isa = XCConfigurationList;
buildConfigurations = (
19B604172D5526BD00B971F2 /* Debug */,
19B604182D5526BD00B971F2 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
19B604192D5526BD00B971F2 /* Build configuration list for PBXNativeTarget "JPEGDEC_Test" */ = {
isa = XCConfigurationList;
buildConfigurations = (
19B6041A2D5526BD00B971F2 /* Debug */,
19B6041B2D5526BD00B971F2 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 19B6040A2D5526BD00B971F2 /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>JPEGDEC_Test.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
</dict>
</plist>

View File

@ -0,0 +1,13 @@
CFLAGS=-D__LINUX__ -Wall -O2
LIBS =
all: jpegtest
jpegtest: main.o
$(CC) main.o $(LIBS) -o jpegtest
main.o: main.cpp
$(CXX) $(CFLAGS) -c main.cpp
clean:
rm -rf *.o jpegtest

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,305 @@
//
// main.cpp
// JPEGDEC_Test
//
// Created by Laurence Bank on 2/6/25.
//
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <time.h>
#include "../../../src/JPEGDEC.cpp"
#include "../../../test_images/tulips.h" // 640x480 56k byte test image
#include "../../../test_images/thumb_test.h" // thumbnail extraction
#include "corrupt1.h" // invalid header offsets
#include "corrupt2.h" // global buffer overflow 1
#include "corrupt3.h" // global buffer overflow 2
#include "corrupt4.h" // FPE 1
#include "corrupt5.h" // FPE 2
#define LOOP_COUNT 100
JPEGDEC jpg;
int x1, y1, x2, y2;
int iWidth, iHeight;
uint16_t *pOldPixels;
int bDMAFailed;
int iTotal = 0;
int iTotalFail = 0, iTotalPass = 0;
//
// Return the current time in microseconds
//
int Micros(void)
{
int iTime;
struct timespec res;
clock_gettime(CLOCK_MONOTONIC, &res);
iTime = (int)(1000000*res.tv_sec + res.tv_nsec/1000);
return iTime;
} /* Micros() */
//
// Simple logging print
//
void JPEGLOG(int line, char *string, const char *result)
{
printf("Line: %d: msg: %s%s\n", line, string, result);
} /* JPEGLOG() */
// Draw callback
int JPEGDraw(JPEGDRAW *pDraw)
{
if (pDraw->pPixels == pOldPixels) {
bDMAFailed = 1; // DMA option should toggle the buffer pointer with each callback
}
pOldPixels = pDraw->pPixels;
// record the max extents of the pixel positions
if (pDraw->x < x1) x1 = pDraw->x;
if (pDraw->y < y1) y1 = pDraw->y;
if (pDraw->x + pDraw->iWidthUsed -1 > x2) x2 = pDraw->x + pDraw->iWidthUsed -1;
if (pDraw->y + pDraw->iHeight-1 > y2) y2 = pDraw->y + pDraw->iHeight-1;
return 1; // continue to decode
} /* JPEGDraw() */
int main(int argc, const char * argv[]) {
int i, rc, iTime1, iTime2;
int w, h;
uint8_t *pFuzzData;
char *szTestName;
const char *szStart = " - START";
// Test 1 - Decode to the correct full image dimensions
x1 = y1 = 1000;
x2 = y2 = 0;
szTestName = (char *)"JPEG full image decode";
iTotal++;
JPEGLOG(__LINE__, szTestName, szStart);
if (jpg.openFLASH((uint8_t *)tulips, sizeof(tulips), JPEGDraw)) {
if (jpg.decode(0,0,0)) { // full sized decode
iWidth = jpg.getWidth();
iHeight = jpg.getHeight();
w = 1 + x2 - x1; h = 1 + y2 - y1;
if (iHeight == h && iWidth == w) {
JPEGLOG(__LINE__, szTestName, " - PASSED");
iTotalPass++;
} else {
iTotalFail++;
JPEGLOG(__LINE__, szTestName, " - FAILED");
if (iHeight != h) {
printf("Image Height = %d, decoded Height = %d\n", iHeight, h);
} else {
printf("Image Width = %d, decoded Width = %d\n", iWidth, w);
}
}
} else { // decode failed
iTotalFail++;
JPEGLOG(__LINE__, szTestName, " - decode failed");
}
jpg.close();
} else {
iTotalFail++;
JPEGLOG(__LINE__, szTestName, " - open failed");
}
// Test 2 - Decode to the correct cropped dimensions
szTestName = (char *)"JPEG full image decode";
iTotal++;
JPEGLOG(__LINE__, szTestName, szStart);
if (jpg.openFLASH((uint8_t *)tulips, sizeof(tulips), JPEGDraw)) {
jpg.setCropArea(50, 50, 125, 170); // purposely using coordinates which will get adjusted
jpg.getCropArea(&x1, &y1, &iWidth, &iHeight);
x1 = y1 = 1000;
x2 = y2 = 0;
if (jpg.decode(0,0,0)) { // cropped decode
w = 1 + x2 - x1; h = 1 + y2 - y1;
if (iHeight == h && iWidth == w) {
JPEGLOG(__LINE__, szTestName, " - PASSED");
iTotalPass++;
} else {
iTotalFail++;
JPEGLOG(__LINE__, szTestName, " - FAILED");
if (iHeight != h) {
printf("Image Height = %d, decoded Height = %d\n", iHeight, h);
} else {
printf("Image Width = %d, decoded Width = %d\n", iWidth, w);
}
}
} else { // decode failed
iTotalFail++;
JPEGLOG(__LINE__, szTestName, " - decode failed");
}
jpg.close();
} else {
iTotalFail++;
JPEGLOG(__LINE__, szTestName, " - open failed");
}
// Test 3 - Decode a color image as grayscale (faster)
szTestName = (char *)"JPEG color->gray image decode";
iTotal++;
JPEGLOG(__LINE__, szTestName, szStart);
iTime1 = Micros();
for (i=0; i<LOOP_COUNT; i++) { // need to do it many times to get an accurate time
jpg.openFLASH((uint8_t *)tulips, sizeof(tulips), JPEGDraw);
jpg.decode(0,0,JPEG_LUMA_ONLY); // grayscale decode
jpg.close();
}
iTime1 = Micros() - iTime1; // total decode time in microseconds
iTime2 = Micros();
for (i=0; i<LOOP_COUNT; i++) {
jpg.openFLASH((uint8_t *)tulips, sizeof(tulips), JPEGDraw);
jpg.decode(0,0,0);
jpg.close();
}
iTime2 = Micros() - iTime2;
printf("%d iterations: color decode - %d us, grayscale decode - %d us\n", LOOP_COUNT, iTime2, iTime1);
if (iTime1 <= (iTime2 * 5)/8) { // it should be at least 40% faster
JPEGLOG(__LINE__, szTestName, " - PASSED");
iTotalPass++;
} else {
iTotalFail++;
JPEGLOG(__LINE__, szTestName, " - FAILED");
}
// Test 4 - open a corrupt image without crashing
szTestName = (char *)"JPEG purposely corrupt image 1";
iTotal++;
JPEGLOG(__LINE__, szTestName, szStart);
if (jpg.openFLASH((uint8_t *)corrupt1, sizeof(corrupt1), JPEGDraw)) {
jpg.decode(0,0,0);
jpg.close();
}
JPEGLOG(__LINE__, szTestName, " - PASSED");
iTotalPass++;
// Test 5 - open a corrupt image without crashing
szTestName = (char *)"JPEG purposely corrupt image 2";
iTotal++;
JPEGLOG(__LINE__, szTestName, szStart);
if (jpg.openFLASH((uint8_t *)corrupt2, sizeof(corrupt2), JPEGDraw)) {
jpg.decode(0,0,0);
jpg.close();
}
JPEGLOG(__LINE__, szTestName, " - PASSED");
iTotalPass++;
// Test 6 - open a corrupt image without crashing
szTestName = (char *)"JPEG purposely corrupt image 3";
iTotal++;
JPEGLOG(__LINE__, szTestName, szStart);
if (jpg.openFLASH((uint8_t *)corrupt3, sizeof(corrupt3), JPEGDraw)) {
jpg.decode(0,0,0);
jpg.close();
}
JPEGLOG(__LINE__, szTestName, " - PASSED");
iTotalPass++;
// Test 7 - open a corrupt image without crashing
szTestName = (char *)"JPEG purposely corrupt image 4";
iTotal++;
JPEGLOG(__LINE__, szTestName, szStart);
if (jpg.openFLASH((uint8_t *)FPE1, sizeof(FPE1), JPEGDraw)) {
jpg.decode(0,0,0);
jpg.close();
}
JPEGLOG(__LINE__, szTestName, " - PASSED");
iTotalPass++;
// Test 8 - open a corrupt image without crashing
szTestName = (char *)"JPEG purposely corrupt image 5";
iTotal++;
JPEGLOG(__LINE__, szTestName, szStart);
if (jpg.openFLASH((uint8_t *)FPE2, sizeof(FPE2), JPEGDraw)) {
jpg.decode(0,0,0);
jpg.close();
}
JPEGLOG(__LINE__, szTestName, " - PASSED");
iTotalPass++;
// Test 9 - confirm DMA option is properly providing a ping-pong buffer
bDMAFailed = 0;
pOldPixels = NULL;
szTestName = (char *)"JPEG DMA ping-pong buffer";
iTotal++;
JPEGLOG(__LINE__, szTestName, szStart);
if (jpg.openFLASH((uint8_t *)tulips, sizeof(tulips), JPEGDraw)) {
jpg.decode(0,0,JPEG_USES_DMA);
jpg.close();
}
if (!bDMAFailed) {
JPEGLOG(__LINE__, szTestName, " - PASSED");
iTotalPass++;
} else {
iTotalFail++;
JPEGLOG(__LINE__, szTestName, " - FAILED");
}
// Test 10 - confirm correct extraction of EXIF thumbnail
szTestName = (char *)"JPEG EXIF Thumbnail";
iTotal++;
JPEGLOG(__LINE__, szTestName, szStart);
if (jpg.openFLASH((uint8_t *)thumb_test, sizeof(thumb_test), JPEGDraw)) {
if (jpg.hasThumb()) {
jpg.decode(0,0,JPEG_EXIF_THUMBNAIL);
jpg.close();
if (jpg.getWidth() != 320 || jpg.getHeight() != 240) {
iTotalFail++;
JPEGLOG(__LINE__, szTestName, " - FAILED");
printf("Thumbnail not decoded successfully\n");
} else {
iTotalPass++;
JPEGLOG(__LINE__, szTestName, " - PASSED");
}
} else {
iTotalFail++;
JPEGLOG(__LINE__, szTestName, " - FAILED");
printf("Thumbnail not detected\n");
}
} else {
iTotalFail++;
JPEGLOG(__LINE__, szTestName, " - open failed");
}
// FUZZ testing
// Randomize the input data (file header and compressed data) and confirm that the library returns an error code
// and doesn't have an invalid pointer exception
printf("Begin fuzz testing...\n");
szTestName = (char *)"Single Byte Sequential Corruption Test";
iTotal++;
pFuzzData = (uint8_t *)malloc(sizeof(tulips));
JPEGLOG(__LINE__, szTestName, szStart);
// We don't need to corrupt the file all the way to the end because it will take a loooong time
// The header is the main area where corruption can cause erratic behavior
for (i=0; i<2000; i++) { // corrupt each byte one at a time by inverting it
memcpy(pFuzzData, tulips, sizeof(tulips)); // start with the valid data
pFuzzData[i] = ~pFuzzData[i]; // invert the bits of this byte
if (jpg.openFLASH(pFuzzData, sizeof(tulips), JPEGDraw)) { // the JPEG header may be rejected
rc = jpg.decode(0,0,0);
jpg.close();
}
} // for each test
JPEGLOG(__LINE__, szTestName, " - PASSED");
iTotalPass++;
// Fuzz test part 2 - multi-byte random corruption
szTestName = (char *)"Multi-Byte Random Corruption Test";
iTotal++;
JPEGLOG(__LINE__, szTestName, szStart);
for (i=0; i<1000; i++) { // 1000 iterations of random spots in the file to corrupt with random values
int iOffset;
memcpy(pFuzzData, tulips, sizeof(tulips)); // start with the valid data
iOffset = rand() % sizeof(tulips);
pFuzzData[iOffset] = (uint8_t)rand();
iOffset = rand() % sizeof(tulips); // corrupt 2 spots just for good measure
pFuzzData[iOffset] = (uint8_t)rand();
if (jpg.openFLASH((uint8_t *)tulips, sizeof(tulips), JPEGDraw)) { // the JPEG header may be rejected
rc = jpg.decode(0,0,0);
jpg.close();
}
} // for each test
JPEGLOG(__LINE__, szTestName, " - PASSED");
iTotalPass++;
free(pFuzzData);
printf("Total tests: %d, %d passed, %d failed\n", iTotal, iTotalPass, iTotalFail);
return 0;
}

View File

@ -14,14 +14,18 @@ I started working with image and video files around 1989 and soon turned my inte
Features:<br> Features:<br>
---------<br> ---------<br>
- *New* JPEGDisplay helper class simplifies displaying images on LCDs supported by my bb_spi_lcd display library.
- Supports any MCU with at least 20K of RAM (Cortex-M0+ is the simplest I've tested) - Supports any MCU with at least 20K of RAM (Cortex-M0+ is the simplest I've tested)
- Optimized for speed; the main limitation will be how fast you can copy the pixels to the display. You can use DMA assisted SPI to help. - Optimized for speed; the main limitation will be how fast you can copy the pixels to the display. You can use DMA assisted SPI to help.
- Includes built-in cropping function that's faster than JPEGTRAN.
- JPEG image data can come from memory (FLASH/RAM), SDCard or any media you provide. - JPEG image data can come from memory (FLASH/RAM), SDCard or any media you provide.
- Simple class and callback design allows you to easily add JPEG support to any application. - Simple class and callback design allows you to easily add JPEG support to any application.
- The C code doing the heavy lifting is completely portable and has no external dependencies. - The C code doing the heavy lifting is completely portable and has no external dependencies.
- Includes fast downscaling options (1/2, 1/4, 1/8). - Includes fast downscaling options (1/2, 1/4, 1/8).
- Includes option to detect and decode the embedded Exif thumbnail - Includes option to detect and decode the embedded Exif thumbnail
- Supports Baseline Huffman images (grayscale or YCbCr)<br> - Supports Baseline Huffman images (grayscale or YCbCr)
- Supports thumbnail (DC-only) decoding of progressive JPEG images
- Now with SIMD (ESP32-S3, Arm NEON, X86 SSE2) optimized color conversion
- Includes optional Floyd-Steinberg dithering to 1, 2 or 4-bpp grayscale output; useful for e-paper displays<br> - Includes optional Floyd-Steinberg dithering to 1, 2 or 4-bpp grayscale output; useful for e-paper displays<br>
<br> <br>
@ -61,3 +65,9 @@ If you find this code useful, please consider becoming a sponsor or sending a do
[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=SR4F44J2UR8S4) [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=SR4F44J2UR8S4)
## Funding
This project received funding through [NGI Zero Core](https://nlnet.nl/core), a fund established by [NLnet](https://nlnet.nl) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) program. Learn more at the [NLnet project page](https://nlnet.nl/project/ImageCodes-Optimised).
[<img src="https://nlnet.nl/logo/banner.png" alt="NLnet foundation logo" width="20%" />](https://nlnet.nl)
[<img src="https://nlnet.nl/image/logos/NGI0_tag.svg" alt="NGI Zero Logo" width="20%" />](https://nlnet.nl/core)

View File

@ -84,9 +84,13 @@ void loop() {
Serial.println(name); Serial.println(name);
tft.print("File: "); tft.print("File: ");
tft.println(name); tft.println(name);
jpeg.open((const char *)name, myOpen, myClose, myRead, mySeek, JPEGDraw); if (jpeg.open((const char *)name, myOpen, myClose, myRead, mySeek, JPEGDraw)) {
jpeg.decode(0,0,0); jpeg.decode(0,0,0);
jpeg.close(); jpeg.close();
} else {
Serial.print("error = ");
Serial.println(jpeg.getLastError(), DEC);
}
filecount = filecount + 1; filecount = filecount + 1;
if (digitalRead(34) == LOW) { if (digitalRead(34) == LOW) {
// skip delay between images when pushbutton is pressed // skip delay between images when pushbutton is pressed

View File

@ -0,0 +1,117 @@
//
// Crop Area Example
// Written by Larry Bank (bitbank@pobox.com)
//
// This sketch demonstrates how to use the optimized crop feature of JPEGDEC
// The library supports cropping JPEG images during the decode process. The
// unused parts of the image are not decoded, so this reduces the decode time.
// The only limitation is that images must be cropped on MCU (minimum coded unit)
// boundaries. The setCropArea() function takes care of this for you if you
// provide coordinates that don't line up, they will be adjusted. The reason
// this is needed is because the nature of JPEG images is that they're composed
// of 8x8 pixel blocks. When color subsampling is enabled, these MCUs can also
// be 16x8, 8x16 or 16x16 pixels. To reduce the confusion when cropping images,
// the library will return the adjust area to know exactly what you're getting.
// The JPEGDraw() callback function will only be passed the cropped image, not
// the full image size.
//
#include <bb_spi_lcd.h>
#include "JPEGDEC.h"
#include "croptest.h"
BB_SPI_LCD lcd;
JPEGDEC jpeg;
//
// Pixel drawing callback
// called once for each set of MCUs (minimum coded units).
// JPEGDEC will try to send as many pixels as it can per call.
// In this case, it's as many as can fit in
// the internal 4K pixel buffer. This allows it to run more
// efficiently than calling this for every MCU. The blocks of pixels
// will typically be 128x8 or 128x16.
//
int drawMCUs(JPEGDRAW *pDraw)
{
int iPixelCount;
iPixelCount = pDraw->iWidth * pDraw->iHeight; // number of pixels to draw in this call
lcd.setAddrWindow(pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight);
lcd.pushPixels(pDraw->pPixels, iPixelCount, DRAW_TO_LCD);
return 1; // returning true (1) tells JPEGDEC to continue decoding. Returning false (0) would quit decoding immediately.
} /* drawMCUs() */
void setup()
{
Serial.begin(115200);
while (!Serial) {}; // CDC-Serial takes a few seconds to start
lcd.begin(DISPLAY_WS_AMOLED_18); // 1.8" Waveshare AMOLED 368x448
Serial.printf("LCD size = %dx%d\n", lcd.width(), lcd.height());
lcd.setFont(FONT_12x16);
lcd.setTextColor(TFT_GREEN, TFT_BLACK);
Serial.println("Starting...");
} /* loop() */
void loop()
{
long lTime;
char szTemp[128];
int x, y, cx, cy;
int iXOff, iYOff; // used for centering the image
lcd.fillScreen(TFT_BLACK);
lcd.println("JPEG Crop Example");
lcd.println("Full image decode:");
if (jpeg.openFLASH((uint8_t *)croptest, sizeof(croptest), drawMCUs)) {
Serial.println("Successfully opened JPEG image");
Serial.printf("Image size: %d x %d, orientation: %d, bpp %d, sub %02x\n", jpeg.getWidth(),
jpeg.getHeight(), jpeg.getOrientation(), jpeg.getBpp(), jpeg.getSubSample());
jpeg.setPixelType(RGB565_BIG_ENDIAN); // The LCD wants the 16-bit pixels in big-endian order
// center the image on the display
iXOff = (lcd.width() - jpeg.getWidth())/2;
if (iXOff < 0) iXOff = 0;
iYOff = (lcd.height() - jpeg.getHeight())/2;
if (iYOff < 0) iYOff = 0;
lTime = millis();
// Decode and draw the image on the LCD
if (jpeg.decode(iXOff,iYOff, 0)) {
lTime = millis() - lTime;
sprintf(szTemp, "Successfully decoded image in %d ms", (int)lTime);
Serial.println(szTemp);
lcd.setCursor(0, lcd.height() - 16);
lcd.printf("Decode+display: %d ms", (int)lTime);
}
jpeg.close();
} else {
Serial.println("Failed to open image");
}
delay(4000); // allow user to see the results
// Now decode a cropped image
lcd.fillScreen(TFT_BLACK);
lcd.println("JPEG Crop Example");
lcd.println("Cropped image decode:");
if (jpeg.openFLASH((uint8_t *)croptest, sizeof(croptest), drawMCUs)) {
jpeg.setPixelType(RGB565_BIG_ENDIAN); // The LCD wants the 16-bit pixels in big-endian order
jpeg.setCropArea(120, 65, 119, 110); // requested area
lcd.printf("Requested: (120,65,119,110)\n");
jpeg.getCropArea(&x, &y, &cx, &cy);
lcd.printf("Actual: (%d,%d,%d,%d)\n", x, y, cx, cy);
// Center the image on the display.
// getWidth() will always return the full size
// so we need to use the returned crop area as the new drawing size
iXOff = (lcd.width() - cx)/2;
if (iXOff < 0) iXOff = 0;
iYOff = (lcd.height() - cy)/2;
if (iYOff < 0) iYOff = 0;
lTime = millis();
// Decode and draw the image on the LCD
if (jpeg.decode(iXOff,iYOff, 0)) {
lTime = millis() - lTime;
sprintf(szTemp, "Successfully decoded image in %d ms", (int)lTime);
Serial.println(szTemp);
lcd.setCursor(0, lcd.height() - 16);
lcd.printf("Decode+display: %d ms", (int)lTime);
}
jpeg.close();
} else {
Serial.println("Failed to open image");
}
delay(10000);
} /* loop() */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,118 @@
//
// Example to demonstrate the built-in dithering features of JPEGDEC
// Dithering is available for grayscale output (1/2/4-bits per pixel)
// This example makes use of my FastEPD library for parallel e-ink displays
// The target device is the M5Stack PaperS3, but any 1-bit or 4-bit grayscale display
// can be substituted
//
#include <JPEGDEC.h>
#include <FastEPD.h>
#include "../../test_images/tulips.h" // 640x480 color images
FASTEPD epaper;
JPEGDEC jpg;
//
// JPEGDraw callback function
// called periodically with blocks of multiple MCUs of pixels
// We're requesting 1 or 4-bit pixels, so with decodeDither()
// that's the pixel format that JPEGDEC supplies
//
// 4-bpp = (bits) aaaabbbb. a = top nibble, left pixel, b = bottom nibble, right pixel
// 0 = black, 0xf (15) = white
//
// 1-bpp = (bits) abcdefgh. a = MSB (0x80), leftmost pixel, h = LSB (0x01), rightmost pixel
// 0 = black, 1 = white
//
// This example function does not take into account rotation of the display buffer
// If you want to view the image rotated from the native orientation of the display
// you'll need to write the pixels in the rotated direction.
//
int JPEGDraw(JPEGDRAW *pDraw)
{
int y, iPitch, shift;
uint8_t *s, *d, *pBuffer = epaper.currentBuffer();
if (jpg.getPixelType() == ONE_BIT_DITHERED) {
shift = 3;
} else {
shift = 1;
}
iPitch = epaper.width() >> shift; // destination pitch of the display framebuffer
for (y=0; y<pDraw->iHeight; y++) { // this is 8 or 16 depending on the color subsampling
// This is the destination pointer into the correct spot (with centering)
// in the FastEPD framebuffer
d = &pBuffer[((pDraw->y + y)*iPitch) + (pDraw->x >> shift)];
// This is the pointer to the source pixels provided by this JPEGDraw callback
// each line advances pDraw->iWidth pixels into the memory
s = (uint8_t *)pDraw->pPixels;
s += (y * (pDraw->iWidth >> shift));
// The pixel format of the display is the same as JPEGDEC, so just copy it
memcpy(d, s, pDraw->iWidth >> shift);
} // for y
return 1; // continue decoding
} /* JPEGDraw() */
void setup()
{
int rc, w, h, xoff, yoff;
uint8_t *pDither;
Serial.begin(115200);
delay(3000); // wait for CDC-Serial to start
// Initialize the ESP32-S3 I/O and allocate (PSRAM) for the framebuffer
rc = epaper.initPanel(BB_PANEL_M5PAPERS3); // 960x540 parallel eink
if (rc != BBEP_SUCCESS) { // something went wrong
Serial.println("Error initializing eink display!");
while (1) {}; // wait forever
}
epaper.fillScreen(BBEP_WHITE); // start in 1-bit mode
epaper.setFont(FONT_12x16);
epaper.setTextColor(BBEP_BLACK);
epaper.setCursor(312, 6);
epaper.print("1-bit Floyd Steinberg Dither");
if (jpg.openFLASH((uint8_t *)tulips, sizeof(tulips), JPEGDraw)) {
// 1-bit dithered can fit in the existing JPEGIMAGE structure
jpg.setPixelType(ONE_BIT_DITHERED); // request 1-bit dithered output
w = jpg.getWidth();
h = jpg.getHeight();
// We need to allocate extra memory to do dithering because it needs the full
// width of the image x 16 rows to do Floyd Steinberg's algorithm properly
pDither = (uint8_t *)malloc(w * 16);
// center the image on the display
xoff = (epaper.width() - w)/2;
yoff = (epaper.height() - h)/2;
jpg.decodeDither(xoff, yoff, pDither, 0); // decode it and pass centering offsets
jpg.close();
free(pDither);
epaper.fullUpdate(true); // display the 1-bpp dithered image
}
delay(3000); // allow time to observe it
// Now switch to 4-bpp grayscale mode and use 4-bpp dithering
epaper.setMode(BB_MODE_4BPP);
epaper.fillScreen(0xf); // 0-15 in grayscale mode, 0=black, 15 = white
epaper.setCursor(312, 6);
epaper.print("4-bit Floyd Steinberg Dither");
// Decode the same image again
if (jpg.openFLASH((uint8_t *)tulips, sizeof(tulips), JPEGDraw)) {
// 1-bit dithered can fit in the existing JPEGIMAGE structure
jpg.setPixelType(FOUR_BIT_DITHERED); // request 4-bit dithered output
w = jpg.getWidth();
h = jpg.getHeight();
// We need to allocate extra memory to do dithering because it needs the full
// width of the image x 16 rows to do Floyd Steinberg's algorithm properly
// Neighboring pixels are used to spread the color errors and if we limit it
// to the JPEG MCU blocks, visible lines will form on block boundaries
pDither = (uint8_t *)malloc(w * 16);
// center the image on the display
xoff = (epaper.width() - w)/2;
yoff = (epaper.height() - h)/2;
jpg.decodeDither(xoff, yoff, pDither, 0); // decode it and pass centering offsets
jpg.close();
free(pDither);
epaper.fullUpdate(true); // display the 1-bpp dithered image
}
// done, program will end
} /* setup() */
void loop()
{
// nothing needed here
} /* loop() */

View File

@ -28,9 +28,8 @@
// does not allocate or free any memory; all memory management decisions // does not allocate or free any memory; all memory management decisions
// are left to you // are left to you
JPEGDEC jpeg; JPEGDEC jpeg;
// The LCD display library instance // The LCD display library instance
SPILCD lcd; BB_SPI_LCD lcd;
// //
// Pixel drawing callback // Pixel drawing callback
// called once for each set of MCUs (minimum coded units). // called once for each set of MCUs (minimum coded units).
@ -46,20 +45,22 @@ int drawMCUs(JPEGDRAW *pDraw)
int iCount; int iCount;
iCount = pDraw->iWidth * pDraw->iHeight; // number of pixels to draw in this call iCount = pDraw->iWidth * pDraw->iHeight; // number of pixels to draw in this call
// Serial.printf("Draw pos = %d,%d. size = %d x %d\n", pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight); // Serial.printf("Draw pos = %d,%d. size = %d x %d\n", pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight);
spilcdSetPosition(&lcd, pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight, DRAW_TO_LCD); lcd.setAddrWindow(pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight);
spilcdWriteDataBlock(&lcd, (uint8_t *)pDraw->pPixels, iCount*2, DRAW_TO_LCD | DRAW_WITH_DMA); lcd.pushPixels(pDraw->pPixels, iCount, DRAW_TO_LCD | DRAW_WITH_DMA);
return 1; // returning true (1) tells JPEGDEC to continue decoding. Returning false (0) would quit decoding immediately. return 1; // returning true (1) tells JPEGDEC to continue decoding. Returning false (0) would quit decoding immediately.
} /* drawMCUs() */ } /* drawMCUs() */
void setup() void setup()
{ {
Serial.begin(115200); Serial.begin(115200);
while (!Serial) {}; delay(3000);
spilcdInit(&lcd, LCD_ILI9341, FLAGS_NONE, 40000000, CS_PIN, DC_PIN, RESET_PIN, LED_PIN, MISO_PIN, MOSI_PIN, SCK_PIN); Serial.println("Starting...");
spilcdSetOrientation(&lcd, LCD_ORIENTATION_90); lcd.begin(/*DISPLAY_WS_AMOLED_18); */ DISPLAY_M5STACK_CORE2);
spilcdFill(&lcd, 0, DRAW_TO_LCD); // erase display to black lcd.fillScreen(TFT_BLACK); // erase display to black
spilcdWriteString(&lcd, 46,0,(char *)"JPEG Thumbnail test", 0x7e0,0,FONT_12x16, DRAW_TO_LCD); lcd.setCursor(46, 0);
delay(4000); lcd.setFont(FONT_12x16);
lcd.setTextColor(TFT_GREEN);
lcd.print("JPEG Thumbnail test");
} /* setup() */ } /* setup() */
void loop() { void loop() {
@ -83,7 +84,10 @@ char szTemp[64];
lTime = micros() - lTime; lTime = micros() - lTime;
sprintf(szTemp, "Successfully decoded image in %d us", (int)lTime); sprintf(szTemp, "Successfully decoded image in %d us", (int)lTime);
Serial.println(szTemp); Serial.println(szTemp);
spilcdWriteString(&lcd, 0, 200, szTemp, 0xffe0, 0, FONT_8x8, DRAW_TO_LCD); lcd.setCursor(0,20);
lcd.setFont(FONT_8x8);
lcd.setTextColor(TFT_YELLOW);
lcd.print(szTemp);
} }
jpeg.close(); jpeg.close();
} }

View File

@ -0,0 +1,80 @@
//
// JPEGDisplay class demo
//
// This sketch shows how to use the new helper class, JPEGDisplay to more easily
// display JPEG images on displays supported by my bb_spi_lcd library
// There are only two overloaded methods exposed by the library: loadJPEG(), getJPEGInfo()
// It allows you to pass JPEG image data as a pointer or a filename on a uSD card
// loadJPEG requires a x,y position for where to draw the image. This code doesn't
// currently support clipping, so attempts to draw off the edge of the display
// will return with an error.
//
#include <JPEGDisplay.h> // the definitions
#include <JPEGDisplay.inl> // the code
#include <bb_spi_lcd.h>
#include <SPI.h>
#include <SD.h>
#include "octocat_small.h"
BB_SPI_LCD lcd, sprite; // one instance for the display and another for a 'sprite'
JPEGDisplay jd; // only one instance of this class is needed
#define USE_SDCARD
SPIClass SD_SPI;
bool bSD = false;
// These GPIOs are for the uSD card slot on the JC4827W543 "Cheap Yellow Display"
#define SD_CS 10
#define SD_MOSI 11
#define SD_SCK 12
#define SD_MISO 13
// These GPIOs are for the uSD card slot on the Waveshare ESP32-S3 AMOLED 2.41"
//#define SD_CS 2
//#define SD_MOSI 5
//#define SD_SCK 4
//#define SD_MISO 6
void setup() {
int x, y, w, h, bpp;
lcd.begin(DISPLAY_CYD_543);
lcd.fillScreen(TFT_BLACK);
lcd.setTextColor(TFT_GREEN);
lcd.setFont(FONT_12x16);
#ifdef USE_SDCARD
SD_SPI.begin(SD_SCK, SD_MISO, SD_MOSI, SD_CS);
if (!SD.begin(SD_CS, SD_SPI, 10000000)) { // Faster than 10MHz seems to fail on the CYDs
lcd.println("Card Mount Failed");
} else {
lcd.println("Card Mount Succeeded");
bSD = true;
}
// Load a JPEG from the uSD card
if (bSD) {
// Instead of passing absolute x/y positioning, let the library center it
jd.loadJPEG(&lcd, JPEGDISPLAY_CENTER, JPEGDISPLAY_CENTER, "/tulips_320x213.jpg"); // load this image from the root dir of the SD card
delay(5000);
}
delay(3000);
#endif // USE_SDCARD
//
// Load and display the PNG image all over the display by using a 'sprite'
// First, create a sprite instance of BB_SPI_LCD with the createVirtual() method
// Next, decode a PNG image directly into the sprite memory
// And finally, draw it in multiple places on the LCD
//
lcd.fillScreen(TFT_BLACK);
// You can request the dimensions and bit depth of the image BEFORE decoding it
if (jd.getJPEGInfo(&w, &h, &bpp, octocat_small, sizeof(octocat_small))) {
sprite.createVirtual(w, h); // create a sprite of the JPEG image size
// The JPEG image can be decoded directly into the sprite instance
jd.loadJPEG(&sprite, 0, 0, octocat_small, sizeof(octocat_small));
for (int y = 0; y < lcd.height(); y += h) { // now draw it all over the LCD
for (int x = 0; x < lcd.width(); x += w) {
lcd.drawSprite(x, y, &sprite, 0xffffffff); // 0xffffffff = no transparent color
} // for x
} // for y
sprite.freeVirtual(); // free the sprite memory
}
}
void loop() {
}

View File

@ -0,0 +1,245 @@
// Created with image_to_c
// https://github.com/bitbank2/image_to_c
//
// octocat_small
// Data size = 3710 bytes
//
// JFIF, Compression=JPEG, Size: 120 x 100, 24-Bpp
//
// for non-Arduino builds...
#ifndef PROGMEM
#define PROGMEM
#endif
const uint8_t octocat_small[] PROGMEM = {
0xff,0xd8,0xff,0xe0,0x00,0x10,0x4a,0x46,0x49,0x46,0x00,0x01,0x01,0x01,0x00,0x48,
0x00,0x48,0x00,0x00,0xff,0xdb,0x00,0x43,0x00,0x03,0x02,0x02,0x03,0x02,0x02,0x03,
0x03,0x03,0x03,0x04,0x03,0x03,0x04,0x05,0x08,0x05,0x05,0x04,0x04,0x05,0x0a,0x07,
0x07,0x06,0x08,0x0c,0x0a,0x0c,0x0c,0x0b,0x0a,0x0b,0x0b,0x0d,0x0e,0x12,0x10,0x0d,
0x0e,0x11,0x0e,0x0b,0x0b,0x10,0x16,0x10,0x11,0x13,0x14,0x15,0x15,0x15,0x0c,0x0f,
0x17,0x18,0x16,0x14,0x18,0x12,0x14,0x15,0x14,0xff,0xdb,0x00,0x43,0x01,0x03,0x04,
0x04,0x05,0x04,0x05,0x09,0x05,0x05,0x09,0x14,0x0d,0x0b,0x0d,0x14,0x14,0x14,0x14,
0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,
0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,
0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0xff,0xc0,
0x00,0x11,0x08,0x00,0x64,0x00,0x78,0x03,0x01,0x22,0x00,0x02,0x11,0x01,0x03,0x11,
0x01,0xff,0xc4,0x00,0x1f,0x00,0x00,0x01,0x05,0x01,0x01,0x01,0x01,0x01,0x01,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,
0x0a,0x0b,0xff,0xc4,0x00,0xb5,0x10,0x00,0x02,0x01,0x03,0x03,0x02,0x04,0x03,0x05,
0x05,0x04,0x04,0x00,0x00,0x01,0x7d,0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,
0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,0x23,
0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,
0x18,0x19,0x1a,0x25,0x26,0x27,0x28,0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,
0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,
0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,
0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,
0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,
0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,
0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,
0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xff,0xc4,0x00,0x1f,0x01,0x00,0x03,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0xff,0xc4,0x00,0xb5,0x11,0x00,
0x02,0x01,0x02,0x04,0x04,0x03,0x04,0x07,0x05,0x04,0x04,0x00,0x01,0x02,0x77,0x00,
0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,
0x22,0x32,0x81,0x08,0x14,0x42,0x91,0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,
0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,0x27,
0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,
0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,0x88,
0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,
0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,
0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe2,
0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,
0xfa,0xff,0xda,0x00,0x0c,0x03,0x01,0x00,0x02,0x11,0x03,0x11,0x00,0x3f,0x00,0xfc,
0xaa,0xa2,0x8a,0x28,0x00,0xab,0x1a,0x75,0xb7,0xdb,0x2f,0xa0,0x87,0xb3,0xb8,0x07,
0xe9,0xde,0xab,0xd6,0x87,0x87,0xe4,0x11,0x6b,0x36,0xa5,0xba,0x6f,0xc7,0xe6,0x31,
0x40,0x1d,0x4d,0xc7,0x84,0xf4,0xf9,0x93,0x08,0x8d,0x0b,0x7f,0x79,0x58,0x9f,0xe7,
0x5c,0xe6,0xa5,0xe1,0x9b,0xbb,0x03,0x94,0x53,0x71,0x11,0x38,0x0d,0x18,0xe7,0xf1,
0x15,0xde,0x51,0x40,0x1e,0x7d,0x17,0x87,0xb5,0x19,0x86,0x56,0xd5,0x80,0xff,0x00,
0x68,0x85,0xfe,0x75,0x2f,0xfc,0x22,0xba,0x96,0x3f,0xd4,0xaf,0xd3,0x78,0xae,0xf2,
0x8a,0x00,0xf3,0xd9,0xbc,0x3f,0xa8,0x42,0x32,0xd6,0xae,0x47,0xfb,0x38,0x6f,0xe5,
0x5a,0x7a,0x6f,0x83,0xe5,0x98,0x07,0xbb,0x73,0x0a,0x9f,0xf9,0x66,0xbf,0x7b,0xff,
0x00,0xad,0x5d,0x7d,0x14,0x01,0xcf,0xea,0x5e,0x19,0xb2,0x87,0x4c,0x9d,0xa1,0x88,
0x89,0x51,0x0b,0x07,0x2c,0x49,0xe3,0x9a,0xe3,0x6b,0xd2,0xb5,0x17,0xf2,0xec,0x2e,
0x58,0x8c,0xe2,0x36,0xe3,0xf0,0xaf,0x35,0xa0,0x02,0x8a,0x28,0xa0,0x02,0x8a,0x28,
0xa0,0x02,0x8a,0x28,0xa0,0x02,0xba,0x3f,0x08,0x69,0x6b,0x3c,0xad,0x77,0x20,0xca,
0xc6,0x70,0x80,0xff,0x00,0x7b,0xd7,0xf0,0xae,0x72,0xbb,0xdf,0x0c,0x20,0x4d,0x12,
0xdf,0x1f,0xc5,0xb8,0x9f,0xcc,0xd0,0x06,0xad,0x15,0xd3,0x7c,0x3a,0xf8,0x73,0xaf,
0x7c,0x54,0xf1,0x55,0xaf,0x87,0xfc,0x3b,0x64,0xd7,0x9a,0x84,0xe7,0x3e,0x89,0x1a,
0x8e,0xae,0xe7,0xb2,0x8f,0x5a,0xfb,0x33,0xc3,0x7f,0xf0,0x4c,0x70,0xf6,0x08,0xda,
0xe7,0x8b,0xe4,0x17,0x64,0x65,0x92,0xc6,0x00,0x15,0x4f,0xa6,0x5b,0x39,0xfa,0xf1,
0x5c,0xf5,0x71,0x14,0xe8,0xbb,0x4d,0x9d,0x54,0xb0,0xd5,0x6b,0xab,0xc1,0x68,0x7c,
0x1b,0x45,0x7d,0xf7,0x7d,0xff,0x00,0x04,0xc6,0xb3,0x2a,0x7e,0xc7,0xe3,0x0b,0x85,
0x3d,0xbc,0xe8,0x55,0xbf,0x90,0x15,0x90,0xbf,0xf0,0x4c,0x5b,0xfd,0xfc,0xf8,0xd6,
0x2d,0x9f,0xf5,0xe3,0xcf,0xfe,0x87,0x58,0xac,0x6d,0x0f,0xe6,0x37,0x78,0x0c,0x42,
0xfb,0x3f,0x8a,0x3e,0x1b,0xa2,0xbe,0xff,0x00,0xb2,0xff,0x00,0x82,0x63,0xe9,0xc1,
0x47,0xda,0xfc,0x61,0x74,0x5b,0xfe,0x98,0xc4,0xa0,0x7e,0xa0,0xd6,0x3f,0x8c,0xff,
0x00,0xe0,0x99,0x97,0x56,0xba,0x6c,0xb3,0x78,0x67,0xc5,0x3f,0x6a,0xbb,0x45,0x2c,
0xb6,0xd7,0xf1,0x00,0x24,0x3e,0x81,0x97,0x18,0xfc,0x8d,0x0b,0x1b,0x41,0xbb,0x5c,
0x1e,0x03,0x10,0x95,0xf9,0x7f,0x13,0xe1,0x8e,0xb5,0xc1,0xf8,0x97,0x4c,0x1a,0x75,
0xfe,0x63,0x18,0x86,0x51,0xb9,0x47,0xa7,0xa8,0xff,0x00,0x3e,0xb5,0xea,0x3e,0x29,
0xf0,0xbe,0xa9,0xe0,0xbd,0x7e,0xf7,0x45,0xd6,0xac,0xe4,0xb0,0xd4,0xac,0xe4,0x31,
0xcd,0x04,0xa3,0x05,0x4f,0xf5,0x07,0xa8,0x35,0xc3,0xf8,0xd6,0x30,0x6c,0xed,0xdf,
0x1c,0x89,0x31,0x9f,0xa8,0xff,0x00,0xeb,0x57,0x6a,0x69,0xab,0xa3,0xcf,0x69,0xa7,
0x66,0x72,0x14,0x51,0x45,0x31,0x05,0x14,0x51,0x40,0x05,0x15,0x35,0xbc,0x62,0x42,
0x47,0x95,0x24,0xa7,0xd1,0x0e,0x31,0xfa,0x1a,0x9b,0xcb,0x55,0xfb,0xd0,0xc0,0x83,
0xfd,0xb9,0x09,0x3f,0x90,0x3f,0xd2,0x80,0x29,0xd7,0x79,0xe1,0x59,0x04,0x9a,0x2c,
0x20,0x1e,0x50,0xb2,0x9f,0xcf,0x3f,0xd6,0xb8,0xf0,0xd0,0x8e,0xa6,0x0f,0xf8,0x0a,
0xb9,0xfe,0x75,0xbd,0xe1,0x6d,0x4e,0x04,0x95,0xed,0x41,0xdb,0xe6,0x7c,0xcb,0xf2,
0xed,0x19,0xef,0xdc,0xff,0x00,0x91,0x40,0x1f,0xaa,0x7f,0xf0,0x4e,0x3f,0x87,0xb6,
0x1a,0x37,0xc2,0x4b,0xbf,0x15,0xf9,0x48,0xfa,0xa6,0xb1,0x79,0x24,0x5e,0x71,0x1f,
0x32,0x43,0x11,0xda,0x10,0x1f,0xf7,0x83,0x1f,0xc4,0x57,0xd4,0x7f,0x18,0x3e,0x2f,
0x9f,0x83,0x7e,0x1d,0xb6,0xb5,0xd1,0xee,0x74,0xe8,0x75,0xf7,0xb6,0x86,0xf9,0xad,
0xef,0xe0,0x79,0x5a,0xea,0x37,0x72,0xbb,0x10,0xa9,0x00,0x6d,0xda,0x49,0xc9,0xe8,
0x38,0xeb,0x5f,0x28,0xff,0x00,0xc1,0x36,0xbe,0x21,0xda,0x6a,0xdf,0x0c,0xf5,0x6f,
0x08,0x49,0x32,0xae,0xa5,0xa4,0xde,0x35,0xcc,0x70,0x93,0x82,0xd0,0x4b,0x83,0xb8,
0x7a,0xe1,0xc3,0x67,0xd3,0x23,0xd6,0xbe,0xa4,0xf8,0xb7,0xf0,0x6f,0x4e,0xf8,0xdf,
0x67,0xa2,0x5d,0x8d,0x72,0x1d,0x0b,0x58,0xd3,0x22,0xfb,0x2c,0xad,0x72,0x9b,0x96,
0x68,0x73,0x91,0x8e,0x47,0x23,0x93,0xf8,0x9a,0xf0,0x1b,0x94,0x6b,0xd4,0xfe,0x6e,
0x9f,0xd7,0xa1,0xf5,0x78,0x75,0x46,0x54,0xe8,0xfb,0x5f,0x83,0xaf,0x5d,0x7c,0xd7,
0x6b,0xfe,0x86,0xc6,0x93,0xe2,0x1b,0x7f,0x19,0x78,0x77,0x46,0xf1,0x15,0xac,0x02,
0xd2,0x3d,0x52,0xd4,0x4e,0xd6,0xea,0x72,0xb1,0x48,0x18,0xa4,0x8a,0x3d,0x83,0x29,
0xc5,0x4f,0x4c,0xb2,0xd2,0x2c,0x7c,0x37,0xa3,0xe9,0x9a,0x1e,0x98,0xcd,0x25,0x86,
0x99,0x6e,0xb6,0xd1,0xca,0xe3,0x0d,0x29,0x04,0x96,0x72,0x3b,0x6e,0x62,0xc7,0xf1,
0xac,0xe8,0x75,0xe4,0x9b,0xc4,0x57,0x1a,0x40,0xb4,0xba,0x59,0x21,0x81,0x67,0x37,
0x2d,0x16,0x20,0x60,0x4e,0x36,0x86,0xee,0xde,0xd5,0xe7,0x55,0xb3,0x9b,0xb1,0xe8,
0x53,0x5e,0xee,0x9b,0x7e,0x97,0xd3,0xf0,0xb1,0xa1,0x7f,0xaf,0xd9,0xf8,0x4b,0x45,
0xd4,0xf5,0xeb,0xe5,0x47,0xb7,0xd3,0xa1,0x12,0x05,0x90,0x12,0x85,0xd9,0xd5,0x13,
0x76,0x32,0x76,0xee,0x70,0x4e,0x3b,0x0a,0xc7,0xf8,0x51,0xf1,0x96,0x6f,0x8b,0xd7,
0xbe,0x2b,0xf0,0xfe,0xa6,0xd6,0x17,0xb7,0x5a,0x4a,0x35,0xd5,0x86,0xa7,0xa7,0x42,
0xf0,0xc7,0x3c,0x2a,0xdb,0x4f,0xc8,0xe4,0x91,0xd5,0x7f,0xef,0xaf,0x6c,0xd6,0xbe,
0xa1,0xa3,0x58,0xf8,0x9f,0x45,0xd4,0xf4,0x3d,0x4d,0xda,0x2b,0x0d,0x4e,0xdc,0xdb,
0xc9,0x2a,0x0c,0x98,0x8e,0x43,0x23,0x81,0xdf,0x6b,0x2a,0x9f,0xc2,0xb1,0xbe,0x12,
0x7c,0x1d,0xd3,0xbe,0x07,0xe9,0xfa,0xdd,0xc9,0xd6,0xe1,0xd7,0x75,0x8d,0x4e,0x31,
0x6d,0x1b,0x5b,0x26,0xd4,0x86,0x1c,0xe4,0xf7,0x3c,0x9e,0x09,0xfa,0x0a,0xec,0xa1,
0x2b,0x53,0xb6,0x96,0xd6,0xff,0x00,0xa7,0xfc,0x03,0x9e,0xac,0x69,0xb5,0x27,0x2b,
0xfb,0x4d,0x39,0x7e,0xf5,0x7d,0x7a,0x75,0xbd,0xf7,0x56,0x3e,0x2f,0xff,0x00,0x82,
0x99,0x7c,0x3d,0xb0,0x86,0xcf,0xc2,0xfe,0x34,0xb7,0x89,0x62,0xbf,0x92,0x76,0xd3,
0x6e,0x59,0x46,0x0c,0xab,0xb4,0xba,0x13,0xee,0x36,0xb0,0xfc,0x6b,0xf3,0x9f,0xc6,
0xce,0x05,0x9d,0xba,0x77,0x2e,0x4f,0xe4,0x3f,0xfa,0xf5,0xfa,0x31,0xff,0x00,0x05,
0x38,0xf1,0xbd,0xb3,0x5b,0x78,0x3f,0xc2,0x31,0x48,0xaf,0x74,0xb2,0x49,0xa9,0xdc,
0x20,0x3c,0xc6,0xb8,0xf2,0xe3,0xc8,0xf7,0xcc,0x9f,0xf7,0xcd,0x7e,0x6e,0x78,0x9b,
0x51,0x82,0x7b,0xe1,0x11,0x21,0xbc,0x91,0x8e,0x53,0x70,0xc9,0xeb,0xdc,0x7b,0x57,
0xad,0x82,0xbf,0xb0,0x8d,0xcf,0x9c,0xc7,0xdb,0xeb,0x12,0xb7,0x91,0xcd,0xd1,0x57,
0x37,0x42,0x7a,0x18,0x3f,0xe0,0x4a,0xe3,0xf9,0x51,0xe5,0x86,0xfb,0xb0,0xc2,0xe3,
0xfd,0x89,0x08,0x3f,0x91,0x3f,0xd2,0xbb,0x8f,0x3c,0xa7,0x45,0x4b,0x71,0x18,0x8c,
0x81,0xe5,0x49,0x11,0xf4,0x73,0x9c,0xfe,0x82,0x8a,0x00,0x8b,0x24,0x02,0x32,0x70,
0x7b,0x51,0x45,0x14,0x00,0xa8,0x8d,0x23,0xaa,0x28,0xdc,0xcc,0x70,0x00,0xee,0x6a,
0xfd,0xce,0x85,0xa9,0xe9,0xb6,0xa9,0x7b,0x35,0x9c,0xf0,0xdb,0x79,0x9e,0x5a,0xdc,
0xec,0x3b,0x37,0x81,0x9d,0xbb,0x87,0x19,0xc6,0x0e,0x3a,0xd4,0xde,0x11,0x7d,0x2e,
0x3f,0x15,0xe8,0xcf,0xad,0x97,0x5d,0x19,0x6f,0x61,0x37,0xa6,0x25,0xdc,0xde,0x46,
0xf1,0xe6,0x60,0x77,0x3b,0x73,0x5e,0xbf,0xa8,0x7c,0x4d,0xf0,0xdf,0xfc,0x2d,0x8f,
0x13,0x69,0xfa,0x57,0x9d,0x7b,0xf0,0xdb,0x54,0xbf,0x90,0xc5,0x68,0xd0,0x22,0xb8,
0xb6,0x76,0x52,0xc2,0x25,0x91,0x48,0x8a,0x4c,0x02,0xaa,0xf8,0x0c,0x00,0x03,0xbe,
0x2a,0xd4,0x53,0x57,0x6c,0xca,0x53,0x69,0xd9,0x2b,0xf5,0xff,0x00,0x81,0xea,0x72,
0xdf,0x0a,0xfe,0x37,0xeb,0x7f,0x0c,0xbc,0x4d,0x65,0xae,0x69,0x37,0xd2,0x69,0xda,
0xad,0xa9,0xf9,0x6e,0x63,0x19,0x57,0x5e,0xe9,0x22,0xff,0x00,0x12,0x9e,0xe2,0xbf,
0x43,0x3e,0x17,0x7f,0xc1,0x4e,0x7c,0x33,0xac,0x59,0x41,0x07,0x8c,0xb4,0x59,0xec,
0x2f,0x00,0x0a,0xf7,0x9a,0x49,0x13,0xc0,0xc7,0x1c,0xb1,0x42,0x43,0x2f,0xd0,0x6e,
0xaf,0xcd,0x8f,0x88,0xbf,0x0f,0x2e,0x3c,0x19,0xa8,0x09,0xad,0xde,0x3b,0xed,0x16,
0xe9,0x12,0x7b,0x6b,0xbb,0x69,0x44,0xeb,0x1a,0xc8,0x37,0x2c,0x32,0xba,0x8c,0x2c,
0xaa,0x38,0x65,0xea,0x08,0xae,0x5a,0xc2,0xca,0x6b,0xeb,0x94,0x8e,0x25,0x63,0x92,
0x37,0x32,0x82,0x76,0x8c,0xf5,0x35,0xcb,0x5b,0x0d,0x0a,0xae,0xd3,0x5a,0x9d,0xb8,
0x7c,0x5d,0x4a,0x4a,0xf4,0xde,0x87,0xed,0xce,0x97,0xfb,0x6e,0xfc,0x18,0xd5,0x02,
0xe3,0xc6,0x29,0x6a,0xc4,0x67,0x6d,0xd5,0x9c,0xf1,0xe3,0xea,0x4a,0x63,0xf5,0xa2,
0x2f,0xda,0x83,0xe1,0x42,0x4e,0x8c,0xdf,0x14,0x2c,0x9e,0xd1,0x24,0x32,0xac,0x07,
0x70,0x72,0x72,0x4e,0xd6,0x7d,0xb9,0x2a,0x33,0xd3,0xaf,0x03,0x24,0x8a,0xfc,0x84,
0xb7,0xd2,0x60,0xb7,0x50,0x01,0x95,0x88,0x1d,0x5a,0x56,0x3f,0xd6,0xa5,0xfb,0x3c,
0x1b,0xca,0x73,0xbb,0x19,0xc6,0xf3,0x9c,0x7e,0x75,0xc3,0xfd,0x9d,0x4f,0xa3,0x67,
0xa2,0xb3,0x4a,0xab,0xec,0xa3,0xf5,0xf3,0x51,0xfd,0xb5,0x3e,0x0c,0xe9,0xab,0x97,
0xf1,0xad,0xbc,0xe7,0x19,0xdb,0x6f,0x6b,0x3c,0x84,0xfe,0x49,0x8a,0xf1,0xef,0x8a,
0x5f,0xf0,0x53,0x1f,0x06,0xe8,0x16,0x13,0xc3,0xe1,0x0d,0x36,0xe7,0x58,0xbf,0x2a,
0x44,0x77,0x5a,0x80,0xfb,0x3d,0xb2,0x9f,0x5d,0xb9,0xde,0xdf,0x4c,0x2f,0xd6,0xbf,
0x35,0xb5,0x1d,0x09,0x6e,0xa2,0x6f,0x26,0x69,0xa1,0x97,0x1c,0x7e,0xf1,0x8a,0x9f,
0xa8,0x26,0xb8,0x49,0x91,0xe2,0x95,0xd2,0x40,0x43,0xa9,0x21,0x81,0xf5,0xab,0x8e,
0x02,0x8c,0x5d,0xdd,0xd9,0x9c,0xf3,0x2a,0xd2,0x56,0x56,0x47,0xa4,0x7c,0x51,0xf8,
0xd1,0xab,0xfc,0x48,0xf1,0x3e,0xa3,0xae,0xea,0x57,0x8f,0x7f,0xab,0x5f,0x3e,0xe9,
0x6e,0x9c,0x6d,0x55,0x1d,0x02,0xa2,0xf6,0x00,0x70,0x07,0x6c,0x57,0x9a,0x92,0x58,
0x92,0x4e,0x49,0xe4,0x93,0x45,0x15,0xe8,0xa4,0x92,0xb2,0x3c,0xb6,0xdb,0x77,0x61,
0x45,0x14,0x53,0x10,0x12,0x48,0x03,0x3c,0x0e,0xd4,0x51,0x45,0x00,0x14,0x51,0x45,
0x00,0x14,0xaa,0xc5,0x18,0x32,0x92,0xac,0x0e,0x41,0x1d,0xa9,0x28,0xa0,0x0f,0x4c,
0xf0,0x57,0xc6,0xdd,0x63,0xc2,0xb0,0xdb,0x40,0x97,0x6e,0xb6,0xf0,0x5c,0x8b,0xc1,
0x6d,0x2c,0x6b,0x3d,0xab,0x4c,0x10,0xa0,0x91,0xa1,0x70,0x54,0xb6,0xd6,0x23,0x24,
0x1a,0xd2,0x6f,0x8b,0x97,0x7a,0x9d,0x8d,0xb6,0x8f,0x6d,0x73,0x15,0xbc,0x3e,0x43,
0xda,0x15,0xb4,0xb3,0x8e,0x03,0x2c,0x4d,0x28,0x95,0x96,0x46,0x55,0x05,0xc6,0xe5,
0x04,0x6e,0xce,0x31,0x81,0x5c,0x57,0x80,0xfe,0x16,0x78,0xbb,0xe2,0x7d,0xff,0x00,
0xd8,0xfc,0x29,0xe1,0xdd,0x43,0x5d,0x9c,0x1c,0x37,0xd9,0x20,0x2c,0x89,0xfe,0xf3,
0xfd,0xd5,0xfc,0x48,0xaf,0x78,0x3f,0xb0,0x3f,0xc5,0x0f,0x05,0xf8,0x4b,0x50,0xf1,
0x97,0x88,0x53,0x4a,0xd2,0x2c,0xb4,0xab,0x76,0xba,0x96,0xce,0x4b,0xbf,0x32,0xe1,
0x94,0x0e,0x40,0x08,0xa5,0x73,0xcf,0x76,0xad,0xe3,0x0a,0xb2,0x8d,0xe2,0x9d,0x8e,
0x4a,0x95,0x30,0xf4,0xe4,0x94,0xda,0x4d,0xfd,0xe7,0x92,0x4d,0x2b,0x0d,0x52,0xda,
0x30,0x7e,0x53,0x1b,0xb1,0x1e,0xa7,0xe5,0xc7,0xf3,0x34,0x2f,0xfc,0x85,0xdf,0xfe,
0xb8,0x2f,0xfe,0x84,0x6b,0xd0,0x74,0x7f,0x84,0xbf,0xdb,0x7f,0x0a,0xf5,0xff,0x00,
0x88,0x3f,0xda,0x5e,0x4f,0xf6,0x25,0xe4,0x36,0x02,0xc3,0xc9,0xcf,0x9d,0xe7,0x11,
0xf3,0x6f,0xcf,0x18,0xc7,0x4c,0x1c,0xd4,0x52,0xfc,0x37,0x82,0x2f,0x84,0xb0,0x78,
0xf7,0xed,0xb2,0x1b,0x89,0xb5,0x96,0xd1,0x8d,0x9e,0xc1,0xb0,0x2a,0xc2,0x25,0xdf,
0xbb,0x39,0xce,0x5b,0x18,0xa8,0xf6,0x72,0xdf,0xca,0xff,0x00,0x23,0x5f,0x6b,0x0b,
0xda,0xfd,0x6d,0xf3,0x38,0x4b,0x69,0x99,0xb5,0x1b,0xc8,0x89,0xca,0xa0,0x46,0x1f,
0x88,0x3f,0xe1,0x5c,0x36,0xbb,0xff,0x00,0x21,0x8b,0xbf,0xfa,0xe8,0x6b,0xdd,0xbc,
0x4b,0xf0,0xa9,0x7c,0x2d,0xf0,0xf3,0xc2,0x3e,0x35,0x1a,0x89,0xb9,0x6f,0x14,0x35,
0xd4,0x66,0xcc,0xc3,0xb4,0x5b,0xfd,0x9d,0xc2,0x67,0x76,0x4e,0xed,0xdb,0xf3,0xd0,
0x63,0x1d,0xeb,0xaa,0x93,0xfe,0x09,0xe7,0xf1,0x37,0xc5,0x5e,0x16,0xd3,0xfc,0x5b,
0xe1,0xf9,0xf4,0x7d,0x5a,0xdb,0x55,0xb5,0x8e,0xfa,0x2b,0x31,0x72,0x62,0x9d,0x55,
0xd4,0x30,0x53,0xbd,0x42,0xe7,0x9f,0xef,0x55,0x2a,0x35,0x24,0xed,0x15,0x7e,0xa6,
0x72,0xc4,0xd1,0x82,0x52,0x9c,0xac,0x9b,0x6b,0x5e,0xeb,0x73,0xe5,0x1a,0x2b,0xb2,
0xf8,0x87,0xf0,0x6b,0xc6,0xff,0x00,0x0a,0x2e,0xbc,0x8f,0x16,0x78,0x67,0x50,0xd1,
0x09,0x3b,0x56,0x5b,0x88,0x4f,0x92,0xe7,0xfd,0x99,0x06,0x55,0xbf,0x03,0x5c,0x6d,
0x64,0xe2,0xe2,0xec,0xd1,0xd1,0x19,0x46,0x6b,0x9a,0x2e,0xe8,0x28,0xa2,0x8a,0x45,
0x05,0x14,0x51,0x40,0x05,0x14,0x51,0x40,0x05,0x7d,0x0d,0xfb,0x11,0xfe,0xcf,0x3a,
0x77,0xed,0x09,0xf1,0x5a,0x6b,0x2d,0x72,0x47,0x1e,0x1f,0xd2,0x2d,0xbe,0xdb,0x79,
0x04,0x4d,0xb5,0xee,0x3e,0x60,0xa9,0x18,0x3d,0x40,0x24,0x92,0x48,0xec,0x31,0xdf,
0x35,0xf3,0xcd,0x7a,0x47,0xc0,0x1f,0x8e,0x9a,0xe7,0xec,0xf9,0xf1,0x0a,0xdb,0xc4,
0xfa,0x22,0xa5,0xc0,0xd8,0x60,0xbb,0xb2,0x94,0xe1,0x2e,0x60,0x24,0x16,0x42,0x7b,
0x1c,0x80,0x41,0xec,0x40,0xfa,0x56,0xd4,0x5c,0x15,0x44,0xe7,0xb1,0xcd,0x89,0x8d,
0x49,0x51,0x94,0x69,0x3b,0x4a,0xda,0x1f,0xb7,0x9e,0x17,0xf0,0x96,0x8b,0xe0,0x9d,
0x16,0xdf,0x49,0xd0,0x74,0xbb,0x5d,0x23,0x4d,0xb7,0x50,0xb1,0xdb,0x5a,0x44,0x23,
0x45,0x1f,0x41,0xdf,0xde,0xbc,0x17,0xf6,0xb5,0xbc,0xf8,0x8d,0xe2,0xad,0x12,0xe7,
0xc0,0xde,0x11,0xf0,0xa3,0x3e,0x99,0xa9,0x22,0x2d,0xde,0xbb,0x3d,0xd4,0x49,0x19,
0x4c,0x82,0xd1,0xa2,0x96,0xc8,0xe9,0x82,0x4f,0x6c,0x80,0x39,0xcd,0x69,0xfc,0x1b,
0xfd,0xb7,0x3e,0x17,0x7c,0x60,0xb5,0xb7,0x8e,0x3d,0x72,0x2f,0x0e,0xeb,0x4e,0x00,
0x7d,0x2f,0x59,0x71,0x03,0xee,0xf4,0x47,0x3f,0x23,0xfe,0x07,0x3e,0xc2,0xbd,0xa7,
0x5c,0x78,0x6f,0x34,0x29,0xe4,0x8d,0xd2,0x68,0xca,0x86,0x57,0x42,0x08,0x3c,0xf5,
0x04,0x57,0xd6,0xc5,0x53,0xc4,0x45,0x46,0x32,0xd1,0xf6,0x3f,0x3c,0xfd,0xee,0x16,
0xaf,0x3d,0x48,0x7b,0xcb,0xbd,0xf7,0xef,0xe7,0xf7,0x9f,0x0b,0x7c,0x4e,0xf8,0x49,
0x6b,0xf0,0x17,0xf6,0x3a,0xbe,0xd2,0x2f,0xb5,0x4b,0x7b,0xcd,0x7b,0x5c,0xd5,0xed,
0xe7,0x94,0x42,0xdf,0x2e,0xf5,0x20,0xf9,0x69,0x9c,0x12,0x15,0x54,0x92,0x71,0xd4,
0xfd,0x2b,0xcc,0xaf,0xfc,0x15,0xac,0x43,0xfb,0x16,0xe9,0xba,0xab,0x59,0x4a,0x2c,
0xdb,0xc5,0x52,0x5d,0xef,0xda,0x78,0x84,0xc0,0x22,0x12,0x1f,0xf6,0x4b,0xa9,0x19,
0xfa,0x7a,0xd7,0xda,0x5e,0x2f,0xf8,0x2b,0xe1,0x1f,0x88,0xd3,0x5b,0xeb,0x7e,0x21,
0xd2,0xff,0x00,0xb4,0xee,0xf4,0xf2,0xb1,0x42,0x92,0xcc,0xe2,0x20,0xa4,0x92,0x72,
0x80,0x85,0x3c,0xfa,0x8a,0xee,0x26,0xb6,0xb4,0x9b,0xc0,0x03,0x47,0x7b,0x38,0x1b,
0x4e,0x27,0xec,0x86,0xd4,0xc6,0x3c,0xa3,0x10,0x1f,0x73,0x6e,0x31,0x8c,0x71,0x8a,
0xce,0x79,0x5b,0x94,0xdf,0x2b,0xb4,0x6d,0xca,0xbf,0xcd,0x9e,0x94,0x33,0x5e,0x48,
0x46,0xea,0xf2,0xe6,0xe6,0x7d,0x3e,0x48,0xf9,0x0b,0xc1,0xbf,0x07,0xad,0xbf,0x68,
0x0f,0xd8,0xfb,0xc3,0x56,0xba,0x56,0xa7,0x6d,0x6b,0xe2,0x2f,0x0e,0x5f,0x5d,0xec,
0x5b,0x86,0xf9,0x49,0x79,0x0b,0x34,0x4d,0x8c,0x95,0xdc,0xa5,0x18,0x1c,0x76,0xaf,
0x77,0xfd,0x92,0x6f,0xbe,0x21,0xe8,0x3e,0x1b,0x87,0xc1,0x7e,0x33,0xf0,0xc3,0xdb,
0x59,0xe9,0x30,0x94,0xb3,0xd6,0xe3,0xb9,0x8d,0xe3,0x74,0x0d,0xf2,0xc4,0xca,0x18,
0x9c,0x80,0x78,0x23,0xb0,0xe7,0x1d,0xf4,0xbc,0x1f,0xf0,0x6f,0xc2,0x7f,0x0d,0x16,
0x7d,0x47,0xc3,0x9a,0x67,0xf6,0x6c,0xfa,0xa3,0x30,0xb8,0x54,0x99,0xda,0x32,0x14,
0xfc,0xa0,0x21,0x24,0x2f,0x53,0xd3,0xd6,0xbd,0x93,0x46,0xf2,0xed,0xb4,0x6b,0x76,
0x25,0x63,0x4d,0x9b,0x99,0x89,0xc0,0xf5,0x26,0xb5,0x8e,0x0d,0x51,0x8c,0x6a,0x49,
0xfb,0xdb,0x3b,0x6c,0xec,0x72,0x62,0x31,0xbe,0xdb,0x9a,0x9c,0x55,0xe2,0xdd,0xd5,
0xf7,0x4d,0xef,0xb0,0xed,0x6f,0x42,0xd3,0x7c,0x4b,0xa6,0x4f,0xa7,0x6a,0xd6,0x16,
0xfa,0x95,0x84,0xea,0x52,0x5b,0x6b,0xa8,0x84,0x91,0xb8,0x3d,0x8a,0x9e,0x2b,0xf2,
0x63,0xf6,0xf8,0xfd,0x9a,0xf4,0x6f,0x80,0x9e,0x3a,0xd2,0xaf,0xfc,0x32,0x0d,0xbe,
0x81,0xaf,0xa4,0xb2,0x47,0x60,0xcd,0xbb,0xec,0xb2,0xa1,0x5d,0xea,0xa4,0xf3,0xb0,
0xef,0x04,0x67,0xa7,0x23,0xd2,0xbf,0x42,0x7e,0x2f,0x7e,0xd8,0xbf,0x0b,0xbe,0x0e,
0x5b,0x4e,0xba,0x8f,0x88,0xad,0xf5,0x4d,0x5a,0x30,0x76,0xe9,0x5a,0x4b,0x8b,0x89,
0xcb,0x7a,0x36,0xd3,0xb5,0x3e,0xac,0x45,0x7e,0x56,0xfe,0xd2,0xff,0x00,0xb4,0x6e,
0xb7,0xfb,0x48,0xf8,0xe8,0x6b,0x5a,0x8c,0x2b,0xa7,0xe9,0x96,0x88,0x60,0xd3,0xb4,
0xd8,0xdb,0x70,0xb7,0x8c,0x9c,0x92,0x4f,0xf1,0x3b,0x1c,0x12,0x7d,0x80,0xed,0x5e,
0x5e,0x3e,0xa5,0x27,0x0e,0x5d,0xe4,0x7a,0x79,0x3d,0x1c,0x44,0x6a,0xf3,0xea,0xa1,
0xd6,0xfd,0x7f,0xae,0xe7,0x91,0xd1,0x45,0x15,0xf3,0xe7,0xd9,0x05,0x14,0x51,0x40,
0x05,0x14,0x51,0x40,0x05,0x14,0x51,0x40,0x05,0x75,0x5e,0x18,0xf8,0xaf,0xe3,0x4f,
0x05,0xc6,0x23,0xd0,0xbc,0x55,0xac,0x69,0x50,0x8f,0xf9,0x63,0x6b,0x7b,0x22,0x47,
0xff,0x00,0x7c,0x83,0x8f,0xd2,0x8a,0x2a,0x94,0x9c,0x5d,0xd3,0xb1,0x32,0x8c,0x64,
0xad,0x25,0x73,0xd1,0x34,0xcf,0xdb,0x4f,0xe3,0x26,0x97,0x6c,0xd0,0x27,0x8c,0xa6,
0x9e,0x26,0x20,0x95,0xb8,0xb5,0x82,0x4c,0x91,0xd3,0x92,0x99,0xad,0x71,0xfb,0x7a,
0x7c,0x65,0x16,0xa2,0xdf,0xfe,0x12,0x1b,0x53,0x18,0x6d,0xfc,0xe9,0xb0,0x67,0x38,
0xc6,0x7e,0xed,0x14,0x57,0x42,0xc5,0xe2,0x16,0xd5,0x1f,0xde,0x72,0xbc,0x1e,0x19,
0xef,0x4d,0x7d,0xc8,0xcb,0xd4,0xff,0x00,0x6d,0x8f,0x8c,0x9a,0xa5,0xba,0x40,0xfe,
0x30,0x92,0x08,0xd3,0x3b,0x45,0xbd,0xa4,0x08,0x46,0x7a,0xf2,0x12,0xbc,0xff,0x00,
0xc4,0xff,0x00,0x19,0x7c,0x77,0xe3,0x48,0xcc,0x5a,0xe7,0x8c,0x35,0xad,0x4e,0x03,
0xc1,0x82,0x7b,0xe9,0x0c,0x5f,0xf7,0xc6,0x76,0xfe,0x94,0x51,0x51,0x3a,0xf5,0x6a,
0x69,0x39,0x37,0xf3,0x66,0x90,0xc3,0xd1,0xa7,0xac,0x20,0x97,0xc9,0x1c,0x75,0x14,
0x51,0x58,0x1d,0x01,0x45,0x14,0x50,0x01,0x45,0x14,0x50,0x07,0xff,0xd9};

View File

@ -0,0 +1,96 @@
//
// LCD w/DMA example showing best practices
//
// DMA = Direct Memory Access
// DMA is extra hardware within a MCU which can move data independently of the CPU.
// This feature can be used to reduce execution time by allowing the CPU to get back
// to doing the desired work while data is read or written to devices such as displays.
// For JPEG image decoding, DMA can allow pixels to be sent to the display while the
// decoder is busy decoding the next set of pixels. The speed advantage gained from
// DMA varies depending on how fast the data can be sent to the external device.
// In the best case scenario, the transmission of the data takes less time than
// preparation of the data - DMA will effectively 'erase' the transmission time
// because the CPU will be kept busy doing the actual work 100% of the time.
// DMA operations like those of image decoding benefit from having two buffers -
// one is where new data is prepared and the other is for transmitting that data.
// If you use a single buffer, you run the risk of new data overwriting old data
// before it has been transmitted.
//
// JPEGDEC DMA Feature
// The JPEGDEC library includes internal support for managing a ping-pong buffer
// scheme to allow your code to simply use the pixel data without worrying about it
// getting overwritten. The JPEG_USES_DMA flag, when passed to the decode() method,
// tells JPEGDEC to split the internal pixel buffer in half and alternate using the
// two halves with each call to JPEGDraw.
//
#include <bb_spi_lcd.h>
#include <JPEGDEC.h>
#include "../../test_images/zebra.h"
JPEGDEC jpg;
BB_SPI_LCD lcd;
bool bDMA; // a flag for our JPEGDraw function to know if DMA should be enabled
//
// Draw callback from JPEG decoder
//
// Called multiple times with groups of MCUs (minimum coded units)
// these are 8x8, 8x16, 16x8 or 16x16 blocks of pixels depending on the
// color subsampling option of the JPEG image
// Each call can have a large group (e.g. 128x16) of pixels
//
// For this example, we set a global boolean value (bDMA) to indicate whether or not
// we should ask the display library to transmit the pixels using DMA
// JPEGDEC manages the ping-pong buffers, so we don't have to.
//
int JPEGDraw(JPEGDRAW *pDraw)
{
lcd.setAddrWindow(pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight);
lcd.pushPixels((uint16_t *)pDraw->pPixels, pDraw->iWidth * pDraw->iHeight, (bDMA) ? DRAW_TO_LCD | DRAW_WITH_DMA : DRAW_TO_LCD);
return 1;
} /* JPEGDraw() */
void setup()
{
int xoff, yoff;
long lTime;
lcd.begin(DISPLAY_WS_AMOLED_18); // set this to the correct display type for your board
lcd.fillScreen(TFT_BLACK);
lcd.setTextColor(TFT_GREEN, TFT_BLACK);
lcd.setFont(FONT_12x16);
lcd.setCursor((lcd.width() - 192)/2, 0);
lcd.println("JPEG DMA Example");
lcd.setCursor((lcd.width() - 264)/2, 16);
lcd.println("Decode + center on LCD");
bDMA = false;
if (jpg.openFLASH((uint8_t *)zebra, sizeof(zebra), JPEGDraw)) { // pass the data and its size
jpg.setPixelType(RGB565_BIG_ENDIAN); // bb_spi_lcd uses big-endian RGB565 pixels
// if the image is smaller than the LCD dimensions, center it
xoff = (lcd.width() - jpg.getWidth())/2;
yoff = (lcd.height() - jpg.getHeight())/2;
lTime = millis();
jpg.decode(xoff,yoff,0); // center the image and no options bits (0)
lTime = millis() - lTime; // total time to decode + display
lcd.setCursor(20, lcd.height()-16);
lcd.printf("W/O DMA, decoded in %d ms", (int)lTime);
delay(5000);
}
bDMA = true;
if (jpg.openFLASH((uint8_t *)zebra, sizeof(zebra), JPEGDraw)) { // pass the data and its size
jpg.setPixelType(RGB565_BIG_ENDIAN); // bb_spi_lcd uses big-endian RGB565 pixels
// if the image is smaller than the LCD dimensions, center it
xoff = (lcd.width() - jpg.getWidth())/2;
yoff = (lcd.height() - jpg.getHeight())/2;
lTime = millis();
jpg.decode(xoff,yoff, JPEG_USES_DMA); // center the image and prepare for DMA
lTime = millis() - lTime; // total time to decode + display
lcd.setCursor(20, lcd.height()-16);
lcd.printf("With DMA, decoded in %d ms", (int)lTime);
}
} /* setup() */
void loop()
{
} /* loop() */

View File

@ -0,0 +1,183 @@
//
// Example sketch showing how to host a web server on your CYD
// and display JPEG images uploaded to it
// The code will scale incoming images by 1/2, 1/4 or 1/8 to
// make them fit on the LCD. Images larger than 64K (arbitrary)
// or larger than 8x either LCD dimension will not display.
//
//
#include <bb_spi_lcd.h>
#include <JPEGDEC.h>
#include <WiFi.h>
#include <ESPAsyncWebSrv.h> // Install ESPAsyncWebSrv by dvarrel
BB_SPI_LCD lcd;
JPEGDEC jpg;
#define MAX_FILE_SIZE 65536
const String default_ssid = "your_ssid";
const String default_wifipassword = "your_password";
uint8_t *pBuffer;
int iWidth, iHeight, iFileSize;
//
// The HTML is a static string with a simple form to upload the file
// This form contains a single submit button which initiates a multi-part
// file upload.
//
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="UTF-8">
</head>
<body>
<p><h1>CYD JPEG image viewer</h1></p>
<p>Upload a baseline JPEG image up to 64K.<br>
Images larger than the LCD will be scaled down.</p>
<form method="POST" action="/upload" enctype="multipart/form-data"><input type="file" name="data"/><input type="submit" name="upload" value="Upload" title="Upload File"></form>
<p>Image Dimensions: %IMAGESIZE%</p>
<p>After clicking upload it will take some time for the file to upload,<br>
there is no indicator that the upload began. Please be patient.</p>
<p>Once uploaded the page will refresh and image information will be displayed.</p>
</body>
</html>
)rawliteral";
// configuration structure
struct Config {
String ssid; // wifi ssid
String wifipassword; // wifi password
int webserverporthttp; // http port number for web admin
};
// variables
Config config; // configuration
AsyncWebServer *server; // initialise webserver
//
// This callback function draws the pixels as they're decoded
// Each block of pixels is 1 MCU tall (8 or 16 pixels) and a variable
// number of MCUs wide.
//
int JPEGDraw(JPEGDRAW *pDraw)
{
lcd.setAddrWindow(pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight);
lcd.pushPixels((uint16_t *)pDraw->pPixels, pDraw->iWidth * pDraw->iHeight);
return 1;
} /* JPEGDraw() */
// Read the file info and decode it to the LCD
void DisplayFile(void)
{
int options = 0, xoff, yoff, w, h;
if (jpg.openRAM(pBuffer, iFileSize, JPEGDraw)) { // pass the data and its size
jpg.setPixelType(RGB565_BIG_ENDIAN); // bb_spi_lcd uses big-endian RGB565 pixels
// if the image is smaller than the LCD dimensions, center it
w = iWidth = jpg.getWidth();
h = iHeight = jpg.getHeight();
if (w > lcd.width() || h > lcd.height()) { // try to scale it to fit the LCD
if (w > lcd.width() * 4 || h > lcd.height() * 4) {
options = JPEG_SCALE_EIGHTH;
w = iWidth/8; h = iHeight/8;
} else if (w > lcd.width() * 2 || h > lcd.height() * 2) {
options = JPEG_SCALE_QUARTER;
w = iWidth / 4; h = iHeight / 4;
} else {
options = JPEG_SCALE_HALF;
w = iWidth / 2; h = iHeight / 2;
}
}
xoff = (lcd.width() - w)/2;
yoff = (lcd.height() - h)/2;
if (xoff < 0) xoff = 0;
if (yoff < 0) yoff = 0;
lcd.fillScreen(TFT_BLACK);
jpg.decode(xoff,yoff, options); // center the image and no extra options (e.g. scaling)
} else {
lcd.println("Error opening JPEG!");
}
} /* DisplayFile() */
void configureWebServer() {
server->on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
// String logmessage = "Client:" + request->client()->remoteIP().toString() + + " " + request->url();
// lcd.println(logmessage);
request->send_P(200, "text/html", index_html, processor);
});
// run handleUpload function when any file is uploaded
server->on("/upload", HTTP_POST, [](AsyncWebServerRequest *request) {
request->send(200);
}, handleUpload);
} /* configureWebServer() */
// handles jpeg file uploads
void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
if (len) {
if (index+len <= MAX_FILE_SIZE) {
memcpy(&pBuffer[index], data, len);
}
iFileSize = index+len;
}
if (final) { // last chunk
// lcd.print("Upload complete\n");
if (iFileSize <= MAX_FILE_SIZE) { // we were able to hold the complete file in memory
DisplayFile();
request->redirect("/");
} else { // we had to abandon the upload, after exceeding our buffer size
lcd.setCursor(0,0);
lcd.println("File too large!");
}
}
} /* handleUpload() */
// Process info requests from our one variable element
String processor(const String& var) {
char szTemp[128];
if (var == "IMAGESIZE") {
sprintf(szTemp, "%d x %d, %d bytes", iWidth, iHeight, iFileSize);
return String(szTemp);
}
return String();
} /* processor() */
void setup() {
lcd.begin(DISPLAY_WS_AMOLED_18); //DISPLAY_CYD_2USB); // Set this to your display type
lcd.fillScreen(TFT_BLACK);
lcd.setTextColor(TFT_GREEN);
lcd.setFont(FONT_12x16);
iFileSize = 0; // no image loaded
lcd.println("Booting ...");
pBuffer = (uint8_t *)malloc(MAX_FILE_SIZE); // allow up to 64k JPEG
if (!pBuffer) {
lcd.println("malloc failed!");
while (1) {};
}
config.ssid = default_ssid;
config.wifipassword = default_wifipassword;
config.webserverporthttp = 80;
lcd.print("Connecting to Wifi: ");
WiFi.begin(config.ssid.c_str(), config.wifipassword.c_str());
while (WiFi.status() != WL_CONNECTED) {
delay(500);
lcd.print(".");
}
lcd.print("\nServer IP: ");
lcd.println(WiFi.localIP());
// configure web server
server = new AsyncWebServer(config.webserverporthttp);
configureWebServer();
// startup web server
lcd.println("Starting Webserver");
server->begin();
} /* setup() */
// once the server starts, there's nothing to do here
void loop() {
}

View File

@ -1,6 +1,6 @@
{ {
"name": "JPEGDEC", "name": "JPEGDEC",
"version": "1.2.7", "version": "1.8.3",
"description": "A fast JPEG library with a unique set of functions to make viewing image on microcontrollers easy. Includes fast downscaling options and the ability to view Exif embedded thumbnails. Supports baseline grayscale and color images with Huffman encoding.", "description": "A fast JPEG library with a unique set of functions to make viewing image on microcontrollers easy. Includes fast downscaling options and the ability to view Exif embedded thumbnails. Supports baseline grayscale and color images with Huffman encoding.",
"repository": "repository":
{ {

View File

@ -1,5 +1,5 @@
name=JPEGDEC name=JPEGDEC
version=1.5.0 version=1.8.3
author=Larry Bank author=Larry Bank
maintainer=Larry Bank maintainer=Larry Bank
sentence=Optimized JPEG decoder for MCUs with 32K+ RAM. sentence=Optimized JPEG decoder for MCUs with 32K+ RAM.
@ -8,3 +8,4 @@ category=Display
url=https://github.com/bitbank2/JPEGDEC url=https://github.com/bitbank2/JPEGDEC
architectures=* architectures=*
includes=JPEGDEC.h includes=JPEGDEC.h
depends=bb_spi_lcd

View File

@ -7,9 +7,9 @@
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include "JPEGDEC.h" #include "../../../src/JPEGDEC.h"
#include "jpeg.inl" #include "../../../src/jpeg.inl"
#include "../test_images/tulips.h" #include "../../../test_images/tulips.h"
// Human readable error messages // Human readable error messages
const char *szErrors[] = {"Success", "Invalid parameter", "Decode error", "Unsupported feature", "Invalid file"}; const char *szErrors[] = {"Success", "Invalid parameter", "Decode error", "Unsupported feature", "Invalid file"};
@ -169,7 +169,7 @@ int ConvertFileTest(char *argv[], int iFraction)
} }
cx = jpg.iWidth / iFraction; cx = jpg.iWidth / iFraction;
cy = jpg.iHeight / iFraction; cy = jpg.iHeight / iFraction;
cx = (cx + 7) & 0xfff8; // align on at least 16-byte boundary //cx = (cx + 7) & 0xfff8; // align on at least 16-byte boundary
if (ucPixelType == RGB8888) { if (ucPixelType == RGB8888) {
iDestPitch = (cx * 4); // 32-bits per pixel iDestPitch = (cx * 4); // 32-bits per pixel
bpp = 32; bpp = 32;

View File

@ -0,0 +1,13 @@
CFLAGS=-c -Wall -O2 -ggdb -I../src -D__LINUX__
LIBS = -lm -lpthread
all: jpegdec
jpegdec: main.o
$(CC) main.o $(LIBS) -g -o jpegdec
main.o: main.c ../../../src/JPEGDEC.h makefile
$(CC) $(CFLAGS) main.c
clean:
rm *.o jpegdec

View File

@ -0,0 +1,13 @@
CFLAGS=-c -Wall -O2 -ggdb -I../src -D__LINUX__
LIBS = -lJPEGDEC -lbb_spi_lcd -lgpiod -lm -lpthread
all: showimg
showimg: showimg.o
$(CC) showimg.o $(LIBS) -o showimg
showimg.o: showimg.cpp
$(CXX) $(CFLAGS) showimg.cpp
clean:
rm *.o showimg

View File

@ -0,0 +1,63 @@
//
// Display a JPEG image on a SPI LCD
// written by Larry Bank 5/12/2025
//
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <JPEGDEC.h>
#include <bb_spi_lcd.h> // SPI LCD library
BB_SPI_LCD lcd;
JPEGDEC jpg;
// Pin definitions for Adafruit PiTFT HAT
// GPIO 25 = Pin 22
#define DC_PIN 25
// OrangePi RV2 #define DC_PIN 49
// GPIO 27 = Pin 13
#define RESET_PIN -1
// GPIO 8 = Pin 24
#define CS_PIN -1
//#define CS_PIN 76
// GPIO 24 = Pin 18
#define LED_PIN 24
//#define LED_PIN 92
// SPI device is sent as MOSI pin
#define MOSI_PIN 0
// SPI device CS line is sent as MISO pin (spidev3.0 in this case)
#define MISO_PIN 0
#define SCK_PIN -1
#define LCD_TYPE LCD_ILI9341
int JPEGDraw(JPEGDRAW *pDraw)
{
if (pDraw->y + pDraw->iHeight > lcd.height()) return 0; // beyond bottom
lcd.setAddrWindow(pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight);
lcd.pushPixels(pDraw->pPixels, pDraw->iWidth * pDraw->iHeight);
return 1;
} /* JPEGDraw() */
int main(int argc, const char *argv[])
{
if (argc != 2) { // show help
printf("showimg - display a JPEG image on a SPI LCD\n");
printf("usage: showimg <input_file>\n");
return 0;
}
// Initialize the display
// int spilcdInit(int iLCDType, int bFlipRGB, int bInvert, int bFlipped, int32_t iSPIFreq, int iCSPin, int iDCPin, int iResetPin, int iLEDPin, int iMISOPin, int iMOSIPin, int iCLKPin);
lcd.begin(LCD_TYPE, FLAGS_NONE, 62500000, CS_PIN, DC_PIN, RESET_PIN, LED_PIN, MISO_PIN, MOSI_PIN, SCK_PIN);
lcd.setRotation(90);
lcd.fillScreen(TFT_BLACK);
lcd.setTextColor(TFT_GREEN, TFT_BLACK);
lcd.drawStringFast("Linux JPEG Decoder Example", 0,0,FONT_12x16);
if (jpg.open(argv[1], JPEGDraw)) {
printf("Image opened: %dx%d\n", jpg.getWidth(), jpg.getHeight());
jpg.setPixelType(RGB565_BIG_ENDIAN);
jpg.decode(0, 16, 0); // leave the first line of text showing
}
return 0;
} /* main() */

View File

@ -1,13 +1,15 @@
CFLAGS=-c -Wall -O2 -I../src -D__LINUX__ CFLAGS=-c -Wall -O2 -ggdb -D__LINUX__ -I../src
LIBS = -lm -lpthread LIBS = -lm -lpthread
all: jpegdec all: libJPEGDEC.a
jpegdec: main.o libJPEGDEC.a: JPEGDEC.o
$(CC) main.o $(LIBS) -o jpegdec ar -rc libJPEGDEC.a JPEGDEC.o ;\
sudo cp libJPEGDEC.a /usr/local/lib ;\
sudo cp ../src/JPEGDEC.h /usr/local/include
main.o: main.c ../src/JPEGDEC.h makefile JPEGDEC.o: ../src/JPEGDEC.cpp ../src/JPEGDEC.h ../src/jpeg.inl
$(CC) $(CFLAGS) main.c $(CXX) $(CFLAGS) ../src/JPEGDEC.cpp
clean: clean:
rm *.o jpegdec rm *.o libJPEGDEC.a

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

After

Width:  |  Height:  |  Size: 302 KiB

View File

@ -40,6 +40,10 @@ void JPEGDEC::setFramebuffer(void *pFramebuffer)
JPEG_setFramebuffer(&_jpeg, pFramebuffer); JPEG_setFramebuffer(&_jpeg, pFramebuffer);
} /* setFramebuffer() */ } /* setFramebuffer() */
int JPEGDEC::getPixelType()
{
return (int)_jpeg.ucPixelType;
}
void JPEGDEC::setPixelType(int iType) void JPEGDEC::setPixelType(int iType)
{ {
if (iType >= 0 && iType < INVALID_PIXEL_TYPE) if (iType >= 0 && iType < INVALID_PIXEL_TYPE)
@ -72,7 +76,7 @@ int JPEGDEC::openRAM(uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw)
return JPEGInit(&_jpeg); return JPEGInit(&_jpeg);
} /* openRAM() */ } /* openRAM() */
int JPEGDEC::openFLASH(uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw) int JPEGDEC::openFLASH(const uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw)
{ {
memset(&_jpeg, 0, sizeof(JPEGIMAGE)); memset(&_jpeg, 0, sizeof(JPEGIMAGE));
_jpeg.ucMemType = JPEG_MEM_FLASH; _jpeg.ucMemType = JPEG_MEM_FLASH;
@ -82,10 +86,10 @@ int JPEGDEC::openFLASH(uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDra
_jpeg.pfnOpen = NULL; _jpeg.pfnOpen = NULL;
_jpeg.pfnClose = NULL; _jpeg.pfnClose = NULL;
_jpeg.JPEGFile.iSize = iDataSize; _jpeg.JPEGFile.iSize = iDataSize;
_jpeg.JPEGFile.pData = pData; _jpeg.JPEGFile.pData = (uint8_t *)pData;
_jpeg.iMaxMCUs = 1000; // set to an unnaturally high value to start _jpeg.iMaxMCUs = 1000; // set to an unnaturally high value to start
return JPEGInit(&_jpeg); return JPEGInit(&_jpeg);
} /* openRAM() */ } /* openFLASH() */
int JPEGDEC::getOrientation() int JPEGDEC::getOrientation()
{ {
@ -142,6 +146,25 @@ void JPEGDEC::getCropArea(int *x, int *y, int *w, int *h)
JPEG_getCropArea(&_jpeg, x, y, w, h); JPEG_getCropArea(&_jpeg, x, y, w, h);
} /* getCropArea() */ } /* getCropArea() */
int JPEGDEC::getJPEGType()
{
return (_jpeg.ucMode == 0xc2) ? JPEG_MODE_PROGRESSIVE : JPEG_MODE_BASELINE;
} /* getJPEGType() */
#ifdef __LINUX__
int JPEGDEC::open(const char *szFilename, JPEG_DRAW_CALLBACK *pfnDraw)
{
memset(&_jpeg, 0, sizeof(JPEGIMAGE));
_jpeg.pfnRead = readFile;
_jpeg.pfnClose = closeFile;
_jpeg.pfnSeek = seekFile;
_jpeg.pfnDraw = pfnDraw;
_jpeg.iMaxMCUs = 1000;
_jpeg.JPEGFile.fHandle = openFile(szFilename, &_jpeg.JPEGFile.iSize);
if (_jpeg.JPEGFile.fHandle == NULL) return 0;
return JPEGInit(&_jpeg);
} /* open() */
#endif // __LINUX__
// //
// File (SD/MMC) based initialization // File (SD/MMC) based initialization
// //

View File

@ -13,7 +13,7 @@
// //
#ifndef __JPEGDEC__ #ifndef __JPEGDEC__
#define __JPEGDEC__ #define __JPEGDEC__
#if defined( __MACH__ ) || defined( __LINUX__ ) || defined( __MCUXPRESSO ) || defined( ESP_PLATFORM ) #if defined( __MACH__ ) || defined( __LINUX__ ) || defined( __MCUXPRESSO ) || defined( ESP_PLATFORM ) || defined(_WIN64)
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
@ -28,6 +28,12 @@
#define memcpy_P memcpy #define memcpy_P memcpy
#define PROGMEM #define PROGMEM
#endif #endif
#ifdef _M_X64 //MSVC
#define __x86_64__
#define __builtin_bswap16 _byteswap_ushort
#define __builtin_bswap64 _byteswap_uint64
#define __builtin_bswap32 _byteswap_ulong
#endif
// Cortex-M4/M7 allow unaligned access to SRAM // Cortex-M4/M7 allow unaligned access to SRAM
#if defined(HAL_ESP32_HAL_H_) || defined(TEENSYDUINO) || defined(ARM_MATH_CM4) || defined(ARM_MATH_CM7) || defined (__x86_64__) || defined(TEENSYDUINO) #if defined(HAL_ESP32_HAL_H_) || defined(TEENSYDUINO) || defined(ARM_MATH_CM4) || defined(ARM_MATH_CM7) || defined (__x86_64__) || defined(TEENSYDUINO)
#define ALLOWS_UNALIGNED #define ALLOWS_UNALIGNED
@ -66,6 +72,7 @@
#define JPEG_LE_PIXELS 16 #define JPEG_LE_PIXELS 16
#define JPEG_EXIF_THUMBNAIL 32 #define JPEG_EXIF_THUMBNAIL 32
#define JPEG_LUMA_ONLY 64 #define JPEG_LUMA_ONLY 64
#define JPEG_USES_DMA 128
#define MCU0 (DCTSIZE * 0) #define MCU0 (DCTSIZE * 0)
#define MCU1 (DCTSIZE * 1) #define MCU1 (DCTSIZE * 1)
@ -84,6 +91,13 @@ typedef uint32_t my_ulong;
typedef int32_t my_long; typedef int32_t my_long;
#endif #endif
// Supported decode modes
enum {
JPEG_MODE_BASELINE = 0,
JPEG_MODE_PROGRESSIVE,
JPEG_MODE_INVALID
};
// Pixel types (defaults to little endian RGB565) // Pixel types (defaults to little endian RGB565)
enum { enum {
RGB565_LITTLE_ENDIAN = 0, RGB565_LITTLE_ENDIAN = 0,
@ -107,7 +121,8 @@ enum {
JPEG_INVALID_PARAMETER, JPEG_INVALID_PARAMETER,
JPEG_DECODE_ERROR, JPEG_DECODE_ERROR,
JPEG_UNSUPPORTED_FEATURE, JPEG_UNSUPPORTED_FEATURE,
JPEG_INVALID_FILE JPEG_INVALID_FILE,
JPEG_ERROR_MEMORY
}; };
typedef struct buffered_bits typedef struct buffered_bits
@ -235,8 +250,11 @@ class JPEGDEC
{ {
public: public:
int openRAM(uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw); int openRAM(uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw);
int openFLASH(uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw); int openFLASH(const uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw);
int open(const char *szFilename, JPEG_OPEN_CALLBACK *pfnOpen, JPEG_CLOSE_CALLBACK *pfnClose, JPEG_READ_CALLBACK *pfnRead, JPEG_SEEK_CALLBACK *pfnSeek, JPEG_DRAW_CALLBACK *pfnDraw); int open(const char *szFilename, JPEG_OPEN_CALLBACK *pfnOpen, JPEG_CLOSE_CALLBACK *pfnClose, JPEG_READ_CALLBACK *pfnRead, JPEG_SEEK_CALLBACK *pfnSeek, JPEG_DRAW_CALLBACK *pfnDraw);
#ifdef __LINUX__
int open(const char *szFilename, JPEG_DRAW_CALLBACK *pfnDraw);
#endif
int open(void *fHandle, int iDataSize, JPEG_CLOSE_CALLBACK *pfnClose, JPEG_READ_CALLBACK *pfnRead, JPEG_SEEK_CALLBACK *pfnSeek, JPEG_DRAW_CALLBACK *pfnDraw); int open(void *fHandle, int iDataSize, JPEG_CLOSE_CALLBACK *pfnClose, JPEG_READ_CALLBACK *pfnRead, JPEG_SEEK_CALLBACK *pfnSeek, JPEG_DRAW_CALLBACK *pfnDraw);
void setFramebuffer(void *pFramebuffer); void setFramebuffer(void *pFramebuffer);
void setCropArea(int x, int y, int w, int h); void setCropArea(int x, int y, int w, int h);
@ -255,11 +273,13 @@ class JPEGDEC
int getBpp(); int getBpp();
void setUserPointer(void *p); void setUserPointer(void *p);
int getSubSample(); int getSubSample();
int getJPEGType();
int hasThumb(); int hasThumb();
int getThumbWidth(); int getThumbWidth();
int getThumbHeight(); int getThumbHeight();
int getLastError(); int getLastError();
void setPixelType(int iType); // defaults to little endian void setPixelType(int iType); // defaults to little endian
int getPixelType();
void setMaxOutputSize(int iMaxMCUs); void setMaxOutputSize(int iMaxMCUs);
private: private:

View File

@ -0,0 +1,46 @@
//
// JPEG Display helper class
//
// written by Larry Bank
// bitbank@pobox.com
//
// Copyright 2025 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.
//===========================================================================
//
#ifndef __JPEGDISPLAY__
#define __JPEGDISPLAY__
#include <JPEGDEC.h>
#include <bb_spi_lcd.h>
#include <SD.h>
#include "FS.h"
#include <LittleFS.h>
// To center one or both coordinates for the drawing position
// use this constant value
#define JPEGDISPLAY_CENTER -2
class JPEGDisplay
{
public:
int loadJPEG(BB_SPI_LCD *pLCD, int x, int y, const void *pData, int iDataSize, int iOptions = 0);
int loadJPEG(BB_SPI_LCD *pLCD, int x, int y, const char *fname, int iOptions = 0);
int loadJPEG_LFS(BB_SPI_LCD *pLCD, int x, int y, const char *fname, int iOptions = 0);
int getJPEGInfo(int *width, int *height, int *bpp, const void *pData, int iDataSize);
int getJPEGInfo(int *width, int *height, int *bpp, const char *fname);
int getJPEGInfo_LFS(int *width, int *height, int *bpp, const char *fname);
int getLastError() {return _iLastError;}
private:
int _iLastError;
};
#endif // __JPEGDISPLAY__

View File

@ -0,0 +1,277 @@
//
// JPEG Display helper class
//
// written by Larry Bank
// bitbank@pobox.com
//
// Copyright 2025 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.
//===========================================================================
//
#ifndef __JPEGDISPLAY_IMPL__
#define __JPEGDISPLAY_IMPL__
#include "JPEGDisplay.h"
static int JPEGDraw(JPEGDRAW *pDraw)
{
BB_SPI_LCD *pLCD = (BB_SPI_LCD *)pDraw->pUser;
pLCD->setAddrWindow(pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight);
pLCD->pushPixels((uint16_t *)pDraw->pPixels, pDraw->iWidth * pDraw->iHeight);
return 1;
} /* JPEGDraw() */
static void * jpegOpen(const char *filename, int32_t *size) {
static File myfile;
myfile = SD.open(filename);
*size = myfile.size();
return &myfile;
}
static void * jpegOpenLFS(const char *filename, int32_t *size) {
static File myfile;
myfile = LittleFS.open(filename, FILE_READ);
if (myfile) {
*size = myfile.size();
return &myfile;
} else {
return NULL;
}
}
static void jpegClose(void *handle) {
File *pFile = (File *)handle;
if (pFile) pFile->close();
}
static int32_t jpegRead(JPEGFILE *handle, uint8_t *buffer, int32_t length) {
File *pFile = (File *)handle->fHandle;
if (!pFile) return 0;
return pFile->read(buffer, length);
}
static int32_t jpegSeek(JPEGFILE *handle, int32_t position) {
File *pFile = (File *)handle->fHandle;
if (!pFile) return 0;
return pFile->seek(position);
}
int JPEGDisplay::loadJPEG(BB_SPI_LCD *pLCD, int x, int y, const void *pData, int iDataSize, int iOptions)
{
JPEGDEC *jpeg;
int w, h, rc;
jpeg = (JPEGDEC *)malloc(sizeof(JPEGDEC));
if (!jpeg) {
_iLastError = JPEG_ERROR_MEMORY;
return 0;
}
if (jpeg->openRAM((uint8_t *)pData, iDataSize, JPEGDraw)) {
jpeg->setPixelType(RGB565_BIG_ENDIAN);
w = jpeg->getWidth();
h = jpeg->getHeight();
if (x == JPEGDISPLAY_CENTER) {
x = (pLCD->width() - w)/2;
if (x < 0) x = 0;
} else if (x < 0 || w + x > pLCD->width()) {
_iLastError = JPEG_INVALID_PARAMETER;
free(jpeg);
return 0; // clipping not supported
}
if (y == JPEGDISPLAY_CENTER) {
y = (pLCD->height() - h)/2;
if (y < 0) y = 0;
} else if (y < 0 || y + h > pLCD->height()) {
// clipping is not supported
_iLastError = JPEG_INVALID_PARAMETER;
free(jpeg);
return 0;
}
jpeg->setUserPointer((void *)pLCD);
jpeg->decode(x, y, iOptions); // perform decoding
jpeg->close();
free(jpeg);
_iLastError = JPEG_SUCCESS;
return 1;
}
_iLastError = jpeg->getLastError();
free(jpeg);
return 0;
} /* loadJPEG() */
int JPEGDisplay::loadJPEG(BB_SPI_LCD *pLCD, int x, int y, const char *fname, int iOptions)
{
JPEGDEC *jpeg;
int w, h, rc;
jpeg = (JPEGDEC *)malloc(sizeof(JPEGDEC));
if (!jpeg) {
_iLastError = JPEG_ERROR_MEMORY;
return 0;
}
if (jpeg->open(fname, jpegOpen, jpegClose, jpegRead, jpegSeek, JPEGDraw)) {
jpeg->setPixelType(RGB565_BIG_ENDIAN);
w = jpeg->getWidth();
h = jpeg->getHeight();
if (x == JPEGDISPLAY_CENTER) {
x = (pLCD->width() - w)/2;
if (x < 0) x = 0;
} else if (x < 0 || w + x > pLCD->width()) {
_iLastError = JPEG_INVALID_PARAMETER;
free(jpeg);
return 0; // clipping not supported
}
if (y == JPEGDISPLAY_CENTER) {
y = (pLCD->height() - h)/2;
if (y < 0) y = 0;
} else if (y < 0 || y + h > pLCD->height()) {
// clipping is not supported
_iLastError = JPEG_INVALID_PARAMETER;
free(jpeg);
return 0;
}
jpeg->setUserPointer((void *)pLCD);
jpeg->decode(x, y, iOptions); // decode the image
jpeg->close();
free(jpeg);
_iLastError = JPEG_SUCCESS;
return 1;
}
_iLastError = jpeg->getLastError();
free(jpeg);
return 0;
} /* loadJPEG() */
int JPEGDisplay::loadJPEG_LFS(BB_SPI_LCD *pLCD, int x, int y, const char *fname, int iOptions)
{
JPEGDEC *jpeg;
int w, h, rc;
if (!LittleFS.begin(false)) {
return 0;
}
jpeg = (JPEGDEC *)malloc(sizeof(JPEGDEC));
if (!jpeg) {
_iLastError = JPEG_ERROR_MEMORY;
return 0;
}
if (jpeg->open(fname, jpegOpenLFS, jpegClose, jpegRead, jpegSeek, JPEGDraw)) {
jpeg->setPixelType(RGB565_BIG_ENDIAN);
w = jpeg->getWidth();
h = jpeg->getHeight();
if (x == JPEGDISPLAY_CENTER) {
x = (pLCD->width() - w)/2;
if (x < 0) x = 0;
} else if (x < 0 || w + x > pLCD->width()) {
jpeg->close();
free(jpeg);
_iLastError = JPEG_INVALID_PARAMETER;
return 0; // clipping not supported
}
if (y == JPEGDISPLAY_CENTER) {
y = (pLCD->height() - h)/2;
if (y < 0) y = 0;
} else if (y < 0 || y + h > pLCD->height()) {
// clipping is not supported
jpeg->close();
free(jpeg);
_iLastError = JPEG_INVALID_PARAMETER;
return 0;
}
jpeg->setUserPointer((void *)pLCD);
jpeg->decode(x, y, iOptions); // decode the image
jpeg->close();
_iLastError = JPEG_SUCCESS;
free(jpeg);
return 1;
} else {
// Serial.printf("jpeg->open failed with code: %d\n", jpeg->getLastError());
}
_iLastError = jpeg->getLastError();
free(jpeg);
return 0;
} /* loadJPEG_LFS() */
int JPEGDisplay::getJPEGInfo(int *width, int *height, int *bpp, const void *pData, int iDataSize)
{
JPEGDEC *jpeg;
int rc;
if (!width || !height || !bpp || !pData || iDataSize < 32) return 0;
jpeg = (JPEGDEC *)malloc(sizeof(JPEGDEC));
if (!jpeg) {
_iLastError = JPEG_ERROR_MEMORY;
return 0;
}
if (jpeg->openRAM((uint8_t *)pData, iDataSize, JPEGDraw)) {
*width = jpeg->getWidth();
*height = jpeg->getHeight();
*bpp = jpeg->getBpp();
free(jpeg);
_iLastError = JPEG_SUCCESS;
return 1;
}
_iLastError = jpeg->getLastError();
free(jpeg);
return 0;
} /* getJPEGInfo() */
int JPEGDisplay::getJPEGInfo(int *width, int *height, int *bpp, const char *fname)
{
JPEGDEC *jpeg;
int rc;
if (!width || !height || !bpp || !fname) return 0;
jpeg = (JPEGDEC *)malloc(sizeof(JPEGDEC));
if (!jpeg) {
_iLastError = JPEG_ERROR_MEMORY;
return 0;
}
if (jpeg->open(fname, jpegOpen, jpegClose, jpegRead, jpegSeek, JPEGDraw)) {
*width = jpeg->getWidth();
*height = jpeg->getHeight();
*bpp = jpeg->getBpp();
jpeg->close();
free(jpeg);
_iLastError = JPEG_SUCCESS;
return 1;
}
_iLastError = jpeg->getLastError();
free(jpeg);
return 0;
} /* getJPEGInfo() */
int JPEGDisplay::getJPEGInfo_LFS(int *width, int *height, int *bpp, const char *fname)
{
JPEGDEC *jpeg;
int rc;
if (!LittleFS.begin(false)) {
return 0;
}
if (!width || !height || !bpp || !fname) return 0;
jpeg = (JPEGDEC *)malloc(sizeof(JPEGDEC));
if (!jpeg) {
_iLastError = JPEG_ERROR_MEMORY;
return 0;
}
if (jpeg->open(fname, jpegOpenLFS, jpegClose, jpegRead, jpegSeek, JPEGDraw)) {
*width = jpeg->getWidth();
*height = jpeg->getHeight();
*bpp = jpeg->getBpp();
jpeg->close();
free(jpeg);
_iLastError = JPEG_SUCCESS;
return 1;
}
_iLastError = jpeg->getLastError();
free(jpeg);
return 0;
} /* getJPEGInfo_LFS() */
#endif // __JPEGDISPLAY_IMPL__

View File

@ -32,6 +32,7 @@
#endif #endif
#if defined (ARDUINO_ARCH_ESP32) && !defined(NO_SIMD) #if defined (ARDUINO_ARCH_ESP32) && !defined(NO_SIMD)
#if __has_include ("dsps_fft2r_platform.h")
#include "dsps_fft2r_platform.h" #include "dsps_fft2r_platform.h"
#if (dsps_fft2r_sc16_aes3_enabled == 1) #if (dsps_fft2r_sc16_aes3_enabled == 1)
#define ESP32S3_SIMD #define ESP32S3_SIMD
@ -42,6 +43,7 @@ void s3_dequant(int16_t *pMCU, int16_t *pQuant);
} }
int16_t i16_Consts[8] = {0x80, 113, 90, 22, 46, 1,32,2048}; int16_t i16_Consts[8] = {0x80, 113, 90, 22, 46, 1,32,2048};
#endif // S3 SIMD #endif // S3 SIMD
#endif // __has_include
#endif // ESP32 #endif // ESP32
#if defined( __x86_64__ ) && !defined(NO_SIMD) #if defined( __x86_64__ ) && !defined(NO_SIMD)
@ -64,13 +66,14 @@ static void JPEGGetMoreData(JPEGIMAGE *pPage);
static int DecodeJPEG(JPEGIMAGE *pImage); static int DecodeJPEG(JPEGIMAGE *pImage);
static int32_t readRAM(JPEGFILE *pFile, uint8_t *pBuf, int32_t iLen); static int32_t readRAM(JPEGFILE *pFile, uint8_t *pBuf, int32_t iLen);
static int32_t seekMem(JPEGFILE *pFile, int32_t iPosition); static int32_t seekMem(JPEGFILE *pFile, int32_t iPosition);
#if defined (__MACH__) || defined( __LINUX__ ) || defined( __MCUXPRESSO ) #if defined (__MACH__) || defined( __LINUX__ ) || defined( __MCUXPRESSO ) || defined(_WIN64)
static int32_t readFile(JPEGFILE *pFile, uint8_t *pBuf, int32_t iLen); static int32_t readFile(JPEGFILE *pFile, uint8_t *pBuf, int32_t iLen);
static int32_t seekFile(JPEGFILE *pFile, int32_t iPosition); static int32_t seekFile(JPEGFILE *pFile, int32_t iPosition);
static void closeFile(void *handle); static void closeFile(void *handle);
#endif #endif
static void JPEGDither(JPEGIMAGE *pJPEG, int iWidth, int iHeight); static void JPEGDither(JPEGIMAGE *pJPEG, int iWidth, int iHeight);
/* JPEG tables */ /* JPEG tables */
const int iBitMasks[33] = {0,1,3,7,0xf,0x1f,0x3f,0x7f,0xff,0x1ff,0x3ff,0x7ff,0x0fff,0x1fff,0x3fff,0x7fff,0xffff,0x1ffff,0x3ffff,0x7ffff,0xfffff,0x1fffff,0x3fffff,0x7fffff,0xffffff,0x1ffffff,0x3ffffff,0x7ffffff,0xfffffff,0x1fffffff,0x3fffffff,0x7fffffff,1};
// zigzag ordering of DCT coefficients // zigzag ordering of DCT coefficients
static const unsigned char cZigZag[64] = {0,1,5,6,14,15,27,28, static const unsigned char cZigZag[64] = {0,1,5,6,14,15,27,28,
2,4,7,13,16,26,29,42, 2,4,7,13,16,26,29,42,
@ -550,7 +553,7 @@ static const uint16_t usRangeTableB[] = {0x0000,0x0000,0x0000,0x0000,0x0000,0x00
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
#if defined (__MACH__) || defined( __LINUX__ ) || defined( __MCUXPRESSO ) #if defined (__MACH__) || defined( __LINUX__ ) || defined( __MCUXPRESSO ) || defined(_WIN64)
// //
// API for C // API for C
// //
@ -773,7 +776,7 @@ static int32_t seekMem(JPEGFILE *pFile, int32_t iPosition)
return iPosition; return iPosition;
} /* seekMem() */ } /* seekMem() */
#if defined (__MACH__) || defined( __LINUX__ ) || defined( __MCUXPRESSO ) #if defined (__MACH__) || defined( __LINUX__ ) || defined( __MCUXPRESSO ) || defined(_WIN64)
static void closeFile(void *handle) static void closeFile(void *handle)
{ {
@ -789,6 +792,17 @@ static int32_t seekFile(JPEGFILE *pFile, int32_t iPosition)
return iPosition; return iPosition;
} /* seekFile() */ } /* seekFile() */
static void * openFile(const char *szFilename, int32_t *pFileSize)
{
FILE *f;
f = fopen(szFilename, "r+b");
if (!f) return NULL;
fseek(f, 0, SEEK_END);
*pFileSize = (int32_t)ftell(f);
fseek(f, 0, SEEK_SET);
return (void *)f;
} /* openFile() */
static int32_t readFile(JPEGFILE *pFile, uint8_t *pBuf, int32_t iLen) static int32_t readFile(JPEGFILE *pFile, uint8_t *pBuf, int32_t iLen)
{ {
int32_t iBytesRead; int32_t iBytesRead;
@ -1171,7 +1185,7 @@ static int JPEGMakeHuffTables(JPEGIMAGE *pJPEG, int bThumbnail)
pBits = &pHuffVals[(iTable+4) * HUFF_TABLEN]; pBits = &pHuffVals[(iTable+4) * HUFF_TABLEN];
p = pBits; p = pBits;
p += 16; // point to bit data p += 16; // point to bit data
if (iTable * HUFF11SIZE >= sizeof(pJPEG->usHuffAC) / 2) if (iTable * HUFF11SIZE >= (int)sizeof(pJPEG->usHuffAC) / 2)
return 0; return 0;
pShort = &pJPEG->usHuffAC[iTable*HUFF11SIZE]; pShort = &pJPEG->usHuffAC[iTable*HUFF11SIZE];
pLong = &pJPEG->usHuffAC[iTable*HUFF11SIZE + 1024]; pLong = &pJPEG->usHuffAC[iTable*HUFF11SIZE + 1024];
@ -1605,6 +1619,10 @@ static int JPEGParseInfo(JPEGIMAGE *pPage, int bExtractThumb)
(*pPage->pfnSeek)(&pPage->JPEGFile, iFilePos); (*pPage->pfnSeek)(&pPage->JPEGFile, iFilePos);
iBytesRead = 0; // throw away any old data iBytesRead = 0; // throw away any old data
} }
if (iOffset > iBytesRead) { // something went wrong
pPage->iError = JPEG_DECODE_ERROR;
return 0;
}
// move existing bytes down // move existing bytes down
if (iOffset) if (iOffset)
{ {
@ -1627,9 +1645,8 @@ static int JPEGParseInfo(JPEGIMAGE *pPage, int bExtractThumb)
} }
switch (usMarker) switch (usMarker)
{ {
case 0xffc1: case 0xffc1: // extended mode
case 0xffc2: case 0xffc3: // lossless mode
case 0xffc3:
pPage->iError = JPEG_UNSUPPORTED_FEATURE; pPage->iError = JPEG_UNSUPPORTED_FEATURE;
return 0; // currently unsupported modes return 0; // currently unsupported modes
@ -1649,7 +1666,7 @@ static int JPEGParseInfo(JPEGIMAGE *pPage, int bExtractThumb)
// point to next IFD // point to next IFD
IFD += (12 * iTagCount) + 2; IFD += (12 * iTagCount) + 2;
IFD = TIFFLONG(&s[IFD + iOffset + 8], bMotorola); IFD = TIFFLONG(&s[IFD + iOffset + 8], bMotorola);
if (IFD != 0) // Thumbnail present? if (IFD != 0 && IFD + iOffset + 8 < JPEG_FILE_BUF_SIZE) // Thumbnail present?
{ {
pPage->ucHasThumb = 1; pPage->ucHasThumb = 1;
GetTIFFInfo(pPage, bMotorola, IFD+iOffset+8); // info for second 'page' of TIFF GetTIFFInfo(pPage, bMotorola, IFD+iOffset+8); // info for second 'page' of TIFF
@ -1658,7 +1675,8 @@ static int JPEGParseInfo(JPEGIMAGE *pPage, int bExtractThumb)
} }
} }
break; break;
case 0xffc0: // SOFx - start of frame case 0xffc0: // SOFx - start of frame (baseline)
case 0xffc2: // (progressive)
pPage->ucMode = (uint8_t)usMarker; pPage->ucMode = (uint8_t)usMarker;
pPage->ucBpp = s[iOffset+2]; // bits per sample pPage->ucBpp = s[iOffset+2]; // bits per sample
pPage->iCropX = pPage->iCropY = 0; // initialize crop rectangle to full image size pPage->iCropX = pPage->iCropY = 0; // initialize crop rectangle to full image size
@ -1666,9 +1684,6 @@ static int JPEGParseInfo(JPEGIMAGE *pPage, int bExtractThumb)
pPage->iCropCX = pPage->iWidth = MOTOSHORT(&s[iOffset+5]); pPage->iCropCX = pPage->iWidth = MOTOSHORT(&s[iOffset+5]);
pPage->ucNumComponents = s[iOffset+7]; pPage->ucNumComponents = s[iOffset+7];
pPage->ucBpp = pPage->ucBpp * pPage->ucNumComponents; /* Bpp = number of components * bits per sample */ pPage->ucBpp = pPage->ucBpp * pPage->ucNumComponents; /* Bpp = number of components * bits per sample */
if (pPage->ucNumComponents == 1)
pPage->ucSubSample = 0; // use this to differentiate from color 1:1
else
{ {
usLen -= 8; usLen -= 8;
iOffset += 8; iOffset += 8;
@ -1692,6 +1707,9 @@ static int JPEGParseInfo(JPEGIMAGE *pPage, int bExtractThumb)
usLen -= 3; usLen -= 3;
} }
} }
if (pPage->ucNumComponents == 1) {
pPage->ucSubSample = 0; // use this to differentiate from color 1:1
}
break; break;
case 0xffdd: // Restart Interval case 0xffdd: // Restart Interval
if (usLen == 4) if (usLen == 4)
@ -1744,11 +1762,11 @@ static int JPEGParseInfo(JPEGIMAGE *pPage, int bExtractThumb)
} // while } // while
if (usMarker == 0xffda) // start of image if (usMarker == 0xffda) // start of image
{ {
if (pPage->ucBpp != 8) // need to match up table IDs // if (pPage->ucBpp != 8) // need to match up table IDs
{ // {
iOffset -= usLen; iOffset -= usLen;
JPEGGetSOS(pPage, &iOffset); // get Start-Of-Scan info for decoding JPEGGetSOS(pPage, &iOffset); // get Start-Of-Scan info for decoding
} // }
if (!JPEGMakeHuffTables(pPage, 0)) //int bThumbnail) DEBUG if (!JPEGMakeHuffTables(pPage, 0)) //int bThumbnail) DEBUG
{ {
pPage->iError = JPEG_UNSUPPORTED_FEATURE; pPage->iError = JPEG_UNSUPPORTED_FEATURE;
@ -1790,6 +1808,279 @@ static void JPEGFixQuantD(JPEGIMAGE *pJPEG)
} }
} }
} /* JPEGFixQuantD() */ } /* JPEGFixQuantD() */
/****************************************************************************
* *
* FUNCTION : JPEGDecodeMCU_P(char *, int *, int *, int *, JPEGDATA *) *
* *
* PURPOSE : Decompress a macro block of Progressive JPEG data. *
* *
****************************************************************************/
static int JPEGDecodeMCU_P(JPEGIMAGE *pJPEG, int iMCU, int *iDCPredictor)
{
int iCount;
int iIndex;
unsigned char ucHuff, *pFastDC;
unsigned short *pFast;
uint32_t usHuff; // this prevents an unnecessary & 65535 for shorts
signed int iPositive, iNegative, iCoeff;
signed short *pMCU = &pJPEG->sMCUs[iMCU & 0xffffff];
uint32_t ulBitOff;
my_ulong ulCode, ulBits, ulTemp; // local copies to allow compiler to use register vars
uint8_t *pBuf;
ulBitOff = pJPEG->bb.ulBitOff;
ulBits = pJPEG->bb.ulBits;
pBuf = pJPEG->bb.pBuf;
if (ulBitOff > (REGISTER_WIDTH-17)) { // need to get more data
pBuf += (ulBitOff >> 3);
ulBitOff &= 7;
ulBits = MOTOLONG(pBuf);
}
iPositive = (1 << pJPEG->cApproxBitsLow); // positive bit position being coded
iNegative = ((-1) << pJPEG->cApproxBitsLow); // negative bit position being coded
if (pJPEG->iScanStart == 0)
{
if (pJPEG->cApproxBitsHigh) // successive approximation - simply encodes the specified bit
{
ulCode = (ulBits >> (31-ulBitOff)) & 1; // just get 1 bit
ulBitOff += 1;
if (ulCode)
{
// (*iDCPredictor) |= iPositive; // in case the scan is run more than once
// pMCU[0] = *iDCPredictor; // store in MCU[0]
pMCU[0] |= iPositive;
}
goto mcu_done; // that's it
}
// get the DC component
ulCode = (ulBits >> (REGISTER_WIDTH - 12 - ulBitOff)) & 0xfff; // get as lower 12 bits
if (ulCode >= 0xf80) // long code
ulCode = (ulCode & 0xff); // point to long table
else
ulCode >>= 6; // use first 6 bits of short code
pFastDC = &pJPEG->ucHuffDC[pJPEG->ucDCTable * DC_TABLE_SIZE];
ucHuff = pFastDC[ulCode]; // get the length+code
if (ucHuff == 0) // invalid code
return -1;
ulBitOff += (ucHuff >> 4); // add the Huffman length
ucHuff &= 0xf; // get the actual code (SSSS)
if (ucHuff) // if there is a change to the DC value
{ // get the 'extra' bits
if (ulBitOff > (REGISTER_WIDTH - 17)) // need to get more data
{
pBuf += (ulBitOff >> 3);
ulBitOff &= 7;
ulBits = MOTOLONG(pBuf);
}
ulCode = ulBits << ulBitOff;
ulTemp = ~(my_ulong)(((my_long)ulCode)>>(REGISTER_WIDTH-1)); // slide sign bit across other 63/31 bits
ulCode >>= (REGISTER_WIDTH - ucHuff);
ulCode -= ulTemp>>(REGISTER_WIDTH-ucHuff);
ulBitOff += ucHuff; // add bit length
ulCode <<= pJPEG->cApproxBitsLow; // successive approximation shift value
(*iDCPredictor) += ulCode;
}
pMCU[0] = (short)*iDCPredictor; // store in MCU[0]
}
// Now get the other 63 AC coefficients
pFast = &pJPEG->usHuffAC[pJPEG->ucACTable * HUFF11SIZE];
if (pJPEG->iScanStart)
iIndex = pJPEG->iScanStart; // starting index of this scan (progressive JPEG)
else
iIndex = 1; // special case when the DC component is included
if (pJPEG->cApproxBitsHigh) // successive approximation - different method
{
if (1)
// if (*iSkip == 0) // only decode this block if not being skipped in EOB run
{
for (; iIndex <= pJPEG->iScanEnd; iIndex++)
{
if (ulBitOff > (REGISTER_WIDTH-17)) { // need to get more data
pBuf += (ulBitOff >> 3);
ulBitOff &= 7;
ulBits = MOTOLONG(pBuf);
}
ulCode = (ulBits >> (REGISTER_WIDTH - 16 - ulBitOff)) & 0xffff; // get as lower 16 bits
if (ulCode >= 0xf000) // first 4 bits = 1, use long table
ulCode = (ulCode & 0x1fff);
else
ulCode >>= 4; // use lower 12 bits (short table)
usHuff = pFast[ulCode];
if (usHuff == 0) // invalid code
return -1;
ulBitOff += (usHuff >> 8); // add length
usHuff &= 0xff; // get code (RRRR/SSSS)
iCoeff = 0;
if (usHuff & 0xf)
{
if ((usHuff & 0xf) != 1) // size of new coefficient should always be one
return -1;
ulCode = (ulBits >> (REGISTER_WIDTH-1-ulBitOff)) & 1; // just get 1 bit
ulBitOff += 1;
if (ulCode) // 1 means use positive value; 0 = use negative
iCoeff = iPositive;
else
iCoeff = iNegative;
}
else // since SSSS = 0, must be a ZRL or EOBn code
{
if (usHuff != 0xf0) // ZRL
{ // EOBn code
usHuff = (usHuff >> 4); // get the number of extra bits needed to code the count
ulCode = ulBits >> (REGISTER_WIDTH - usHuff - ulBitOff); // shift down by (SSSS) - extra length
ulCode &= iBitMasks[usHuff];
ulCode += (1 << usHuff); // plus base amount
ulBitOff += usHuff; // add extra length
//*iSkip = ulCode; // return this skip amount
break;
}
}
// Advance over already-nonzero coefficients and RRRR still-zero coefficients
// appending correction bits to the nonzeroes. A correction bit is 1 if the abs
// value of the coefficient must be increased.
iCount = (usHuff >> 4); // get RRRR in lower 4 bits
do {
if (pMCU[iIndex])
{
if (ulBitOff > (REGISTER_WIDTH-17)) { // need to get more data
pBuf += (ulBitOff >> 3);
ulBitOff &= 7;
ulBits = MOTOLONG(pBuf);
}
ulCode = (ulBits >> (REGISTER_WIDTH-1-ulBitOff)) & 1; // just get 1 bit
ulBitOff++;
if (ulCode)
{
if ((pMCU[iIndex] & iPositive) == 0) // only combine if not already done
{
if (pMCU[iIndex] >= 0)
pMCU[iIndex] += (short)iPositive;
else
pMCU[iIndex] += (short)iNegative;
}
}
}
else // count the zero coeffs to skip
{
if (--iCount < 0)
break; // done skipping zeros
}
iIndex++;
} while (iIndex <= pJPEG->iScanEnd);
if (iCoeff && iIndex < 0x40) // store the non-zero coefficient
pMCU[iIndex] = (short) iCoeff;
} // for - AC coeffs
} // if not skipped
if (0)
// if (*iSkip) // scan any remaining coefficient positions after the end-of-band
{
for (; iIndex <= pJPEG->iScanEnd; iIndex++)
{
if (pMCU[iIndex]) // only non-zero ones need correction
{
if (ulBitOff > 15) // need to grab more bytes to nibble on
{
pBuf += 2; // grab 2 more bytes since that's what we really need
ulBitOff -= 16;
ulBits <<= 16;
ulBits |= MOTOSHORT(&pBuf[2]);
}
ulCode = ulBits >> (REGISTER_WIDTH - 1 - ulBitOff); // get 1 bit
ulBitOff++;
if (ulCode & 1) // correction bit
{
if ((pMCU[iIndex] & iPositive) == 0) // only combine if not already done
{
if (pMCU[iIndex] >= 0)
pMCU[iIndex] += (short)iPositive;
else
pMCU[iIndex] += (short)iNegative;
}
} // if correction bit
} // if coeff is non-zero
} // for the rest of the AC coefficients
// (*iSkip)--; // count this block as completed
} // if this block is being skipped
} // if successive approx
else // normal AC decoding
{
// if (*iSkip == 0) // if this block is not being skipped in a EOB run
{
while (iIndex <= pJPEG->iScanEnd)
{
if (ulBitOff > 15) // need to grab more bytes to nibble on
{
pBuf += 2; // grab 2 more bytes since that's what we really need
ulBitOff -= 16;
ulBits <<= 16;
ulBits |= MOTOSHORT(&pBuf[2]);
}
ulCode = (ulBits >> (REGISTER_WIDTH - 16 - ulBitOff)) & 0xffff; // get as lower 16 bits
if (ulCode >= 0xf000) // first 4 bits = 1, use long table
ulCode = (ulCode & 0x1fff);
else
ulCode >>= 4; // use lower 12 bits (short table)
usHuff = pFast[ulCode];
if (usHuff == 0) // invalid code
return -1;
ulBitOff += (usHuff >> 8); // add length
usHuff &= 0xff; // get code (RRRR/SSSS)
// if (usHuff == 0) // no more AC components
// {
// goto mcu_done;
// }
if (usHuff == 0xf0) // is it ZRL?
{
iIndex += 16; // skip 16 AC coefficients
}
else
{
if (ulBitOff > 15)
{
pBuf += 2; // grab 2 more bytes since that's what we really need
ulBitOff -= 16;
ulBits <<= 16;
ulBits |= MOTOSHORT(&pBuf[2]);
}
if ((usHuff & 0xf) == 0) // special case for encoding EOB (end-of-band) codes (SSSS=0)
{
usHuff = (usHuff >> 4); // get the number of extra bits needed to code the count
ulCode = ulBits >> (REGISTER_WIDTH - usHuff - ulBitOff); // shift down by (SSSS) - extra length
ulCode &= iBitMasks[usHuff];
ulCode += (1 << usHuff); // plus base amount
ulBitOff += usHuff; // add extra length
// *iSkip = ulCode; // return this skip amount
break;
}
else
{
iIndex += (usHuff >> 4); // skip amount
usHuff &= 0xf; // get (SSSS) - extra length
ulCode = ulBits << ulBitOff;
ulCode >>= (32 - usHuff);
if (!(ulCode & 0x80000000>>(REGISTER_WIDTH - 16 - -usHuff))) // test for negative
ulCode -= 0xffffffff>>(REGISTER_WIDTH - 16 - -usHuff);
ulBitOff += usHuff; // add (SSSS) extra length
ulCode <<= pJPEG->cApproxBitsLow; // successive approximation shift value
pMCU[iIndex++] = (signed short)ulCode; // store AC coefficient
}
}
} // while
} // if this block not skipped
// if (*iSkip)
// (*iSkip)--; // count this block as being completed (or skipped)
} // end of non-successive approx code
mcu_done:
pJPEG->bb.pBuf = pBuf;
pJPEG->iVLCOff = (int)(pBuf - pJPEG->ucFileBuf);
pJPEG->bb.ulBitOff = ulBitOff;
pJPEG->bb.ulBits = ulBits;
return 0;
} /* JPEGDecodeMCU_P() */
// //
// Decode the DC and 2-63 AC coefficients of the current DCT block // Decode the DC and 2-63 AC coefficients of the current DCT block
// For 1/4 and 1/8 scaled images, we don't store most of the AC values since we // For 1/4 and 1/8 scaled images, we don't store most of the AC values since we
@ -1873,6 +2164,9 @@ static int JPEGDecodeMCU(JPEGIMAGE *pJPEG, int iMCU, int *iDCPredictor)
} }
if (pJPEG->ucACTable > 1) // unsupported if (pJPEG->ucACTable > 1) // unsupported
return -1; return -1;
if (pJPEG->iScanEnd == 0) { // first scan of progressive has only DC values
return 0; // we're done
}
// Now get the other 63 AC coefficients // Now get the other 63 AC coefficients
pFast = &pJPEG->usHuffAC[pJPEG->ucACTable * HUFF11SIZE]; pFast = &pJPEG->usHuffAC[pJPEG->ucACTable * HUFF11SIZE];
if (pJPEG->b11Bit) // 11-bit "slow" tables used if (pJPEG->b11Bit) // 11-bit "slow" tables used
@ -1983,8 +2277,6 @@ mcu_done:
static void JPEGIDCT(JPEGIMAGE *pJPEG, int iMCUOffset, int iQuantTable) static void JPEGIDCT(JPEGIMAGE *pJPEG, int iMCUOffset, int iQuantTable)
{ {
int iRow; int iRow;
unsigned char ucColMask;
int iCol;
signed int tmp6,tmp7,tmp10,tmp11,tmp12,tmp13; signed int tmp6,tmp7,tmp10,tmp11,tmp12,tmp13;
signed int z5,z10,z11,z12,z13; signed int z5,z10,z11,z12,z13;
signed int tmp0,tmp1,tmp2,tmp3,tmp4,tmp5; signed int tmp0,tmp1,tmp2,tmp3,tmp4,tmp5;
@ -2260,7 +2552,7 @@ int16x8_t mmxZ5, mmxZ10, mmxZ11, mmxZ12, mmxZ13;
#if !defined (HAS_SSE) && !defined(HAS_NEON) #if !defined (HAS_SSE) && !defined(HAS_NEON)
// do columns first // do columns first
u16MCUFlags |= 1; // column 0 must always be calculated u16MCUFlags |= 1; // column 0 must always be calculated
for (iCol = 0; iCol < 8 && u16MCUFlags; iCol++) for (int iCol = 0; iCol < 8 && u16MCUFlags; iCol++)
{ {
if (u16MCUFlags & (1<<iCol)) // column has data in it if (u16MCUFlags & (1<<iCol)) // column has data in it
{ {
@ -2471,7 +2763,7 @@ int16x8_t mmxZ5, mmxZ10, mmxZ11, mmxZ12, mmxZ13;
#ifdef HAS_NEON #ifdef HAS_NEON
{ {
int16x4_t L_in_16x4, R_in_16x4, L_out, R_out; int16x4_t L_in_16x4, R_in_16x4, L_out, R_out;
int8x8_t LR_out_8x8; uint8x8_t LR_out_8x8;
int16x8_t LR_out; int16x8_t LR_out;
L_in_16x4 = vdup_n_s16(tmp0); // suppresses warning of setting lane 0 of uninitialized var L_in_16x4 = vdup_n_s16(tmp0); // suppresses warning of setting lane 0 of uninitialized var
L_in_16x4 = vset_lane_s16(tmp1, L_in_16x4, 1); L_in_16x4 = vset_lane_s16(tmp1, L_in_16x4, 1);
@ -2508,11 +2800,12 @@ static void JPEGPutMCU8BitGray(JPEGIMAGE *pJPEG, int x, int iPitch)
int i, j, xcount, ycount; int i, j, xcount, ycount;
uint8_t *pDest, *pSrc = (uint8_t *)&pJPEG->sMCUs[0]; uint8_t *pDest, *pSrc = (uint8_t *)&pJPEG->sMCUs[0];
if (pJPEG->pDitherBuffer) if (pJPEG->pDitherBuffer) {
pDest = &pJPEG->pDitherBuffer[x]; pDest = &pJPEG->pDitherBuffer[x];
else } else {
pDest = (uint8_t *)&pJPEG->usPixels[x/2]; pDest = (uint8_t *)&pJPEG->usPixels[0];
pDest += x;
}
if (pJPEG->ucSubSample <= 0x11) // single Y if (pJPEG->ucSubSample <= 0x11) // single Y
{ {
if (pJPEG->iOptions & JPEG_SCALE_HALF) // special handling of 1/2 size (pixel averaging) if (pJPEG->iOptions & JPEG_SCALE_HALF) // special handling of 1/2 size (pixel averaging)
@ -2704,8 +2997,8 @@ static void JPEGPutMCU8BitGray(JPEGIMAGE *pJPEG, int x, int iPitch)
return; return;
} }
#ifdef ALLOWS_UNALIGNED #ifdef ALLOWS_UNALIGNED
for (i=0; i<8; i++) if (x + 16 <= iPitch) { // no cropping needed
{ for (i=0; i<8; i++) {
*(uint32_t *)pDest = *(uint32_t *)pSrc; // Y0 *(uint32_t *)pDest = *(uint32_t *)pSrc; // Y0
*(uint32_t *)&pDest[4] = *(uint32_t *)&pSrc[4]; // Y0 *(uint32_t *)&pDest[4] = *(uint32_t *)&pSrc[4]; // Y0
*(uint32_t *)&pDest[8] = *(uint32_t *)&pSrc[128]; // Y1 *(uint32_t *)&pDest[8] = *(uint32_t *)&pSrc[128]; // Y1
@ -2717,27 +3010,33 @@ static void JPEGPutMCU8BitGray(JPEGIMAGE *pJPEG, int x, int iPitch)
pSrc += 8; pSrc += 8;
pDest += iPitch; pDest += iPitch;
} }
#else return;
}
#endif
xcount = iPitch - x;
for (i=0; i<8; i++) for (i=0; i<8; i++)
{ {
for (j=0; j<8; j++) for (j=0; j<8; j++)
{ {
if (j < xcount) {
pDest[j] = pSrc[j]; // Y0 pDest[j] = pSrc[j]; // Y0
pDest[j+8] = pSrc[j+128]; // Y1
pDest[iPitch*8 + j] = pSrc[j+256]; // Y2 pDest[iPitch*8 + j] = pSrc[j+256]; // Y2
}
if (j+8 < xcount) {
pDest[j+8] = pSrc[j+128]; // Y1
pDest[iPitch*8 + j + 8] = pSrc[j + 384]; // Y3 pDest[iPitch*8 + j + 8] = pSrc[j + 384]; // Y3
} }
}
pSrc += 8; pSrc += 8;
pDest += iPitch; pDest += iPitch;
} }
#endif
} // 0x22 } // 0x22
} /* JPEGMPutMCU8BitGray() */ } /* JPEGMPutMCU8BitGray() */
static void JPEGPutMCUGray(JPEGIMAGE *pJPEG, int x, int iPitch) static void JPEGPutMCUGray(JPEGIMAGE *pJPEG, int x, int iPitch)
{ {
uint16_t *usDest = (uint16_t *)&pJPEG->usPixels[x]; uint16_t *usDest = (uint16_t *)&pJPEG->usPixels[x];
int i, j, xcount, ycount; int i, j, xcount, ycount, delta;
uint8_t *pSrc = (uint8_t *)&pJPEG->sMCUs[0]; uint8_t *pSrc = (uint8_t *)&pJPEG->sMCUs[0];
if (pJPEG->iOptions & JPEG_SCALE_HALF) // special handling of 1/2 size (pixel averaging) if (pJPEG->iOptions & JPEG_SCALE_HALF) // special handling of 1/2 size (pixel averaging)
@ -2769,10 +3068,17 @@ static void JPEGPutMCUGray(JPEGIMAGE *pJPEG, int x, int iPitch)
return; return;
} }
xcount = ycount = 8; // debug xcount = ycount = 8; // debug
if (pJPEG->iOptions & JPEG_SCALE_QUARTER) delta = 0;
if (pJPEG->iOptions & JPEG_SCALE_QUARTER) {
xcount = ycount = 2; xcount = ycount = 2;
else if (pJPEG->iOptions & JPEG_SCALE_EIGHTH) } else if (pJPEG->iOptions & JPEG_SCALE_EIGHTH) {
xcount = ycount = 1; xcount = ycount = 1;
} else {
if (x + 8 > iPitch) {
xcount = iPitch - x; // final block is partial width
delta = 8 - xcount;
}
}
for (i=0; i<ycount; i++) // do up to 8 rows for (i=0; i<ycount; i++) // do up to 8 rows
{ {
if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN)
@ -2787,6 +3093,7 @@ static void JPEGPutMCUGray(JPEGIMAGE *pJPEG, int x, int iPitch)
} }
usDest -= xcount; usDest -= xcount;
usDest += iPitch; // next line usDest += iPitch; // next line
pSrc += delta;
} }
} /* JPEGPutMCUGray() */ } /* JPEGPutMCUGray() */
@ -2855,7 +3162,7 @@ static void JPEGPixelRGB(uint32_t *pDest, int iY, int iCb, int iCr)
i32 = ((iCBB + iY) >> 12); i32 = ((iCBB + iY) >> 12);
if (i32 < 0) i32 = 0; if (i32 < 0) i32 = 0;
else if (i32 > 255) i32 = 255; else if (i32 > 255) i32 = 255;
u32Pixel |= (uint32_t)i32; // blue u32Pixel |= (uint32_t)(i32<<16); // blue
i32 = ((iCBG + iCRG + iY) >> 12); // green pixel i32 = ((iCBG + iCRG + iY) >> 12); // green pixel
if (i32 < 0) i32 = 0; if (i32 < 0) i32 = 0;
else if (i32 > 255) i32 = 255; else if (i32 > 255) i32 = 255;
@ -2863,7 +3170,7 @@ static void JPEGPixelRGB(uint32_t *pDest, int iY, int iCb, int iCr)
i32 = ((iCRR + iY) >> 12); // red pixel i32 = ((iCRR + iY) >> 12); // red pixel
if (i32 < 0) i32 = 0; if (i32 < 0) i32 = 0;
else if (i32 > 255) i32 = 255; else if (i32 > 255) i32 = 255;
u32Pixel |= (uint32_t)(i32 << 16); u32Pixel |= (uint32_t)(i32);
pDest[0] = u32Pixel; pDest[0] = u32Pixel;
} /* JPEGPixelRGB() */ } /* JPEGPixelRGB() */
@ -2943,7 +3250,7 @@ static void JPEGPixel2RGB(uint32_t *pDest, int32_t iY1, int32_t iY2, int32_t iCb
if (i32 < 0) i32 = 0; if (i32 < 0) i32 = 0;
else if (i32 > 255) i32 = 255; else if (i32 > 255) i32 = 255;
u32Pixel1 = u32Pixel2 = 0xff000000; // Alpha = 255 u32Pixel1 = u32Pixel2 = 0xff000000; // Alpha = 255
u32Pixel1 |= (uint32_t)i32; // blue u32Pixel1 |= (uint32_t)(i32<<16); // blue
i32 = ((iCBG + iCRG + iY1) >> 12); // green pixel i32 = ((iCBG + iCRG + iY1) >> 12); // green pixel
if (i32 < 0) i32 = 0; if (i32 < 0) i32 = 0;
else if (i32 > 255) i32 = 255; else if (i32 > 255) i32 = 255;
@ -2951,12 +3258,12 @@ static void JPEGPixel2RGB(uint32_t *pDest, int32_t iY1, int32_t iY2, int32_t iCb
i32 = ((iCRR + iY1) >> 12); // red pixel i32 = ((iCRR + iY1) >> 12); // red pixel
if (i32 < 0) i32 = 0; if (i32 < 0) i32 = 0;
else if (i32 > 255) i32 = 255; else if (i32 > 255) i32 = 255;
u32Pixel1 |= (uint32_t)(i32 << 16); // red u32Pixel1 |= (uint32_t)i32; // red
i32 = ((iCBB + iY2) >> 12); // blue pixel i32 = ((iCBB + iY2) >> 12); // blue pixel
if (i32 < 0) i32 = 0; if (i32 < 0) i32 = 0;
else if (i32 > 255) i32 = 255; else if (i32 > 255) i32 = 255;
u32Pixel2 |= (uint32_t)i32; u32Pixel2 |= (uint32_t)(i32<<16);
i32 = ((iCBG + iCRG + iY2) >> 12); // green pixel i32 = ((iCBG + iCRG + iY2) >> 12); // green pixel
if (i32 < 0) i32 = 0; if (i32 < 0) i32 = 0;
else if (i32 > 255) i32 = 255; else if (i32 > 255) i32 = 255;
@ -2964,7 +3271,7 @@ static void JPEGPixel2RGB(uint32_t *pDest, int32_t iY1, int32_t iY2, int32_t iCb
i32 = ((iCRR + iY2) >> 12); // red pixel i32 = ((iCRR + iY2) >> 12); // red pixel
if (i32 < 0) i32 = 0; if (i32 < 0) i32 = 0;
else if (i32 > 255) i32 = 255; else if (i32 > 255) i32 = 255;
u32Pixel2 |= (uint32_t)(i32 << 16); u32Pixel2 |= (uint32_t)i32;
pDest[0] = u32Pixel1; pDest[0] = u32Pixel1;
pDest[1] = u32Pixel2; pDest[1] = u32Pixel2;
} /* JPEGPixel2RGB() */ } /* JPEGPixel2RGB() */
@ -2973,7 +3280,7 @@ static void JPEGPutMCU11(JPEGIMAGE *pJPEG, int x, int iPitch)
{ {
int iCr, iCb; int iCr, iCb;
signed int Y; signed int Y;
int iCol; int iCol, w, delta;
int iRow; int iRow;
uint8_t *pY, *pCr, *pCb; uint8_t *pY, *pCr, *pCb;
uint16_t *pOutput = &pJPEG->usPixels[x]; uint16_t *pOutput = &pJPEG->usPixels[x];
@ -3088,12 +3395,14 @@ static void JPEGPutMCU11(JPEGIMAGE *pJPEG, int x, int iPitch)
} }
// full size // full size
#ifdef ESP32S3_SIMD #ifdef ESP32S3_SIMD
if (x + 8 <= iPitch && (iPitch & 15) == 0) { // only for non-clipped MCUs
if (pJPEG->ucPixelType == RGB8888) iPitch *= 2; if (pJPEG->ucPixelType == RGB8888) iPitch *= 2;
for (iRow=0; iRow<8; iRow++) { for (iRow=0; iRow<8; iRow++) {
s3_ycbcr_convert_444(pY, pCb, pCr, pOutput, i16_Consts, pJPEG->ucPixelType); s3_ycbcr_convert_444(pY, pCb, pCr, pOutput, i16_Consts, pJPEG->ucPixelType);
pCb += 8; pCr += 8; pY += 8; pOutput += iPitch; pCb += 8; pCr += 8; pY += 8; pOutput += iPitch;
} }
return; return;
}
#endif // ESP32S3_SIMD #endif // ESP32S3_SIMD
#ifdef HAS_SSE #ifdef HAS_SSE
@ -3206,35 +3515,44 @@ static void JPEGPutMCU11(JPEGIMAGE *pJPEG, int x, int iPitch)
} }
#endif // HAS_SSE #endif // HAS_SSE
// C reference version
w = 8; delta = 0;
if (x + 8 > iPitch) {
w = iPitch - x;
delta = 8 - w;
}
for (iRow=0; iRow<8; iRow++) // up to 8 rows to do for (iRow=0; iRow<8; iRow++) // up to 8 rows to do
{ {
if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN) if (pJPEG->ucPixelType == RGB565_LITTLE_ENDIAN)
{ {
for (iCol=0; iCol<8; iCol++) // up to 4x2 cols to do for (iCol=0; iCol<w; iCol++) // up to 4x2 cols to do
{ {
iCr = *pCr++; iCr = *pCr++;
iCb = *pCb++; iCb = *pCb++;
Y = (int)(*pY++) << 12; Y = (int)(*pY++) << 12;
JPEGPixelLE(pOutput+iCol, Y, iCb, iCr); JPEGPixelLE(pOutput+iCol, Y, iCb, iCr);
} // for col } // for col
pCr += delta; pCb += delta;
} }
else if (pJPEG->ucPixelType == RGB565_BIG_ENDIAN) else if (pJPEG->ucPixelType == RGB565_BIG_ENDIAN)
{ {
for (iCol=0; iCol<8; iCol++) // up to 4x2 cols to do for (iCol=0; iCol<w; iCol++) // up to 4x2 cols to do
{ {
iCr = *pCr++; iCr = *pCr++;
iCb = *pCb++; iCb = *pCb++;
Y = (int)(*pY++) << 12; Y = (int)(*pY++) << 12;
JPEGPixelBE(pOutput+iCol, Y, iCb, iCr); JPEGPixelBE(pOutput+iCol, Y, iCb, iCr);
} // for col } // for col
pCr += delta; pCb += delta;
} else { // RGB888 } else { // RGB888
for (iCol=0; iCol<8; iCol++) // up to 4x2 cols to do for (iCol=0; iCol<w; iCol++) // up to 4x2 cols to do
{ {
iCr = *pCr++; iCr = *pCr++;
iCb = *pCb++; iCb = *pCb++;
Y = (int)(*pY++) << 12; Y = (int)(*pY++) << 12;
JPEGPixelRGB((uint32_t *)&pOutput[iCol*2], Y, iCb, iCr); JPEGPixelRGB((uint32_t *)&pOutput[iCol*2], Y, iCb, iCr);
} // for col } // for col
pCr += delta; pCb += delta;
} }
pOutput += (pJPEG->ucPixelType == RGB8888) ? iPitch*2 : iPitch; pOutput += (pJPEG->ucPixelType == RGB8888) ? iPitch*2 : iPitch;
} // for row } // for row
@ -3429,6 +3747,7 @@ static void JPEGPutMCU22(JPEGIMAGE *pJPEG, int x, int iPitch)
} }
// full size // full size
#ifdef ESP32S3_SIMD #ifdef ESP32S3_SIMD
if (x + 8 <= iPitch && (iPitch & 15) == 0) { // only for non-clipped MCUs
if (pJPEG->ucPixelType == RGB8888) iPitch *= 2; if (pJPEG->ucPixelType == RGB8888) iPitch *= 2;
for (iRow=0; iRow<4; iRow++) { // top L+R, 4 pairs of lines x 16 pixels for (iRow=0; iRow<4; iRow++) { // top L+R, 4 pairs of lines x 16 pixels
// each call converts 16 pixels // each call converts 16 pixels
@ -3443,6 +3762,7 @@ static void JPEGPutMCU22(JPEGIMAGE *pJPEG, int x, int iPitch)
pCb += 8; pCr += 8; pY += 16; pOutput += iPitch*2; pCb += 8; pCr += 8; pY += 16; pOutput += iPitch*2;
} }
return; return;
}
#endif // ESP32S3_SIMD #endif // ESP32S3_SIMD
#ifdef HAS_NEON #ifdef HAS_NEON
@ -3938,7 +4258,7 @@ static void JPEGPutMCU22(JPEGIMAGE *pJPEG, int x, int iPitch)
mmxTemp = _mm_or_si128(mmxR, mmxG); // R+G mmxTemp = _mm_or_si128(mmxR, mmxG); // R+G
mmxTemp = _mm_or_si128(mmxTemp, mmxB); // R+G+B mmxTemp = _mm_or_si128(mmxTemp, mmxB); // R+G+B
// store first row of right block // store first row of right block
_mm_storeu_si128((__m128i *)(pOutput+16), mmxTemp); // write 8 RGB565 pixels _mm_storeu_si128((__m128i *)(pOutput+8), mmxTemp); // write 8 RGB565 pixels
// prepare second row of right block // prepare second row of right block
mmxY = _mm_loadl_epi64((__m128i *)(pY+136)); // load 1 row of Y (right block) mmxY = _mm_loadl_epi64((__m128i *)(pY+136)); // load 1 row of Y (right block)
mmxTemp2 = _mm_setzero_si128(); // zero it to use to set upper bits to 0 mmxTemp2 = _mm_setzero_si128(); // zero it to use to set upper bits to 0
@ -3970,7 +4290,7 @@ static void JPEGPutMCU22(JPEGIMAGE *pJPEG, int x, int iPitch)
mmxTemp = _mm_or_si128(mmxR, mmxG); // R+G mmxTemp = _mm_or_si128(mmxR, mmxG); // R+G
mmxTemp = _mm_or_si128(mmxTemp, mmxB); // R+G+B mmxTemp = _mm_or_si128(mmxTemp, mmxB); // R+G+B
// store second row of right block // store second row of right block
_mm_storeu_si128((__m128i *)(pOutput+16+iPitch), mmxTemp); // write 8 RGB565 pixels _mm_storeu_si128((__m128i *)(pOutput+8+iPitch), mmxTemp); // write 8 RGB565 pixels
pOutput += iPitch*2; pOutput += iPitch*2;
pCr += 8; pCr += 8;
@ -3987,21 +4307,21 @@ static void JPEGPutMCU22(JPEGIMAGE *pJPEG, int x, int iPitch)
/* Convert YCC pixels into RGB pixels and store in output image */ /* Convert YCC pixels into RGB pixels and store in output image */
iYCount = 4; iYCount = 4;
bUseOdd1 = bUseOdd2 = 1; // assume odd column can be used bUseOdd1 = bUseOdd2 = 1; // assume odd column can be used
if ((x+15) >= pJPEG->iWidth) if ((x+15) >= iPitch)
{ {
iCol = (((pJPEG->iWidth & 15)+1) >> 1); iCol = (((iPitch & 15)+1) >> 1);
if (iCol >= 4) if (iCol >= 4)
{ {
iXCount1 = 4; iXCount1 = 4;
iXCount2 = iCol-4; iXCount2 = iCol-4;
if (pJPEG->iWidth & 1 && (iXCount2 * 2) + 8 + (x * 16) > pJPEG->iWidth) if (iPitch & 1 && (iXCount2 * 2) + 8 + (x * 16) > iPitch)
bUseOdd2 = 0; bUseOdd2 = 0;
} }
else else
{ {
iXCount1 = iCol; iXCount1 = iCol;
iXCount2 = 0; iXCount2 = 0;
if (pJPEG->iWidth & 1 && (iXCount1 * 2) + (x * 16) > pJPEG->iWidth) if (iPitch & 1 && (iXCount1 * 2) + (x * 16) > iPitch)
bUseOdd1 = 0; bUseOdd1 = 0;
} }
} }
@ -4627,6 +4947,8 @@ static int DecodeJPEG(JPEGIMAGE *pJPEG)
signed int iDCPred0, iDCPred1, iDCPred2; signed int iDCPred0, iDCPred1, iDCPred2;
int i, iQuant1, iQuant2, iQuant3, iErr; int i, iQuant1, iQuant2, iQuant3, iErr;
int iSkipMask, bSkipRow; int iSkipMask, bSkipRow;
int iDMASize, iDMAOffset;
uint16_t *pAlignedPixels = pJPEG->usPixels;
uint8_t c; uint8_t c;
int iMCUCount, xoff, iPitch, bThumbnail = 0; int iMCUCount, xoff, iPitch, bThumbnail = 0;
int bContinue = 1; // early exit if the DRAW callback wants to stop int bContinue = 1; // early exit if the DRAW callback wants to stop
@ -4636,6 +4958,9 @@ static int DecodeJPEG(JPEGIMAGE *pJPEG)
int iMaxFill = 16, iScaleShift = 0; int iMaxFill = 16, iScaleShift = 0;
// Requested the Exif thumbnail // Requested the Exif thumbnail
if (pJPEG->ucMode == 0xc2) { // progressive mode - we only decode the first scan (DC values)
pJPEG->iOptions |= JPEG_SCALE_EIGHTH; // return 1/8 sized image
}
if (pJPEG->iOptions & JPEG_EXIF_THUMBNAIL) if (pJPEG->iOptions & JPEG_EXIF_THUMBNAIL)
{ {
if (pJPEG->iThumbData == 0 || pJPEG->iThumbWidth == 0) // doesn't exist if (pJPEG->iThumbData == 0 || pJPEG->iThumbWidth == 0) // doesn't exist
@ -4660,7 +4985,9 @@ static int DecodeJPEG(JPEGIMAGE *pJPEG)
iMaxFill = 1; iMaxFill = 1;
bThumbnail = 1; bThumbnail = 1;
} }
if (pJPEG->iOptions & JPEG_LUMA_ONLY && pJPEG->ucPixelType < EIGHT_BIT_GRAYSCALE) {
pJPEG->ucPixelType = EIGHT_BIT_GRAYSCALE; // switch to grayscale output
}
// reorder and fix the quantization table for decoding // reorder and fix the quantization table for decoding
JPEGFixQuantD(pJPEG); JPEGFixQuantD(pJPEG);
pJPEG->bb.ulBits = MOTOLONG(&pJPEG->ucFileBuf[0]); // preload first 4/8 bytes pJPEG->bb.ulBits = MOTOLONG(&pJPEG->ucFileBuf[0]); // preload first 4/8 bytes
@ -4681,14 +5008,14 @@ static int DecodeJPEG(JPEGIMAGE *pJPEG)
case 0x01: // fake value to handle sRGB/CMYK case 0x01: // fake value to handle sRGB/CMYK
case 0x11: case 0x11:
cx = (pJPEG->iWidth + 7) >> 3; // number of MCU blocks cx = (pJPEG->iWidth + 7) >> 3; // number of MCU blocks
cy = (pJPEG->iCropY + pJPEG->iCropCY) >> 3; cy = (pJPEG->iCropY + pJPEG->iCropCY + 7) >> 3;
iCr = MCU1; iCr = MCU1;
iCb = MCU2; iCb = MCU2;
mcuCX = mcuCY = 8; mcuCX = mcuCY = 8;
break; break;
case 0x12: case 0x12:
cx = (pJPEG->iWidth + 7) >> 3; // number of MCU blocks cx = (pJPEG->iWidth + 7) >> 3; // number of MCU blocks
cy = (pJPEG->iCropY + pJPEG->iCropCY) >> 4; cy = (pJPEG->iCropY + pJPEG->iCropCY + 15) >> 4;
iCr = MCU2; iCr = MCU2;
iCb = MCU3; iCb = MCU3;
mcuCX = 8; mcuCX = 8;
@ -4696,7 +5023,7 @@ static int DecodeJPEG(JPEGIMAGE *pJPEG)
break; break;
case 0x21: case 0x21:
cx = (pJPEG->iWidth + 15) >> 4; // number of MCU blocks cx = (pJPEG->iWidth + 15) >> 4; // number of MCU blocks
cy = (pJPEG->iCropY + pJPEG->iCropCY) >> 3; cy = (pJPEG->iCropY + pJPEG->iCropCY + 7) >> 3;
iCr = MCU2; iCr = MCU2;
iCb = MCU3; iCb = MCU3;
mcuCX = 16; mcuCX = 16;
@ -4704,7 +5031,7 @@ static int DecodeJPEG(JPEGIMAGE *pJPEG)
break; break;
case 0x22: case 0x22:
cx = (pJPEG->iWidth + 15) >> 4; // number of MCU blocks cx = (pJPEG->iWidth + 15) >> 4; // number of MCU blocks
cy = (pJPEG->iCropY + pJPEG->iCropCY) >> 4; cy = (pJPEG->iCropY + pJPEG->iCropCY + 15) >> 4;
iCr = MCU4; iCr = MCU4;
iCb = MCU5; iCb = MCU5;
mcuCX = mcuCY = 16; mcuCX = mcuCY = 16;
@ -4733,14 +5060,25 @@ static int DecodeJPEG(JPEGIMAGE *pJPEG)
if (pJPEG->ucPixelType == RGB8888) { if (pJPEG->ucPixelType == RGB8888) {
iMCUCount /= 2; // half as many will fit iMCUCount /= 2; // half as many will fit
} }
iDMASize = iDMAOffset = 0; // assume no DMA scheme
if (pJPEG->ucPixelType == EIGHT_BIT_GRAYSCALE) if (pJPEG->ucPixelType == EIGHT_BIT_GRAYSCALE)
iMCUCount *= 2; // each pixel is only 1 byte iMCUCount *= 2; // each pixel is only 1 byte
if (iMCUCount > cx) if (iMCUCount > cx)
iMCUCount = cx; // don't go wider than the image iMCUCount = cx; // don't go wider than the image
if (iMCUCount > pJPEG->iMaxMCUs) // did the user set an upper bound on how many pixels per JPEGDraw callback? if (iMCUCount > pJPEG->iMaxMCUs) { // did the user set an upper bound on how many pixels per JPEGDraw callback?
iMCUCount = pJPEG->iMaxMCUs; iMCUCount = pJPEG->iMaxMCUs;
if (pJPEG->ucPixelType > EIGHT_BIT_GRAYSCALE) // dithered, override the max MCU count } else if (pJPEG->iOptions & JPEG_USES_DMA) { // user wants a ping-pong buffer scheme
iMCUCount /= 2; // divide the pixel buffer in half
iDMASize = MAX_BUFFERED_PIXELS / 2; // offset to the second half of the buffer
}
if (pJPEG->ucPixelType > EIGHT_BIT_GRAYSCALE) { // dithered, override the max MCU count
iMCUCount = cx; // do the whole row iMCUCount = cx; // do the whole row
}
if (pJPEG->iCropCX != pJPEG->iWidth /*(cx * mcuCX)*/) { // crop enabled
if (iMCUCount * mcuCX > pJPEG->iCropCX) {
iMCUCount = (pJPEG->iCropCX / mcuCX); // maximum width is the crop width
}
}
jd.iBpp = 16; jd.iBpp = 16;
switch (pJPEG->ucPixelType) switch (pJPEG->ucPixelType)
{ {
@ -4786,14 +5124,20 @@ static int DecodeJPEG(JPEGIMAGE *pJPEG)
} }
for (x = 0; x < cx && bContinue && iErr == 0; x++) for (x = 0; x < cx && bContinue && iErr == 0; x++)
{ {
pJPEG->usPixels = &pAlignedPixels[iDMAOffset]; // make sure output is correct offset for DMA
iSkipMask = 0; // assume not skipping iSkipMask = 0; // assume not skipping
if (bSkipRow || x*mcuCX < pJPEG->iCropX || x*mcuCX >= pJPEG->iCropX+pJPEG->iCropCX) { if (bSkipRow || x*mcuCX < pJPEG->iCropX || x*mcuCX > pJPEG->iCropX+pJPEG->iCropCX) {
iSkipMask = MCU_SKIP; iSkipMask = MCU_SKIP;
} }
pJPEG->ucACTable = cACTable0; pJPEG->ucACTable = cACTable0;
pJPEG->ucDCTable = cDCTable0; pJPEG->ucDCTable = cDCTable0;
// do the first luminance component // do the first luminance component
if (pJPEG->ucMode == 0xc2) { // progressive
iErr = JPEGDecodeMCU_P(pJPEG, iLum0 | iSkipMask, &iDCPred0);
} else {
iErr = JPEGDecodeMCU(pJPEG, iLum0 | iSkipMask, &iDCPred0); iErr = JPEGDecodeMCU(pJPEG, iLum0 | iSkipMask, &iDCPred0);
}
if (pJPEG->u16MCUFlags == 0 || bThumbnail) // no AC components, save some time if (pJPEG->u16MCUFlags == 0 || bThumbnail) // no AC components, save some time
{ {
pl = (uint32_t *)&pJPEG->sMCUs[iLum0]; pl = (uint32_t *)&pJPEG->sMCUs[iLum0];
@ -4810,7 +5154,11 @@ static int DecodeJPEG(JPEGIMAGE *pJPEG)
// do the second luminance component // do the second luminance component
if (pJPEG->ucSubSample > 0x11) // subsampling if (pJPEG->ucSubSample > 0x11) // subsampling
{ {
if (pJPEG->ucMode == 0xc2) { // progressive
iErr |= JPEGDecodeMCU_P(pJPEG, iLum1 | iSkipMask, &iDCPred0);
} else {
iErr |= JPEGDecodeMCU(pJPEG, iLum1 | iSkipMask, &iDCPred0); iErr |= JPEGDecodeMCU(pJPEG, iLum1 | iSkipMask, &iDCPred0);
}
if (pJPEG->u16MCUFlags == 0 || bThumbnail) // no AC components, save some time if (pJPEG->u16MCUFlags == 0 || bThumbnail) // no AC components, save some time
{ {
c = ucRangeTable[((iDCPred0 * iQuant1) >> 5) & 0x3ff]; c = ucRangeTable[((iDCPred0 * iQuant1) >> 5) & 0x3ff];
@ -4826,7 +5174,11 @@ static int DecodeJPEG(JPEGIMAGE *pJPEG)
} }
if (pJPEG->ucSubSample == 0x22) if (pJPEG->ucSubSample == 0x22)
{ {
if (pJPEG->ucMode == 0xc2) { // progressive
iErr |= JPEGDecodeMCU_P(pJPEG, iLum2 | iSkipMask, &iDCPred0);
} else {
iErr |= JPEGDecodeMCU(pJPEG, iLum2 | iSkipMask, &iDCPred0); iErr |= JPEGDecodeMCU(pJPEG, iLum2 | iSkipMask, &iDCPred0);
}
if (pJPEG->u16MCUFlags == 0 || bThumbnail) // no AC components, save some time if (pJPEG->u16MCUFlags == 0 || bThumbnail) // no AC components, save some time
{ {
c = ucRangeTable[((iDCPred0 * iQuant1) >> 5) & 0x3ff]; c = ucRangeTable[((iDCPred0 * iQuant1) >> 5) & 0x3ff];
@ -4840,7 +5192,11 @@ static int DecodeJPEG(JPEGIMAGE *pJPEG)
{ {
JPEGIDCT(pJPEG, iLum2, pJPEG->JPCI[0].quant_tbl_no); // first quantization table JPEGIDCT(pJPEG, iLum2, pJPEG->JPCI[0].quant_tbl_no); // first quantization table
} }
if (pJPEG->ucMode == 0xc2) { // progressive
iErr |= JPEGDecodeMCU_P(pJPEG, iLum3 | iSkipMask, &iDCPred0);
} else {
iErr |= JPEGDecodeMCU(pJPEG, iLum3 | iSkipMask, &iDCPred0); iErr |= JPEGDecodeMCU(pJPEG, iLum3 | iSkipMask, &iDCPred0);
}
if (pJPEG->u16MCUFlags == 0 || bThumbnail) // no AC components, save some time if (pJPEG->u16MCUFlags == 0 || bThumbnail) // no AC components, save some time
{ {
c = ucRangeTable[((iDCPred0 * iQuant1) >> 5) & 0x3ff]; c = ucRangeTable[((iDCPred0 * iQuant1) >> 5) & 0x3ff];
@ -4863,10 +5219,19 @@ static int DecodeJPEG(JPEGIMAGE *pJPEG)
pJPEG->ucDCTable = cDCTable1; pJPEG->ucDCTable = cDCTable1;
if (pJPEG->ucPixelType >= EIGHT_BIT_GRAYSCALE) { if (pJPEG->ucPixelType >= EIGHT_BIT_GRAYSCALE) {
// We're not going to use the color channels, so avoid as much work as possible // We're not going to use the color channels, so avoid as much work as possible
if (pJPEG->ucMode == 0xc2) { // progressive
iErr |= JPEGDecodeMCU_P(pJPEG, MCU_SKIP, &iDCPred1);
iErr |= JPEGDecodeMCU_P(pJPEG, MCU_SKIP, &iDCPred2);
} else {
iErr |= JPEGDecodeMCU(pJPEG, MCU_SKIP, &iDCPred1); // decode Cr block iErr |= JPEGDecodeMCU(pJPEG, MCU_SKIP, &iDCPred1); // decode Cr block
iErr |= JPEGDecodeMCU(pJPEG, MCU_SKIP, &iDCPred2); // decode Cb block iErr |= JPEGDecodeMCU(pJPEG, MCU_SKIP, &iDCPred2); // decode Cb block
}
} else {
if (pJPEG->ucMode == 0xc2) { // progressive
iErr |= JPEGDecodeMCU_P(pJPEG, iCr | iSkipMask, &iDCPred1);
} else { } else {
iErr |= JPEGDecodeMCU(pJPEG, iCr | iSkipMask, &iDCPred1); iErr |= JPEGDecodeMCU(pJPEG, iCr | iSkipMask, &iDCPred1);
}
if (pJPEG->u16MCUFlags == 0 || bThumbnail) // no AC components, save some time if (pJPEG->u16MCUFlags == 0 || bThumbnail) // no AC components, save some time
{ {
c = ucRangeTable[((iDCPred1 * iQuant2) >> 5) & 0x3ff]; c = ucRangeTable[((iDCPred1 * iQuant2) >> 5) & 0x3ff];
@ -4883,7 +5248,11 @@ static int DecodeJPEG(JPEGIMAGE *pJPEG)
// second chroma // second chroma
pJPEG->ucACTable = cACTable2; pJPEG->ucACTable = cACTable2;
pJPEG->ucDCTable = cDCTable2; pJPEG->ucDCTable = cDCTable2;
if (pJPEG->ucMode == 0xc2) { // progressive
iErr |= JPEGDecodeMCU_P(pJPEG, iCb | iSkipMask, &iDCPred2);
} else {
iErr |= JPEGDecodeMCU(pJPEG, iCb | iSkipMask, &iDCPred2); iErr |= JPEGDecodeMCU(pJPEG, iCb | iSkipMask, &iDCPred2);
}
if (pJPEG->u16MCUFlags == 0 || bThumbnail) // no AC components, save some time if (pJPEG->u16MCUFlags == 0 || bThumbnail) // no AC components, save some time
{ {
c = ucRangeTable[((iDCPred2 * iQuant3) >> 5) & 0x3ff]; c = ucRangeTable[((iDCPred2 * iQuant3) >> 5) & 0x3ff];
@ -4923,26 +5292,42 @@ static int DecodeJPEG(JPEGIMAGE *pJPEG)
} // normal MCU drawing } // normal MCU drawing
xoff += mcuCX; xoff += mcuCX;
} // if not skipped } // if not skipped
if (pJPEG->pFramebuffer == NULL && (xoff == iPitch || x == cx-1) && !bSkipRow) // time to draw if (pJPEG->pFramebuffer == NULL && (xoff == iPitch || x == cx-1) && !iSkipMask) // time to draw
{ {
int iAdjust;
int iCurW, iCurH;
iAdjust = (1<<iScaleShift)-1;
iCurW = (pJPEG->iWidth + iAdjust) >> iScaleShift;
iCurH = (pJPEG->iHeight + iAdjust) >> iScaleShift;
jd.iWidth = jd.iWidthUsed = iPitch; // width of each LCD block group jd.iWidth = jd.iWidthUsed = iPitch; // width of each LCD block group
jd.pUser = pJPEG->pUser; jd.pUser = pJPEG->pUser;
if (pJPEG->ucPixelType > EIGHT_BIT_GRAYSCALE) // dither to 4/2/1 bits if (pJPEG->ucPixelType > EIGHT_BIT_GRAYSCALE) { // dither to 4/2/1 bits
JPEGDither(pJPEG, cx * mcuCX, mcuCY); JPEGDither(pJPEG, cx * mcuCX, mcuCY);
if ((x+1)*mcuCX > pJPEG->iWidth) { // right edge has clipped pixels }
jd.iWidthUsed = iPitch - (cx*mcuCX - pJPEG->iWidth); if (((jd.x - pJPEG->iXOffset) + iPitch) > iCurW) { // right edge has clipped pixels
} else if (jd.x + iPitch > pJPEG->iCropCX) { // not a full width jd.iWidthUsed = iCurW - (jd.x-pJPEG->iXOffset);
jd.iWidthUsed = pJPEG->iCropCX - jd.x; } else if (((jd.x-pJPEG->iXOffset) + iPitch) > pJPEG->iCropCX) { // not a full width
jd.iWidthUsed = pJPEG->iCropCX - (jd.x-pJPEG->iXOffset);
} }
jd.y = pJPEG->iYOffset + (y * mcuCY) - pJPEG->iCropY; jd.y = pJPEG->iYOffset + (y * mcuCY) - pJPEG->iCropY;
if ((jd.y - pJPEG->iYOffset + mcuCY) > (pJPEG->iHeight>>iScaleShift)) { // last row needs to be trimmed if ((jd.y - pJPEG->iYOffset + mcuCY) > iCurH) { // last row needs to be trimmed
jd.iHeight = (pJPEG->iHeight>>iScaleShift) - (jd.y - pJPEG->iYOffset); jd.iHeight = iCurH - (jd.y - pJPEG->iYOffset);
} }
if (pJPEG->ucPixelType > EIGHT_BIT_GRAYSCALE)
jd.pPixels = (uint16_t *)pJPEG->pDitherBuffer;
else
jd.pPixels = pJPEG->usPixels;
bContinue = (*pJPEG->pfnDraw)(&jd); bContinue = (*pJPEG->pfnDraw)(&jd);
iDMAOffset ^= iDMASize; // toggle ping-pong offset
jd.x += iPitch; jd.x += iPitch;
if ((cx - 1 - x) < iMCUCount) // change pitch for the last set of MCUs on this row if (pJPEG->iCropCX != (cx * mcuCX) && (iPitch + jd.x) > (pJPEG->iCropX + pJPEG->iCropCX)) { // image is cropped, don't go past end
iPitch = pJPEG->iCropCX - (jd.x-pJPEG->iXOffset); // x=0 of output is really pJPEG->iCropx
} else if ((cx - 1 - x) < iMCUCount) // change pitch for the last set of MCUs on this row
iPitch = (cx - 1 - x) * mcuCX; iPitch = (cx - 1 - x) * mcuCX;
xoff = 0; xoff = 0;
if (iPitch & (mcuCX-1)) { // we don't clip the MCU drawing, so expand it
iPitch = (iPitch + (mcuCX-1)) & ~(mcuCX-1);
}
} }
if (pJPEG->iResInterval) if (pJPEG->iResInterval)
{ {

View File

@ -4,7 +4,8 @@
// Copyright (c) 2024 BitBank Software, Inc. // Copyright (c) 2024 BitBank Software, Inc.
// Project started Jan 21, 2024 // Project started Jan 21, 2024
// //
#ifdef ARDUINO_ARCH_ESP32 #if defined (ARDUINO_ARCH_ESP32) && !defined(NO_SIMD)
#if __has_include ("dsps_fft2r_platform.h")
#include "dsps_fft2r_platform.h" #include "dsps_fft2r_platform.h"
#if (dsps_fft2r_sc16_aes3_enabled == 1) #if (dsps_fft2r_sc16_aes3_enabled == 1)
.text .text
@ -117,7 +118,7 @@
ee.vzip.16 q7,q2 # create RGB8888 pixels ee.vzip.16 q7,q2 # create RGB8888 pixels
ee.vst.128.ip q7,a5,16 # store 8 x RGB8888 pixels = 32 bytes ee.vst.128.ip q7,a5,16 # store 8 x RGB8888 pixels = 32 bytes
ee.vst.128.ip q2,a5,16 ee.vst.128.ip q2,a5,16
addi.n a6,a6,-16 # restore pointer to start of 16-bit constants addi.n a6,a6,-12 # restore pointer to start of 16-bit constants
ee.vld.l.64.ip q0,a2,0 # load right 8 Y values into Q0 ee.vld.l.64.ip q0,a2,0 # load right 8 Y values into Q0
ee.movi.32.q q1,a3,0 # restore second 4 values of Cb ee.movi.32.q q1,a3,0 # restore second 4 values of Cb
ee.movi.32.q q2,a4,0 # restore second 4 values of Cr ee.movi.32.q q2,a4,0 # restore second 4 values of Cr
@ -125,4 +126,5 @@
bnez.n a8,.convert_420_loop bnez.n a8,.convert_420_loop
retw.n retw.n
#endif // dsps_fft2r_sc16_aes3_enabled #endif // dsps_fft2r_sc16_aes3_enabled
#endif // __has_include
#endif // ESP32 #endif // ESP32

View File

@ -4,8 +4,8 @@
// Copyright (c) 2024 BitBank Software, Inc. // Copyright (c) 2024 BitBank Software, Inc.
// Project started Jan 21, 2024 // Project started Jan 21, 2024
// //
#ifdef ARDUINO_ARCH_ESP32 #if defined (ARDUINO_ARCH_ESP32) && !defined(NO_SIMD)
#if __has_include ("dsps_fft2r_platform.h")
#include "dsps_fft2r_platform.h" #include "dsps_fft2r_platform.h"
#if (dsps_fft2r_sc16_aes3_enabled == 1) #if (dsps_fft2r_sc16_aes3_enabled == 1)
.text .text
@ -106,4 +106,5 @@ s3_ycbcr_convert_444:
ee.vst.128.ip q2,a5,0 ee.vst.128.ip q2,a5,0
retw.n # done retw.n # done
#endif // dsps_fft2r_sc16_aes3_enabled #endif // dsps_fft2r_sc16_aes3_enabled
#endif // __has_include
#endif // ESP32 #endif // ESP32

View File

@ -4,7 +4,7 @@
// Copyright (c) 2024 BitBank Software, Inc. // Copyright (c) 2024 BitBank Software, Inc.
// Project started Jan 21, 2024 // Project started Jan 21, 2024
// //
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ESP32S3_DEV
#include "dsps_fft2r_platform.h" #include "dsps_fft2r_platform.h"
#if (dsps_fft2r_sc16_aes3_enabled == 1) #if (dsps_fft2r_sc16_aes3_enabled == 1)

View File

@ -0,0 +1,115 @@
//
// ESP32-S3 SIMD optimized code
// Written by Larry Bank
// Copyright (c) 2024 BitBank Software, Inc.
// Project started Jan 21, 2024
//
#ifdef ARDUINO_ESP32S3_DEV
#include "dsps_fft2r_platform.h"
#if (dsps_fft2r_sc16_aes3_enabled == 1)
.text
.align 4
//
// Inverse DCT dequantization for JPEG decompression
// A2 A3 A4 A5
// Call as void s3_idct(int16_t *pMCU, int16_t *pQuant, const int16_t *pConst, uint16_ u16MCUFlags);
.global s3_idct
.type s3_idct,@function
s3_idct:
# no idea what this frequency keyword does
# .frequency 1.000 0.000
entry a1,64
mov.n a9,a1 # keep stack ptr copy in a9
andi.n a5,0x2000 # isolate lower rows populated info
bnez.n a5,.full_calc
// Lower 4 rows are empty, this simplifies the calculations
// columns first, even part
ee.vld.128.ip q0,a2,32 # load MCU row 0 int Q0
ee.vld.128.ip q1,a2,0 # load row 2 into Q1
ee.vld.128.ip q2,a3,32 # load quant row 0 into Q2
ee.vld.128.ip q3,a3,0 # load quant row 2 into Q3
movi.n a6,0 # load the shift register with 0
wsr.sar a6 # put it in the SAR (shift amount register)
ee.vmul.s16 q0,q0,q2 # de-quantize row 0
ee.vmul.s16 q1,q1,q3 # de-quantize row 2
ee.vld.128.ip q4,a4,16 # load 0.414 constants into Q4
movi.n a6,14 # load the shift register with 14
wsr.sar a6 # put it in the SAR (shift amount register)
ee.vmul.s16 q6,q4,q1 # temp12 = row2 * 0.414
ee.vadds.s16 q4,q0,q1 # 0+2 tmp0
ee.vsubs.s16 q7,q0,q1 # 0-2 tmp3
ee.vadds.s16 q5,q0,q6 # 10+12 tmp1
ee.vsubs.s16 q6,q0,q6 # 10-12 tmp2
// odd part
subi.n a2,16 # point to row 1
subi.n a3,16 # point to quant vals for row 1
ee.vld.128.ip q0,a2,32 # load MCU row 1 int Q0
ee.vld.128.ip q1,a2,0 # load row 3 into Q1
ee.vld.128.ip q2,a3,32 # load quant row 1 into Q2
ee.vld.128.ip q3,a3,0 # load quant row 3 into Q3
movi.n a6,0 # load the shift register with 0
wsr.sar a6 # put it in the SAR (shift amount register)
ee.vmul.s16 q0,q0,q2 # de-quantize row 1
ee.vmul.s16 q1,q1,q3 # de-quantize row 3
ee.vadds.s16 q2,q0,q1 # tmp7 = tmp4+tmp5
ee.vld.128.ip q3,a4,16 # load 1.414 constants into Q3
movi.n a6,14 # load the shift register with 14
wsr.sar a6 # put it in the SAR (shift amount register)
ee.vst.128.ip q4,a9,16 # need to stack these registers
ee.vst.128.ip q5,a9,16 # because 8 Q registers is not enough :(
ee.vsubs.s16 q4,q0,q1 # tmp4-tmp5
ee.vmul.s16 q5,q4,q3 # temp11 = (tmp4-tmp5) * 1.414
ee.vld.128.ip q3,a4,16 # load 1.8477 constant into Q3
ee.vmul.s16 q4,q4,q3 # Z5 = (tmp4-tmp5) * 1.847
ee.vld.128.ip q3,a4,16 # load 2.613 constant into Q3
movi.n a6,13 # load the shift register with 13
wsr.sar a6 # otherwise this constant would overflow
ee.vmul.s16 q3,q3,q1 # tmp5 * 2.613
.full_calc:
// Need to do the full calculations
ee.vld.128.ip q0,a2,16 # load MCU rows 0-3 into Q0,Q1,Q2,Q3
ee.vld.128.ip q4,a3,16 # load quantization values into Q4,Q5,Q6,Q7
ee.vld.128.ip q1,a2,16
ee.vld.128.ip q5,a3,16
ee.vld.128.ip q2,a2,16
ee.vld.128.ip q6,a3,16
ee.vld.128.ip q3,a2,16
ee.vld.128.ip q7,a3,16
movi.n a4,0 # load the shift register with 0
wsr.sar a2 # put it in the SAR (shift amount register)
ee.vmul.s16 q0,q0,q4 # de-quantize each row
ee.vmul.s16 q1,q1,q5
ee.vmul.s16 q2,q2,q6
ee.vmul.s16 q3,q3,q7
addi.n a2,a2,64 # point to first row of MCUs to store dequantized values
ee.vst.128.ip q0,a2,16 # write back dequantized rows 0-3
ee.vst.128.ip q1,a2,16
ee.vst.128.ip q2,a2,16
ee.vst.128.ip q3,a2,16
// repeat for rows 4-7
ee.vld.128.ip q0,a2,16 # load MCU rows 4-7 into Q0,Q1,Q2,Q3
ee.vld.128.ip q4,a3,16 # load quantization values into Q4,Q5,Q6,Q7
ee.vld.128.ip q1,a2,16
ee.vld.128.ip q5,a3,16
ee.vld.128.ip q2,a2,16
ee.vld.128.ip q6,a3,16
ee.vld.128.ip q3,a2,16
ee.vld.128.ip q7,a3,16
ee.vmul.s16 q0,q0,q4 # de-quantize rows 4-7
ee.vmul.s16 q1,q1,q5
ee.vmul.s16 q2,q2,q6
ee.vmul.s16 q3,q3,q7
addi.n a2,a2,64 # point to 4th row of MCUs
ee.vst.128.ip q0,a2,16 # write back dequantized rows 4-7
ee.vst.128.ip q1,a2,16
ee.vst.128.ip q2,a2,16
ee.vst.128.ip q3,a2,16
retw.n # done
#endif // dsps_fft2r_sc16_aes3_enabled
#endif // ESP32

File diff suppressed because it is too large Load Diff