From 421f35f4abf4a54fa02b51358c93cd88e492b39f Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 10 Apr 2021 12:37:25 +0200 Subject: [PATCH 01/17] Use the address sanitizer for gtests --- radio/src/tests/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/tests/CMakeLists.txt b/radio/src/tests/CMakeLists.txt index 18f462aed..f88022ec7 100644 --- a/radio/src/tests/CMakeLists.txt +++ b/radio/src/tests/CMakeLists.txt @@ -9,8 +9,8 @@ if(GTEST_INCDIR AND GTEST_SRCDIR AND Qt5Widgets_FOUND) set(TESTS_BUILD_PATH ${CMAKE_CURRENT_BINARY_DIR}) configure_file(${RADIO_SRC_DIR}/tests/location.h.in ${CMAKE_CURRENT_BINARY_DIR}/location.h @ONLY) include_directories(${CMAKE_CURRENT_BINARY_DIR}) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS_DEBUG} -O0") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_DEBUG} -O0 ${WARNING_FLAGS}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS_DEBUG} -O0 -fsanitize=address") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_DEBUG} -O0 ${WARNING_FLAGS} -fsanitize=address") use_cxx11() # ensure gnu++11 in CXX_FLAGS with CMake < 3.1 if(PCB STREQUAL X12S OR PCB STREQUAL X10) From d2bc155e783d79da20f3613a5ad952519a21dbcc Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 10 Apr 2021 14:14:44 +0200 Subject: [PATCH 02/17] Re-enabled lcd tests (480x272) --- radio/src/tests/lcd_480x272.cpp | 190 +++++++++++++++++++++++++++ radio/src/tests/lcd_480x272.cpp.todo | 183 -------------------------- 2 files changed, 190 insertions(+), 183 deletions(-) create mode 100644 radio/src/tests/lcd_480x272.cpp delete mode 100644 radio/src/tests/lcd_480x272.cpp.todo diff --git a/radio/src/tests/lcd_480x272.cpp b/radio/src/tests/lcd_480x272.cpp new file mode 100644 index 000000000..3c8192ad8 --- /dev/null +++ b/radio/src/tests/lcd_480x272.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#define SWAP_DEFINED +#include "opentx.h" +#include "location.h" +#include "targets/simu/simulcd.h" + +#if defined(COLORLCD) + +#include "gui/colorlcd/fonts.h" + +void doPaint_colorlcd(const BitmapBuffer* dc, QPainter & p) +{ + QRgb rgb = qRgb(0, 0, 0); + p.setBackground(QBrush(rgb)); + p.eraseRect(0, 0, LCD_W, LCD_H); + + uint16_t previousColor = 0xFF; + for (int y=0; ygetPixelPtr(x, y)); // color in RGB565 + if (color) { + if (color != previousColor) { + previousColor = color; + RGB_SPLIT(color, r, g, b); + rgb = qRgb(r<<3, g<<2, b<<3); + p.setPen(rgb); + p.setBrush(QBrush(rgb)); + } + p.drawPoint(x, y); + } + } + } +} + +bool checkScreenshot_colorlcd(const BitmapBuffer* dc, const QString & test) +{ + QImage buffer(LCD_W, LCD_H, QImage::Format_RGB32); + QPainter p(&buffer); + doPaint_colorlcd(dc, p); + QString filename(QString("%1_%2x%3.png").arg(test).arg(LCD_W).arg(LCD_H)); + QImage reference(TESTS_PATH "/" + filename); + + if (buffer == reference) { + return true; + } + else { + QString filename(QString("%1_%2x%3.png").arg(test).arg(LCD_W).arg(LCD_H)); + buffer.save("/tmp/" + filename); + return false; + } +} + + +TEST(Lcd_colorlcd, vline) +{ + loadFonts(); + + BitmapBuffer dc(BMP_RGB565, LCD_W, LCD_H); + dc.clear(DEFAULT_BGCOLOR); + + for (int x=0; x<100; x+=2) { + dc.drawSolidVerticalLine(x, x/2, 12, DEFAULT_COLOR); + } + EXPECT_TRUE(checkScreenshot_colorlcd(&dc, "vline")); +} + +TEST(Lcd_colorlcd, primitives) +{ + loadFonts(); + + BitmapBuffer dc(BMP_RGB565, LCD_W, LCD_H); + dc.clear(DEFAULT_BGCOLOR); + + dc.drawText(8, 8, "The quick brown fox jumps over the lazy dog", DISABLE_COLOR); + dc.drawText(5, 5, "The quick brown fox jumps over the lazy dog", DEFAULT_COLOR); + + dc.drawFilledRect(10, 30, 30, 30, SOLID, TITLE_BGCOLOR); + dc.drawFilledRect(50, 30, 30, 30, DOTTED, DEFAULT_COLOR); + //dc.drawFilledRect(90, 30, 30, 30, SOLID, ROUND|TITLE_BGCOLOR); + dc.drawRect(130, 30, 30, 30, 1, SOLID, TITLE_BGCOLOR); + dc.drawRect(170, 30, 30, 30, 2, SOLID, TITLE_BGCOLOR); + dc.drawRect(210, 30, 30, 30, 5, SOLID, TITLE_BGCOLOR); + + dc.drawVerticalLine(10, 70, 100, SOLID, TITLE_BGCOLOR); + dc.drawVerticalLine(15, 70, 90, SOLID, TITLE_BGCOLOR); + dc.drawVerticalLine(20, 70, 80, SOLID, TITLE_BGCOLOR); + dc.drawVerticalLine(25, 70, 70, SOLID, TITLE_BGCOLOR); + + dc.drawHorizontalLine(30, 70, 100, SOLID, DEFAULT_COLOR); + dc.drawHorizontalLine(30, 75, 90, SOLID, DEFAULT_COLOR); + dc.drawHorizontalLine(30, 80, 80, SOLID, DEFAULT_COLOR); + dc.drawHorizontalLine(30, 85, 70, SOLID, DEFAULT_COLOR); + + + EXPECT_TRUE(checkScreenshot_colorlcd(&dc, "primitives")); +} + +TEST(Lcd_colorlcd, transparency) +{ + loadFonts(); + + BitmapBuffer dc(BMP_RGB565, LCD_W, LCD_H); + dc.clear(DEFAULT_BGCOLOR); + + dc.drawText(8, 8, "The quick brown fox jumps over the lazy dog", DEFAULT_COLOR|OPACITY(4)); + dc.drawText(5, 5, "The quick brown fox jumps over the lazy dog", DEFAULT_COLOR|OPACITY(12)); + + dc.drawFilledRect(10, 30, 30, 30, SOLID, TITLE_BGCOLOR|OPACITY(8)); + dc.drawFilledRect(50, 30, 30, 30, DOTTED, DEFAULT_COLOR|OPACITY(10)); + //dc.drawFilledRect(90, 30, 30, 30, SOLID, ROUND|TITLE_BGCOLOR|OPACITY(12)); + dc.drawRect(130, 30, 30, 30, 1, SOLID, TITLE_BGCOLOR|OPACITY(8)); + dc.drawRect(170, 30, 30, 30, 2, SOLID, TITLE_BGCOLOR|OPACITY(8)); + dc.drawRect(210, 30, 30, 30, 5, SOLID, TITLE_BGCOLOR|OPACITY(8)); + + dc.drawVerticalLine(10, 70, 100, SOLID, TITLE_BGCOLOR|OPACITY(2)); + dc.drawVerticalLine(15, 70, 90, SOLID, TITLE_BGCOLOR|OPACITY(6)); + dc.drawVerticalLine(20, 70, 80, SOLID, TITLE_BGCOLOR|OPACITY(10)); + dc.drawVerticalLine(25, 70, 70, SOLID, TITLE_BGCOLOR|OPACITY(OPACITY_MAX)); + + dc.drawHorizontalLine(30, 70, 100, SOLID, DEFAULT_COLOR|OPACITY(2)); + dc.drawHorizontalLine(30, 75, 90, SOLID, DEFAULT_COLOR|OPACITY(6)); + dc.drawHorizontalLine(30, 80, 80, SOLID, DEFAULT_COLOR|OPACITY(10)); + dc.drawHorizontalLine(30, 85, 70, SOLID, DEFAULT_COLOR|OPACITY(OPACITY_MAX)); + + + for(int n=0; n<10; n++) { + int x = 120 + n * 20; + int y = 80 + n * 10; + int color = COLOR(n/2 + 5); + int size = 100; + dc.drawFilledRect(x, y, size, size, SOLID, color|OPACITY(8)); + + } + + EXPECT_TRUE(checkScreenshot_colorlcd(&dc, "transparency")); +} + +TEST(Lcd_colorlcd, fonts) +{ + loadFonts(); + + BitmapBuffer dc(BMP_RGB565, LCD_W, LCD_H); + dc.clear(DEFAULT_BGCOLOR); + + dc.drawText(8, 8, "The quick brown fox jumps over the lazy dog", DEFAULT_COLOR|OPACITY(4)); + dc.drawText(5, 5, "The quick brown fox jumps over the lazy dog", DEFAULT_COLOR|OPACITY(12)); + + dc.drawText(10, 200, "The quick", DEFAULT_COLOR|VERTICAL); + dc.drawText(30, 200, "The quick brown fox", DEFAULT_COLOR|VERTICAL); + // dc.drawText(50, 200, "The quick brown fox jumps over the lazy dog", DEFAULT_COLOR|VERTICAL); + + dc.drawText(50, 25, "The quick", DEFAULT_COLOR | FONT(XXS)); + dc.drawText(50, 40, "The quick", DEFAULT_COLOR | FONT(XS)); + dc.drawText(50, 55, "The quick", DEFAULT_COLOR | FONT(L)); + dc.drawText(50, 80, "The quick", DEFAULT_COLOR | FONT(XL)); + dc.drawText(50, 120, "The quick", DEFAULT_COLOR | FONT(XXL)); + + dc.drawText(8, 208, "The quick brown fox jumps over the lazy dog", TITLE_BGCOLOR|OPACITY(4)); + dc.drawText(5, 205, "The quick brown fox jumps over the lazy dog", TITLE_BGCOLOR|OPACITY(12)); + + EXPECT_TRUE(checkScreenshot_colorlcd(&dc, "fonts")); +} + +#endif diff --git a/radio/src/tests/lcd_480x272.cpp.todo b/radio/src/tests/lcd_480x272.cpp.todo deleted file mode 100644 index 42d20fb8a..000000000 --- a/radio/src/tests/lcd_480x272.cpp.todo +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include - -#define SWAP_DEFINED -#include "opentx.h" -#include "location.h" -#include "targets/simu/simulcd.h" - -#if defined(COLORLCD) - -void doPaint_colorlcd(QPainter & p) -{ - QRgb rgb = qRgb(0, 0, 0); - p.setBackground(QBrush(rgb)); - p.eraseRect(0, 0, LCD_W, LCD_H); - - uint16_t previousColor = 0xFF; - for (int y=0; yclear(DEFAULT_BGCOLOR); - for (int x=0; x<100; x+=2) { - lcdDrawSolidVerticalLine(x, x/2, 12, DEFAULT_COLOR); - } - EXPECT_TRUE(checkScreenshot_colorlcd("vline")); -} - -TEST(Lcd_colorlcd, primitives) -{ - loadFonts(); - lcd->clear(DEFAULT_BGCOLOR); - lcdDrawText(8, 8, "The quick brown fox jumps over the lazy dog", DISABLE_COLOR); - lcdDrawText(5, 5, "The quick brown fox jumps over the lazy dog", DEFAULT_COLOR); - - lcdDrawFilledRect(10, 30, 30, 30, SOLID, TITLE_BGCOLOR); - lcdDrawFilledRect(50, 30, 30, 30, DOTTED, DEFAULT_COLOR); - lcdDrawFilledRect(90, 30, 30, 30, SOLID, ROUND|TITLE_BGCOLOR); - lcdDrawRect(130, 30, 30, 30, 1, SOLID, TITLE_BGCOLOR); - lcdDrawRect(170, 30, 30, 30, 2, SOLID, TITLE_BGCOLOR); - lcdDrawRect(210, 30, 30, 30, 5, SOLID, TITLE_BGCOLOR); - - lcdDrawVerticalLine(10, 70, 100, SOLID, TITLE_BGCOLOR); - lcdDrawVerticalLine(15, 70, 90, SOLID, TITLE_BGCOLOR); - lcdDrawVerticalLine(20, 70, 80, SOLID, TITLE_BGCOLOR); - lcdDrawVerticalLine(25, 70, 70, SOLID, TITLE_BGCOLOR); - - lcdDrawHorizontalLine(30, 70, 100, SOLID, DEFAULT_COLOR); - lcdDrawHorizontalLine(30, 75, 90, SOLID, DEFAULT_COLOR); - lcdDrawHorizontalLine(30, 80, 80, SOLID, DEFAULT_COLOR); - lcdDrawHorizontalLine(30, 85, 70, SOLID, DEFAULT_COLOR); - - - EXPECT_TRUE(checkScreenshot_colorlcd("primitives")); -} - -TEST(Lcd_colorlcd, transparency) -{ - loadFonts(); - lcd->clear(DEFAULT_BGCOLOR); - lcdDrawText(8, 8, "The quick brown fox jumps over the lazy dog", DEFAULT_COLOR|OPACITY(4)); - lcdDrawText(5, 5, "The quick brown fox jumps over the lazy dog", DEFAULT_COLOR|OPACITY(12)); - - lcdDrawFilledRect(10, 30, 30, 30, SOLID, TITLE_BGCOLOR|OPACITY(8)); - lcdDrawFilledRect(50, 30, 30, 30, DOTTED, DEFAULT_COLOR|OPACITY(10)); - lcdDrawFilledRect(90, 30, 30, 30, SOLID, ROUND|TITLE_BGCOLOR|OPACITY(12)); - lcdDrawRect(130, 30, 30, 30, 1, SOLID, TITLE_BGCOLOR|OPACITY(8)); - lcdDrawRect(170, 30, 30, 30, 2, SOLID, TITLE_BGCOLOR|OPACITY(8)); - lcdDrawRect(210, 30, 30, 30, 5, SOLID, TITLE_BGCOLOR|OPACITY(8)); - - lcdDrawVerticalLine(10, 70, 100, SOLID, TITLE_BGCOLOR|OPACITY(2)); - lcdDrawVerticalLine(15, 70, 90, SOLID, TITLE_BGCOLOR|OPACITY(6)); - lcdDrawVerticalLine(20, 70, 80, SOLID, TITLE_BGCOLOR|OPACITY(10)); - lcdDrawVerticalLine(25, 70, 70, SOLID, TITLE_BGCOLOR|OPACITY(OPACITY_MAX)); - - lcdDrawHorizontalLine(30, 70, 100, SOLID, DEFAULT_COLOR|OPACITY(2)); - lcdDrawHorizontalLine(30, 75, 90, SOLID, DEFAULT_COLOR|OPACITY(6)); - lcdDrawHorizontalLine(30, 80, 80, SOLID, DEFAULT_COLOR|OPACITY(10)); - lcdDrawHorizontalLine(30, 85, 70, SOLID, DEFAULT_COLOR|OPACITY(OPACITY_MAX)); - - - for(int n=0; n<10; n++) { - int x = 120 + n * 20; - int y = 80 + n * 10; - int color = COLOR(n/2 + 5); - int size = 100; - lcdDrawFilledRect(x, y, size, size, SOLID, color|OPACITY(8)); - - } - - EXPECT_TRUE(checkScreenshot_colorlcd("transparency")); -} - -TEST(Lcd_colorlcd, fonts) -{ - loadFonts(); - lcd->clear(DEFAULT_BGCOLOR); - - lcdDrawText(8, 8, "The quick brown fox jumps over the lazy dog", DEFAULT_COLOR|OPACITY(4)); - lcdDrawText(5, 5, "The quick brown fox jumps over the lazy dog", DEFAULT_COLOR|OPACITY(12)); - - lcdDrawText(10, 200, "The quick", DEFAULT_COLOR|VERTICAL); - lcdDrawText(30, 200, "The quick brown fox", DEFAULT_COLOR|VERTICAL); - // lcdDrawText(50, 200, "The quick brown fox jumps over the lazy dog", DEFAULT_COLOR|VERTICAL); - - lcdDrawText(50, 25, "The quick", TEXT_COLOR|TINSIZE); - lcdDrawText(50, 40, "The quick", TEXT_COLOR|SMLSIZE); - lcdDrawText(50, 55, "The quick", TEXT_COLOR|MIDSIZE); - lcdDrawText(50, 80, "The quick", TEXT_COLOR|DBLSIZE); - lcdDrawText(50, 120, "The quick", TEXT_COLOR|XXLSIZE); - - lcdDrawText(8, 208, "The quick brown fox jumps over the lazy dog", TITLE_BGCOLOR|OPACITY(4)); - lcdDrawText(5, 205, "The quick brown fox jumps over the lazy dog", TITLE_BGCOLOR|OPACITY(12)); - - EXPECT_TRUE(checkScreenshot_colorlcd("fonts")); -} - - -#endif From a217b3710940246eb5e2c5f0cc8bcc91e70e9869 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 10 Apr 2021 14:24:34 +0200 Subject: [PATCH 03/17] updated font test picture --- radio/src/tests/fonts_480x272.png | Bin 14748 -> 16095 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/radio/src/tests/fonts_480x272.png b/radio/src/tests/fonts_480x272.png index 94d4237b5eac8903230b699682a37c313addd11e..7c02a46dd89c7c4495e79bf098671f81bb27d7e6 100644 GIT binary patch literal 16095 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 literal 14748 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:H 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 M<<;a6GH?C=58jg>JOBUy From badb840bea6fb465ec25d3ff0d388b20d84970b4 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 10 Apr 2021 15:15:37 +0200 Subject: [PATCH 04/17] Add a clipping test --- radio/src/tests/lcd_480x272.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/radio/src/tests/lcd_480x272.cpp b/radio/src/tests/lcd_480x272.cpp index 3c8192ad8..e9f6990d6 100644 --- a/radio/src/tests/lcd_480x272.cpp +++ b/radio/src/tests/lcd_480x272.cpp @@ -187,4 +187,31 @@ TEST(Lcd_colorlcd, fonts) EXPECT_TRUE(checkScreenshot_colorlcd(&dc, "fonts")); } +TEST(Lcd_colorlcd, clipping) +{ + loadFonts(); + + BitmapBuffer dc(BMP_RGB565, LCD_W, LCD_H); + dc.clear(DEFAULT_BGCOLOR); + + dc.drawSolidVerticalLine(100, 0, LCD_H, DEFAULT_COLOR); + dc.drawSolidVerticalLine(400, 0, LCD_H, DEFAULT_COLOR); + + dc.drawSolidHorizontalLine(0, 50, LCD_W, DEFAULT_COLOR); + dc.drawSolidHorizontalLine(0, 200, LCD_W, DEFAULT_COLOR); + + dc.setClippingRect(100, 400, 50, 200); + + dc.drawSolidHorizontalLine(0, 80, LCD_W, TITLE_BGCOLOR); + dc.drawHorizontalLine( 0, 81, LCD_W, SOLID, TITLE_BGCOLOR); + + dc.drawSolidVerticalLine(150, 0, LCD_H, TITLE_BGCOLOR); + dc.drawVerticalLine( 151, 0, LCD_H, SOLID, TITLE_BGCOLOR); + + dc.drawSolidRect(20, 20, 50, 50, 2, TITLE_BGCOLOR); + dc.drawRect( 380, 20, 50, 50, 2, SOLID, TITLE_BGCOLOR); + + EXPECT_TRUE(checkScreenshot_colorlcd(&dc, "clipping")); +} + #endif From e20203b0dc9168befa5944e4026c0cf5876bbf3c Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 10 Apr 2021 16:40:07 +0200 Subject: [PATCH 05/17] Fixed test pictures --- radio/src/tests/clipping_480x272.png | Bin 0 -> 1198 bytes radio/src/tests/lcd_480x272.cpp | 17 ++++++++--------- radio/src/tests/primitives_480x272.png | Bin 5713 -> 5666 bytes radio/src/tests/transparency_480x272.png | Bin 5773 -> 5763 bytes 4 files changed, 8 insertions(+), 9 deletions(-) create mode 100644 radio/src/tests/clipping_480x272.png diff --git a/radio/src/tests/clipping_480x272.png b/radio/src/tests/clipping_480x272.png new file mode 100644 index 0000000000000000000000000000000000000000..4110ca0b4593da02782c540b3ecb36c12b559777 GIT binary patch literal 1198 zcmeAS@N?(olHy`uVBq!ia0y~yV0-|?0vt?0k>@G~w}BLAfk$L90|S2|2s5s*U!KXp zz>?tU;uumf=j~m?yh9Em4vyAE51Rv@NN5(TOt*CFk~zcb$`!T5q2qD8&E@8OI`1!M zsZJ}jUC+FI=JK15n`C8kdH21sWnj3@Y2q-&f!SrSQwQAqP8URdlVi$z*1PxSmU-9r zrdLRPHer{){^yq=k8^>+j#m@Sx9w!x{Q~?|2nPBAcW7{W6pndo;ym%GBMnF_4;qXNvZbBo4)ff z#3ZuUwm-9Fm|k1^Sx@gr^jXs#vgsEX0-1OPR7a^n76c3h{lIKeeg1>Vv&?Th65QRR noBJ#nNKa1q(#cSednKKu?>6gzSOO=o0AlcT^>bP0l+XkK5e`K$ literal 0 HcmV?d00001 diff --git a/radio/src/tests/lcd_480x272.cpp b/radio/src/tests/lcd_480x272.cpp index e9f6990d6..a00d92da1 100644 --- a/radio/src/tests/lcd_480x272.cpp +++ b/radio/src/tests/lcd_480x272.cpp @@ -102,10 +102,10 @@ TEST(Lcd_colorlcd, primitives) dc.drawFilledRect(10, 30, 30, 30, SOLID, TITLE_BGCOLOR); dc.drawFilledRect(50, 30, 30, 30, DOTTED, DEFAULT_COLOR); - //dc.drawFilledRect(90, 30, 30, 30, SOLID, ROUND|TITLE_BGCOLOR); - dc.drawRect(130, 30, 30, 30, 1, SOLID, TITLE_BGCOLOR); - dc.drawRect(170, 30, 30, 30, 2, SOLID, TITLE_BGCOLOR); - dc.drawRect(210, 30, 30, 30, 5, SOLID, TITLE_BGCOLOR); + + dc.drawRect(90, 30, 30, 30, 1, SOLID, TITLE_BGCOLOR); + dc.drawRect(130, 30, 30, 30, 2, SOLID, TITLE_BGCOLOR); + dc.drawRect(170, 30, 30, 30, 5, SOLID, TITLE_BGCOLOR); dc.drawVerticalLine(10, 70, 100, SOLID, TITLE_BGCOLOR); dc.drawVerticalLine(15, 70, 90, SOLID, TITLE_BGCOLOR); @@ -133,10 +133,10 @@ TEST(Lcd_colorlcd, transparency) dc.drawFilledRect(10, 30, 30, 30, SOLID, TITLE_BGCOLOR|OPACITY(8)); dc.drawFilledRect(50, 30, 30, 30, DOTTED, DEFAULT_COLOR|OPACITY(10)); - //dc.drawFilledRect(90, 30, 30, 30, SOLID, ROUND|TITLE_BGCOLOR|OPACITY(12)); - dc.drawRect(130, 30, 30, 30, 1, SOLID, TITLE_BGCOLOR|OPACITY(8)); - dc.drawRect(170, 30, 30, 30, 2, SOLID, TITLE_BGCOLOR|OPACITY(8)); - dc.drawRect(210, 30, 30, 30, 5, SOLID, TITLE_BGCOLOR|OPACITY(8)); + + dc.drawRect(90, 30, 30, 30, 1, SOLID, TITLE_BGCOLOR|OPACITY(8)); + dc.drawRect(130, 30, 30, 30, 2, SOLID, TITLE_BGCOLOR|OPACITY(8)); + dc.drawRect(170, 30, 30, 30, 5, SOLID, TITLE_BGCOLOR|OPACITY(8)); dc.drawVerticalLine(10, 70, 100, SOLID, TITLE_BGCOLOR|OPACITY(2)); dc.drawVerticalLine(15, 70, 90, SOLID, TITLE_BGCOLOR|OPACITY(6)); @@ -173,7 +173,6 @@ TEST(Lcd_colorlcd, fonts) dc.drawText(10, 200, "The quick", DEFAULT_COLOR|VERTICAL); dc.drawText(30, 200, "The quick brown fox", DEFAULT_COLOR|VERTICAL); - // dc.drawText(50, 200, "The quick brown fox jumps over the lazy dog", DEFAULT_COLOR|VERTICAL); dc.drawText(50, 25, "The quick", DEFAULT_COLOR | FONT(XXS)); dc.drawText(50, 40, "The quick", DEFAULT_COLOR | FONT(XS)); diff --git a/radio/src/tests/primitives_480x272.png b/radio/src/tests/primitives_480x272.png index 68bb2f88655e889c9e3530c9de29d6945d1bcbcf..3574c7ecf7af6d270e2e2381e6a8497c7b871156 100644 GIT binary patch literal 5666 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 UX_sFK4}AgVCYCo#e|3rf8?hm&egFUf literal 5713 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 diff --git a/radio/src/tests/transparency_480x272.png b/radio/src/tests/transparency_480x272.png index 2b0588eb91099c84eb5331edfa06082056dd42b4..71e626f43839c79a0547b4db2599643e92d47d9d 100644 GIT binary patch literal 5763 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 ZQl7Z?0)+C{B%rGdf|%R?O1N+>@gK3e(G&mx literal 5773 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 KsPyu^sJ{WTviZOO From 537d26351ad341cfe42e187c1a4976c88bccc915 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 10 Apr 2021 16:50:59 +0200 Subject: [PATCH 06/17] Added bitmap tests --- radio/src/tests/bitmap_480x272.png | Bin 0 -> 4944 bytes radio/src/tests/lcd_480x272.cpp | 15 ++++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 radio/src/tests/bitmap_480x272.png diff --git a/radio/src/tests/bitmap_480x272.png b/radio/src/tests/bitmap_480x272.png new file mode 100644 index 0000000000000000000000000000000000000000..b5ff2a857e6391774d86f67bdc1444b452580bcd GIT binary patch literal 4944 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 oZ%h8C>B|2t$@y bmp(BitmapBuffer::loadBitmap(TESTS_PATH "/opentx.png")); + dc.drawBitmap( 0, 0, bmp.get()); + dc.drawBitmap(320, 0, bmp.get()); + dc.drawBitmap( 0, 150, bmp.get()); + + EXPECT_TRUE(checkScreenshot_colorlcd(&dc, "bitmap")); +} #endif From b4857ade9346d275e747985049f8b8e74ce8651c Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 10 Apr 2021 18:03:59 +0200 Subject: [PATCH 07/17] added missing fonts.h --- radio/src/gui/colorlcd/fonts.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 radio/src/gui/colorlcd/fonts.h diff --git a/radio/src/gui/colorlcd/fonts.h b/radio/src/gui/colorlcd/fonts.h new file mode 100644 index 000000000..92a7a60a3 --- /dev/null +++ b/radio/src/gui/colorlcd/fonts.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#pragma once + +void loadFonts(); From 95507e43a6694cff87111f2e6bb2780a007ae49c Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 10 Apr 2021 18:04:18 +0200 Subject: [PATCH 08/17] added missing opentx.png --- radio/src/tests/opentx.png | Bin 0 -> 12364 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 radio/src/tests/opentx.png diff --git a/radio/src/tests/opentx.png b/radio/src/tests/opentx.png new file mode 100644 index 0000000000000000000000000000000000000000..d85a1a087155e67300f594060d272dc8cd6f0241 GIT binary patch literal 12364 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 Date: Tue, 13 Apr 2021 07:40:27 +0200 Subject: [PATCH 09/17] Updated tests according to new clipping in libopenui --- radio/src/tests/clipping_480x272.png | Bin 1198 -> 1189 bytes radio/src/tests/lcd_480x272.cpp | 2 ++ 2 files changed, 2 insertions(+) diff --git a/radio/src/tests/clipping_480x272.png b/radio/src/tests/clipping_480x272.png index 4110ca0b4593da02782c540b3ecb36c12b559777..8572bae6e1734998c4f3ef359a36e4fe0349b2bc 100644 GIT binary patch delta 273 zcmV+s0q*{;38e{;IYw7WL_t(|obB8@PQ)-2K+z0J;e}DK3`&+n$ucNdn2RYWXb~Dd zgKsqF^pT#d`06P|PV+oBE;IJwxL~1MyMuSD2LRx6lR*NfEN~_F&)fa;>LK7_-q?%n z^E_A2a2R7B!Q&baFWa^S2ZK-j=8bLM!Rzsf1C!wcHX!!Z0|2+^86rpg9eDQL4U*8NAS2l_WIxa z-vExHc?Yk@2D1?Ye*u#~0vHwm0N-(%=ec@@!x;N;T(Hos-9eu?0Fwa(my_@VTNdga X253K5yG>bY00000NkvXXu0mjfmjQv_ literal 1198 zcmeAS@N?(olHy`uVBq!ia0y~yV0-|?0vt?0k>@G~w}BLAfk$L90|S2|2s5s*U!KXp zz>?tU;uumf=j~m?yh9Em4vyAE51Rv@NN5(TOt*CFk~zcb$`!T5q2qD8&E@8OI`1!M zsZJ}jUC+FI=JK15n`C8kdH21sWnj3@Y2q-&f!SrSQwQAqP8URdlVi$z*1PxSmU-9r zrdLRPHer{){^yq=k8^>+j#m@Sx9w!x{Q~?|2nPBAcW7{W6pndo;ym%GBMnF_4;qXNvZbBo4)ff z#3ZuUwm-9Fm|k1^Sx@gr^jXs#vgsEX0-1OPR7a^n76c3h{lIKeeg1>Vv&?Th65QRR noBJ#nNKa1q(#cSednKKu?>6gzSOO=o0AlcT^>bP0l+XkK5e`K$ diff --git a/radio/src/tests/lcd_480x272.cpp b/radio/src/tests/lcd_480x272.cpp index 479c4d6b1..1e2c47a7e 100644 --- a/radio/src/tests/lcd_480x272.cpp +++ b/radio/src/tests/lcd_480x272.cpp @@ -210,6 +210,7 @@ TEST(Lcd_colorlcd, clipping) dc.drawSolidRect(20, 20, 50, 50, 2, TITLE_BGCOLOR); dc.drawRect( 380, 20, 50, 50, 2, SOLID, TITLE_BGCOLOR); + dc.clearClippingRect(); EXPECT_TRUE(checkScreenshot_colorlcd(&dc, "clipping")); } @@ -224,6 +225,7 @@ TEST(Lcd_colorlcd, bitmap) dc.drawBitmap(320, 0, bmp.get()); dc.drawBitmap( 0, 150, bmp.get()); + dc.clearClippingRect(); EXPECT_TRUE(checkScreenshot_colorlcd(&dc, "bitmap")); } #endif From 3ad1c10fa59de4b990755aada233b9e55c752954 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 13 Apr 2021 07:58:02 +0200 Subject: [PATCH 10/17] Fixed solid rect clipping test --- radio/src/tests/clipping_480x272.png | Bin 1189 -> 1197 bytes radio/src/tests/lcd_480x272.cpp | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/tests/clipping_480x272.png b/radio/src/tests/clipping_480x272.png index 8572bae6e1734998c4f3ef359a36e4fe0349b2bc..5a90991cb50cde470ce699ffe72b0a0c0359708d 100644 GIT binary patch literal 1197 zcmeAS@N?(olHy`uVBq!ia0y~yV0-|?0vt?0k>@G~w}BLAfk$L90|S2|2s5s*U!KXp zz!LB2;uumf=k49SzDEog8ZOFsO+2_Rfyc{j*Cay?37w;&A^|~n_$Thyc+V>k8TRL7 zm&oreao0O<>wHeym9Sv(+Jn(V)@DMjExE@(V-R)0&@_ z*)kQAmX>f^XEdz1yzpIrWzgqcbMJGv*xhIAXWn)_rup{9X8cBfQhz8rM~%wv9_!`HWXC)@U9_Ms8%gFanQ+kp%W-!0Z&Fa07>mONAZGApxeF7K}+ hj6))eP&X*@G~w}BLAfk$L90|S2|2s5s*U!KXp zz!L81;uumf=j|P9zas`B4i|a69<~N<;PIN~a?(g6Vn(;7Muwo2U30$4^Dh(Ut@-gv z)79^@jk%ujw}(3t7Dn%GE?>>g@SvE9S3vawL*QVi3RJ(Jzy0^~E5!#%_il8*{cIC{ zzCa>b{@`ONe*N3qSlOHX|32%@y?5~S$4+JiuLX>YSfog#zFlQz*#6@2_P;e}*?0v~ zo7w(dt*QO~hd+F0hkr>>|FFaE z+pqh7?lbf)s$^e(oPas_>=>298wHdF4+F#fLP#7+{4R*uaAECT$N3fvq$eYi)7>D` b@Jf~~!#;esZ)6s*++px^^>bP0l+XkKuRBB5 diff --git a/radio/src/tests/lcd_480x272.cpp b/radio/src/tests/lcd_480x272.cpp index 1e2c47a7e..1c8602fa0 100644 --- a/radio/src/tests/lcd_480x272.cpp +++ b/radio/src/tests/lcd_480x272.cpp @@ -207,7 +207,7 @@ TEST(Lcd_colorlcd, clipping) dc.drawSolidVerticalLine(150, 0, LCD_H, TITLE_BGCOLOR); dc.drawVerticalLine( 151, 0, LCD_H, SOLID, TITLE_BGCOLOR); - dc.drawSolidRect(20, 20, 50, 50, 2, TITLE_BGCOLOR); + dc.drawSolidRect(70, 20, 50, 50, 2, TITLE_BGCOLOR); dc.drawRect( 380, 20, 50, 50, 2, SOLID, TITLE_BGCOLOR); dc.clearClippingRect(); From ddaeff7f650942e87a845bf12c02243c5f588382 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 13 Apr 2021 12:20:03 +0200 Subject: [PATCH 11/17] Use stb_image_write.h instead of Qt hocus-pocus --- radio/src/tests/lcd_480x272.cpp | 84 +- radio/src/tests/stb_image_write.h | 1690 +++++++++++++++++++++++++++++ 2 files changed, 1737 insertions(+), 37 deletions(-) create mode 100644 radio/src/tests/stb_image_write.h diff --git a/radio/src/tests/lcd_480x272.cpp b/radio/src/tests/lcd_480x272.cpp index 1c8602fa0..c7662ffba 100644 --- a/radio/src/tests/lcd_480x272.cpp +++ b/radio/src/tests/lcd_480x272.cpp @@ -18,62 +18,72 @@ * GNU General Public License for more details. */ -#include -#include -#include -#include #include #include #define SWAP_DEFINED #include "opentx.h" #include "location.h" -#include "targets/simu/simulcd.h" #if defined(COLORLCD) #include "gui/colorlcd/fonts.h" -void doPaint_colorlcd(const BitmapBuffer* dc, QPainter & p) -{ - QRgb rgb = qRgb(0, 0, 0); - p.setBackground(QBrush(rgb)); - p.eraseRect(0, 0, LCD_W, LCD_H); +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" - uint16_t previousColor = 0xFF; - for (int y=0; ygetPixelPtr(x, y)); // color in RGB565 - if (color) { - if (color != previousColor) { - previousColor = color; - RGB_SPLIT(color, r, g, b); - rgb = qRgb(r<<3, g<<2, b<<3); - p.setPen(rgb); - p.setBrush(QBrush(rgb)); - } - p.drawPoint(x, y); - } - } +void convert_RGB565_to_RGB888(uint8_t * dst, const uint16_t * src, unsigned pixels) +{ + while(pixels--) { + RGB_SPLIT(*src, r, g, b); src++; + *(dst++) = (uint8_t)(r << 3); + *(dst++) = (uint8_t)(g << 2); + *(dst++) = (uint8_t)(b << 3); } } -bool checkScreenshot_colorlcd(const BitmapBuffer* dc, const QString & test) +void dumpImage(const std::string& filename, const BitmapBuffer* dc) { - QImage buffer(LCD_W, LCD_H, QImage::Format_RGB32); - QPainter p(&buffer); - doPaint_colorlcd(dc, p); - QString filename(QString("%1_%2x%3.png").arg(test).arg(LCD_W).arg(LCD_H)); - QImage reference(TESTS_PATH "/" + filename); + std::string fullpath = TESTS_PATH "/failed_" + filename; - if (buffer == reference) { - return true; - } - else { - QString filename(QString("%1_%2x%3.png").arg(test).arg(LCD_W).arg(LCD_H)); - buffer.save("/tmp/" + filename); + // allocate enough for 3 channels + auto pixels = dc->width() * dc->height(); + auto stride = dc->width() * 3; + uint8_t * img = (uint8_t *)malloc(pixels * 3); + convert_RGB565_to_RGB888(img, dc->getPixelPtr(0,0), pixels); + stbi_write_png(fullpath.c_str(), dc->width(), dc->height(), 3, img, stride); +} + +bool checkScreenshot_colorlcd(const BitmapBuffer* dc, const char* test) +{ + if (dc->width() != LCD_W || dc->height() != LCD_H) { return false; } + + std::string filename = std::string(test); + filename += '_' + std::to_string(LCD_W); + filename += 'x' + std::to_string(LCD_H); + filename += ".png"; + + std::string fullpath = TESTS_PATH "/" + filename; + + std::unique_ptr testPict(BitmapBuffer::loadBitmap(fullpath.c_str())); + if (!testPict || testPict->width() != LCD_W || testPict->height() != LCD_H) { + dumpImage(filename, dc); + return false; + } + + auto testPtr = testPict->getPixelPtr(0,0); + auto dcPtr = dc->getPixelPtr(0,0); + + for (int i=0; i + +// if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' +#ifndef STBIWDEF +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else +#ifdef __cplusplus +#define STBIWDEF extern "C" +#else +#define STBIWDEF extern +#endif +#endif +#endif + +#ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations +extern int stbi_write_tga_with_rle; +extern int stbi_write_png_compression_level; +extern int stbi_write_force_png_filter; +#endif + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); + +#ifdef STBI_WINDOWS_UTF8 +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif +#endif + +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + +STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); + +#endif//INCLUDE_STB_IMAGE_WRITE_H + +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + +#ifdef _WIN32 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #endif +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include +#endif // STBI_WRITE_NO_STDIO + +#include +#include +#include +#include + +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) +// ok +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." +#endif + +#ifndef STBIW_MALLOC +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,newsz) realloc(p,newsz) +#define STBIW_FREE(p) free(p) +#endif + +#ifndef STBIW_REALLOC_SIZED +#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) +#endif + + +#ifndef STBIW_MEMMOVE +#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) +#endif + + +#ifndef STBIW_ASSERT +#include +#define STBIW_ASSERT(x) assert(x) +#endif + +#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) + +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi_write_png_compression_level = 8; +static int stbi_write_tga_with_rle = 1; +static int stbi_write_force_png_filter = -1; +#else +int stbi_write_png_compression_level = 8; +int stbi_write_tga_with_rle = 1; +int stbi_write_force_png_filter = -1; +#endif + +static int stbi__flip_vertically_on_write = 0; + +STBIWDEF void stbi_flip_vertically_on_write(int flag) +{ + stbi__flip_vertically_on_write = flag; +} + +typedef struct +{ + stbi_write_func *func; + void *context; + unsigned char buffer[64]; + int buf_used; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) +{ + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) +{ + fwrite(data,1,size,(FILE*) context); +} + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#ifdef __cplusplus +#define STBIW_EXTERN extern "C" +#else +#define STBIW_EXTERN extern +#endif +STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); + +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbiw__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + return 0; + +#if _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f = stbiw__fopen(filename, "wb"); + stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + +typedef unsigned int stbiw_uint32; +typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; + +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); + s->func(s->context,&x,1); + break; } + case '2': { int x = va_arg(v,int); + unsigned char b[2]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x>>8); + s->func(s->context,b,2); + break; } + case '4': { stbiw_uint32 x = va_arg(v,int); + unsigned char b[4]; + b[0]=STBIW_UCHAR(x); + b[1]=STBIW_UCHAR(x>>8); + b[2]=STBIW_UCHAR(x>>16); + b[3]=STBIW_UCHAR(x>>24); + s->func(s->context,b,4); + break; } + default: + STBIW_ASSERT(0); + return; + } + } +} + +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__write_flush(stbi__write_context *s) +{ + if (s->buf_used) { + s->func(s->context, &s->buffer, s->buf_used); + s->buf_used = 0; + } +} + +static void stbiw__putc(stbi__write_context *s, unsigned char c) +{ + s->func(s->context, &c, 1); +} + +static void stbiw__write1(stbi__write_context *s, unsigned char a) +{ + if (s->buf_used + 1 > sizeof(s->buffer)) + stbiw__write_flush(s); + s->buffer[s->buf_used++] = a; +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) +{ + int n; + if (s->buf_used + 3 > sizeof(s->buffer)) + stbiw__write_flush(s); + n = s->buf_used; + s->buf_used = n+3; + s->buffer[n+0] = a; + s->buffer[n+1] = b; + s->buffer[n+2] = c; +} + +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) +{ + unsigned char bg[3] = { 255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + stbiw__write1(s, d[comp - 1]); + + switch (comp) { + case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case + case 1: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + stbiw__write1(s, d[0]); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + stbiw__write1(s, d[comp - 1]); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +{ + stbiw_uint32 zero = 0; + int i,j, j_end; + + if (y <= 0) + return; + + if (stbi__flip_vertically_on_write) + vdir *= -1; + + if (vdir < 0) { + j_end = -1; j = y-1; + } else { + j_end = y; j = 0; + } + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + unsigned char *d = (unsigned char *) data + (j*x+i)*comp; + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); + } + stbiw__write_flush(s); + s->func(s->context, &zero, scanline_pad); + } +} + +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +{ + if (y < 0 || x < 0) { + return 0; + } else { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); + stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); + return 1; + } +} + +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) +{ + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header +} + +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //!STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) +{ + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp-1 : comp; + int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i,j,k; + int jend, jdir; + + stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + + if (stbi__flip_vertically_on_write) { + j = 0; + jend = y; + jdir = 1; + } else { + j = y-1; + jend = -1; + jdir = -1; + } + for (; j != jend; j += jdir) { + unsigned char *row = (unsigned char *) data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = STBIW_UCHAR(len - 1); + stbiw__write1(s, header); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = STBIW_UCHAR(len - 129); + stbiw__write1(s, header); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + stbiw__write_flush(s); + } + return 1; +} + +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR writer +// by Baldur Karlsson + +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) + +static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +{ + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + + if (maxcomp < 1e-32f) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; + + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } +} + +static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) +{ + unsigned char lengthbyte = STBIW_UCHAR(length+128); + STBIW_ASSERT(length+128 <= 255); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); +} + +static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) +{ + unsigned char lengthbyte = STBIW_UCHAR(length); + STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); +} + +static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +{ + unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; + unsigned char rgbe[4]; + float linear[3]; + int x; + + scanlineheader[2] = (width&0xff00)>>8; + scanlineheader[3] = (width&0x00ff); + + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) { + for (x=0; x < width; x++) { + switch (ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + s->func(s->context, rgbe, 4); + } + } else { + int c,r; + /* encode into scratch buffer */ + for (x=0; x < width; x++) { + switch(ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width*0] = rgbe[0]; + scratch[x + width*1] = rgbe[1]; + scratch[x + width*2] = rgbe[2]; + scratch[x + width*3] = rgbe[3]; + } + + s->func(s->context, scanlineheader, 4); + + /* RLE each component separately */ + for (c=0; c < 4; c++) { + unsigned char *comp = &scratch[width*c]; + + x = 0; + while (x < width) { + // find first run + r = x; + while (r+2 < width) { + if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) + break; + ++r; + } + if (r+2 >= width) + r = width; + // dump up to first run + while (x < r) { + int len = r-x; + if (len > 128) len = 128; + stbiw__write_dump_data(s, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) { + int len = r-x; + if (len > 127) len = 127; + stbiw__write_run_data(s, len, comp[x]); + x += len; + } + } + } + } + } +} + +static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) +{ + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full output scanline. + unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header)-1); + +#ifdef __STDC_WANT_SECURE_LIB__ + len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#else + len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#endif + s->func(s->context, buffer, len); + + for(i=0; i < y; i++) + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)); + STBIW_FREE(scratch); + return 1; + } +} + +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +#ifndef STBIW_ZLIB_COMPRESS +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() +#define stbiw__sbraw(a) ((int *) (void *) (a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] + +#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) +#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) + +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; + void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw__zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int stbiw__zhash(unsigned char *data) +{ + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) +// default huffman tables +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) +#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) + +#define stbiw__ZHASH 16384 + +#endif // STBIW_ZLIB_COMPRESS + +STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ +#ifdef STBIW_ZLIB_COMPRESS + // user provided a zlib compress implementation, use that + return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); +#else // use builtin + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**)); + if (hash_table == NULL) + return NULL; + if (quality < 5) quality = 5; + + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1,1); // BFINAL = 1 + stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + + for (i=0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) { best=d; bestloc=hlist[j]; } + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h],data+i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); // distance back + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbiw__zlib_huff(j+257); + if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); + if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (;i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0,1); + + for (i=0; i < stbiw__ZHASH; ++i) + (void) stbiw__sbfree(hash_table[i]); + STBIW_FREE(hash_table); + + { + // compute adler32 on input + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; } + s1 %= 65521; s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s2)); + stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s1)); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (unsigned char *) stbiw__sbraw(out); +#endif // STBIW_ZLIB_COMPRESS +} + +static unsigned int stbiw__crc32(unsigned char *buffer, int len) +{ +#ifdef STBIW_CRC32 + return STBIW_CRC32(buffer, len); +#else + static unsigned int crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + unsigned int crc = ~0u; + int i; + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +#endif +} + +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) + +static void stbiw__wpcrc(unsigned char **data, int len) +{ + unsigned int crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); +} + +static unsigned char stbiw__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); + if (pb <= pc) return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +// @OPTIMIZE: provide an option that always forces left-predict or paeth predict +static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) +{ + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = (y != 0) ? mapping : firstmap; + int i; + int type = mymap[filter_type]; + unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); + int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; + + if (type==0) { + memcpy(line_buffer, z, width*n); + return; + } + + // first loop isn't optimized since it's just one pixel + for (i = 0; i < n; ++i) { + switch (type) { + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + } + switch (type) { + case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break; + case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; + case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; + case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } +} + +STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int force_filter = stbi_write_force_png_filter; + int ctype[5] = { -1, 0, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int j,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + if (force_filter >= 5) { + force_filter = -1; + } + + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + int filter_type; + if (force_filter > -1) { + filter_type = force_filter; + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); + } else { // Estimate the best filter by running through all of them: + int best_filter = 0, best_filter_val = 0x7fffffff, est, i; + for (filter_type = 0; filter_type < 5; filter_type++) { + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); + + // Estimate the entropy of the line using this filter; the less, the better. + est = 0; + for (i = 0; i < x*n; ++i) { + est += abs((signed char) line_buffer[i]); + } + if (est < best_filter_val) { + best_filter_val = est; + best_filter = filter_type; + } + } + if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); + filter_type = best_filter; + } + } + // when we get here, filter_type contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) filter_type; + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); + STBIW_FREE(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + STBIW_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +{ + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + + f = stbiw__fopen(filename, "wb"); + if (!f) { STBIW_FREE(png); return 0; } + fwrite(png, 1, len, f); + fclose(f); + STBIW_FREE(png); + return 1; +} +#endif + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +{ + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + + +/* *************************************************************************** + * + * JPEG writer + * + * This is based on Jon Olick's jo_jpeg.cpp: + * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html + */ + +static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, + 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; + +static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { + int bitBuf = *bitBufP, bitCnt = *bitCntP; + bitCnt += bs[1]; + bitBuf |= bs[0] << (24 - bitCnt); + while(bitCnt >= 8) { + unsigned char c = (bitBuf >> 16) & 255; + stbiw__putc(s, c); + if(c == 255) { + stbiw__putc(s, 0); + } + bitBuf <<= 8; + bitCnt -= 8; + } + *bitBufP = bitBuf; + *bitCntP = bitCnt; +} + +static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { + float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; + float z1, z2, z3, z4, z5, z11, z13; + + float tmp0 = d0 + d7; + float tmp7 = d0 - d7; + float tmp1 = d1 + d6; + float tmp6 = d1 - d6; + float tmp2 = d2 + d5; + float tmp5 = d2 - d5; + float tmp3 = d3 + d4; + float tmp4 = d3 - d4; + + // Even part + float tmp10 = tmp0 + tmp3; // phase 2 + float tmp13 = tmp0 - tmp3; + float tmp11 = tmp1 + tmp2; + float tmp12 = tmp1 - tmp2; + + d0 = tmp10 + tmp11; // phase 3 + d4 = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * 0.707106781f; // c4 + d2 = tmp13 + z1; // phase 5 + d6 = tmp13 - z1; + + // Odd part + tmp10 = tmp4 + tmp5; // phase 2 + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + // The rotator is modified from fig 4-8 to avoid extra negations. + z5 = (tmp10 - tmp12) * 0.382683433f; // c6 + z2 = tmp10 * 0.541196100f + z5; // c2-c6 + z4 = tmp12 * 1.306562965f + z5; // c2+c6 + z3 = tmp11 * 0.707106781f; // c4 + + z11 = tmp7 + z3; // phase 5 + z13 = tmp7 - z3; + + *d5p = z13 + z2; // phase 6 + *d3p = z13 - z2; + *d1p = z11 + z4; + *d7p = z11 - z4; + + *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; +} + +static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { + int tmp1 = val < 0 ? -val : val; + val = val < 0 ? val-1 : val; + bits[1] = 1; + while(tmp1 >>= 1) { + ++bits[1]; + } + bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { + } + // end0pos = first element in reverse order !=0 + if(end0pos == 0) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + return DU[0]; + } + for(i = 1; i <= end0pos; ++i) { + int startpos = i; + int nrzeroes; + unsigned short bits[2]; + for (; DU[i]==0 && i<=end0pos; ++i) { + } + nrzeroes = i-startpos; + if ( nrzeroes >= 16 ) { + int lng = nrzeroes>>4; + int nrmarker; + for (nrmarker=1; nrmarker <= lng; ++nrmarker) + stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); + nrzeroes &= 15; + } + stbiw__jpg_calcBits(DU[i], bits); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); + } + if(end0pos != 63) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + } + return DU[0]; +} + +static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { + // Constants that don't pollute global namespace + static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; + static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; + static const unsigned char std_ac_luminance_values[] = { + 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 + }; + static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; + static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; + static const unsigned char std_ac_chrominance_values[] = { + 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 + }; + // Huffman tables + static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; + static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; + static const unsigned short YAC_HT[256][2] = { + {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const unsigned short UVAC_HT[256][2] = { + {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, + 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; + static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, + 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; + static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, + 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; + + int row, col, i, k, subsample; + float fdtbl_Y[64], fdtbl_UV[64]; + unsigned char YTable[64], UVTable[64]; + + if(!data || !width || !height || comp > 4 || comp < 1) { + return 0; + } + + quality = quality ? quality : 90; + subsample = quality <= 90 ? 1 : 0; + quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; + quality = quality < 50 ? 5000 / quality : 200 - quality * 2; + + for(i = 0; i < 64; ++i) { + int uvti, yti = (YQT[i]*quality+50)/100; + YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); + uvti = (UVQT[i]*quality+50)/100; + UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); + } + + for(row = 0, k = 0; row < 8; ++row) { + for(col = 0; col < 8; ++col, ++k) { + fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + } + } + + // Write Headers + { + static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; + static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; + const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), + 3,1,(unsigned char)(subsample?0x22:0x11),0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; + s->func(s->context, (void*)head0, sizeof(head0)); + s->func(s->context, (void*)YTable, sizeof(YTable)); + stbiw__putc(s, 1); + s->func(s->context, UVTable, sizeof(UVTable)); + s->func(s->context, (void*)head1, sizeof(head1)); + s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); + s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); + stbiw__putc(s, 0x10); // HTYACinfo + s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); + s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); + stbiw__putc(s, 1); // HTUDCinfo + s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); + stbiw__putc(s, 0x11); // HTUACinfo + s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); + s->func(s->context, (void*)head2, sizeof(head2)); + } + + // Encode 8x8 macroblocks + { + static const unsigned short fillBits[] = {0x7F, 7}; + int DCY=0, DCU=0, DCV=0; + int bitBuf=0, bitCnt=0; + // comp == 2 is grey+alpha (alpha is ignored) + int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; + const unsigned char *dataR = (const unsigned char *)data; + const unsigned char *dataG = dataR + ofsG; + const unsigned char *dataB = dataR + ofsB; + int x, y, pos; + if(subsample) { + for(y = 0; y < height; y += 16) { + for(x = 0; x < width; x += 16) { + float Y[256], U[256], V[256]; + for(row = y, pos = 0; row < y+16; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; + for(col = x; col < x+16; ++col, ++pos) { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width-1))*comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; + U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; + V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; + } + } + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + + // subsample U,V + { + float subU[64], subV[64]; + int yy, xx; + for(yy = 0, pos = 0; yy < 8; ++yy) { + for(xx = 0; xx < 8; ++xx, ++pos) { + int j = yy*32+xx*2; + subU[pos] = (U[j+0] + U[j+1] + U[j+16] + U[j+17]) * 0.25f; + subV[pos] = (V[j+0] + V[j+1] + V[j+16] + V[j+17]) * 0.25f; + } + } + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + } + } else { + for(y = 0; y < height; y += 8) { + for(x = 0; x < width; x += 8) { + float Y[64], U[64], V[64]; + for(row = y, pos = 0; row < y+8; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; + for(col = x; col < x+8; ++col, ++pos) { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width-1))*comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; + U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; + V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; + } + } + + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + } + + // Do the bit alignment of the EOI marker + stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); + } + + // EOI + stbiw__putc(s, 0xFF); + stbiw__putc(s, 0xD9); + + return 1; +} + +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); +} + + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +#endif // STB_IMAGE_WRITE_IMPLEMENTATION + +/* Revision history + 1.14 (2020-02-02) updated JPEG writer to downsample chroma channels + 1.13 + 1.12 + 1.11 (2019-08-11) + + 1.10 (2019-02-07) + support utf8 filenames in Windows; fix warnings and platform ifdefs + 1.09 (2018-02-11) + fix typo in zlib quality API, improve STB_I_W_STATIC in C++ + 1.08 (2018-01-29) + add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter + 1.07 (2017-07-24) + doc fix + 1.06 (2017-07-23) + writing JPEG (using Jon Olick's code) + 1.05 ??? + 1.04 (2017-03-03) + monochrome BMP expansion + 1.03 ??? + 1.02 (2016-04-02) + avoid allocating large structures on the stack + 1.01 (2016-01-16) + STBIW_REALLOC_SIZED: support allocators with no realloc support + avoid race-condition in crc initialization + minor compile issues + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support + 0.98 (2015-04-08) + added STBIW_MALLOC, STBIW_ASSERT etc + 0.97 (2015-01-18) + fixed HDR asserts, rewrote HDR rle logic + 0.96 (2015-01-17) + add HDR output + fix monochrome BMP + 0.95 (2014-08-17) + add monochrome TGA output + 0.94 (2014-05-31) + rename private functions to avoid conflicts with stb_image.h + 0.93 (2014-05-27) + warning fixes + 0.92 (2010-08-01) + casts to unsigned char to fix warnings + 0.91 (2010-07-17) + first public release + 0.90 first internal release +*/ + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ From c61e85d212100974c64b77aed1bf69bf99334d2c Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 13 Apr 2021 12:53:03 +0200 Subject: [PATCH 12/17] free image buffer after dump --- radio/src/tests/lcd_480x272.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/tests/lcd_480x272.cpp b/radio/src/tests/lcd_480x272.cpp index c7662ffba..ba0d030fe 100644 --- a/radio/src/tests/lcd_480x272.cpp +++ b/radio/src/tests/lcd_480x272.cpp @@ -52,6 +52,7 @@ void dumpImage(const std::string& filename, const BitmapBuffer* dc) uint8_t * img = (uint8_t *)malloc(pixels * 3); convert_RGB565_to_RGB888(img, dc->getPixelPtr(0,0), pixels); stbi_write_png(fullpath.c_str(), dc->width(), dc->height(), 3, img, stride); + free(img); } bool checkScreenshot_colorlcd(const BitmapBuffer* dc, const char* test) From 0ff81909a308de05c985338415b2a42a1273bd8f Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 13 Apr 2021 14:32:31 +0200 Subject: [PATCH 13/17] Make Pillow version used by Travis explicit This is the version which is currently used, but it might change (depending on what "pip" delivers). --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 48cf1c29e..445a27cf8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,7 +47,7 @@ before_install: - sudo ln -sf /usr/bin/python3 /usr/bin/python - python3 -m pip install pip==20.3.3 - sudo apt-get install python3-setuptools - - python3 -m pip install pillow clang + - python3 -m pip install pillow==7.2.0 clang script: - ./tools/commit-tests.sh From 578e53ad9069948b8d74e12aa136321392e01072 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 13 Apr 2021 15:17:01 +0200 Subject: [PATCH 14/17] Align the test images to those generated with Pillow 7.2.0 --- radio/src/tests/fonts_480x272.png | Bin 16095 -> 20246 bytes radio/src/tests/primitives_480x272.png | Bin 5666 -> 8836 bytes radio/src/tests/transparency_480x272.png | Bin 5763 -> 8933 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/radio/src/tests/fonts_480x272.png b/radio/src/tests/fonts_480x272.png index 7c02a46dd89c7c4495e79bf098671f81bb27d7e6..48964f3d12f61c3436fd3ba4a8513efcdfe50072 100644 GIT binary patch literal 20246 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 P-9a%C8^W7I3DtiAA&q`x literal 16095 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 diff --git a/radio/src/tests/primitives_480x272.png b/radio/src/tests/primitives_480x272.png index 3574c7ecf7af6d270e2e2381e6a8497c7b871156..260a1bb959c303509dcaefd85c0d6addc6bef803 100644 GIT binary patch literal 8836 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 N*r=q)ec@=){{V$tsk8t9 literal 5666 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 UX_sFK4}AgVCYCo#e|3rf8?hm&egFUf diff --git a/radio/src/tests/transparency_480x272.png b/radio/src/tests/transparency_480x272.png index 71e626f43839c79a0547b4db2599643e92d47d9d..c68c6585866adf22041d38b3ad393d9b17fc5a0e 100644 GIT binary patch literal 8933 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 C{Ri~` literal 5763 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 ZQl7Z?0)+C{B%rGdf|%R?O1N+>@gK3e(G&mx From c76ea252d4e9c102159a7fc3876d81f17200ee33 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 13 Apr 2021 16:08:05 +0200 Subject: [PATCH 15/17] font test picture update --- radio/src/tests/fonts_480x272.png | Bin 20246 -> 20235 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/radio/src/tests/fonts_480x272.png b/radio/src/tests/fonts_480x272.png index 48964f3d12f61c3436fd3ba4a8513efcdfe50072..bec2408243cc6d40300a5b99899f365d3e4e4b57 100644 GIT binary patch delta 2235 zcmYLJYgAKL7QXl9CILbSj{qTrNO&m>Xn^oggWe<%P=P^EK#Y0^DP9C^j8quLF*gDM zN`RoqAfpZt)TL4k7#%9=TuV?CAB48*LbL%<5ov{jR_mh^U0TnNz1CUh+voefefB=} zhrr51pw{)cx`vC3{;}!2l$axD$1zR*{8e>x?Fe{Ckz^u#+uxL(%4QCwSo}kD~ppRIolK}YGal}|on8+gd=Wn%P+W=AqZ zk5TG}bUhxQCGjPAyM)tlxhnH(jdbuMy+#fAz~gBMiP3Qv-!?oiDa)YYCRBbh7Y1WX zC{@BEAm=(@7u6GS97*JmmE2AtJK`h6BGE!S6O0L8rBsNP1yetPw+F48o%A2i{>as4dkds*@uc+f@RY>h?glVsZ<&@ z)v)nqwP{S~BL)inT=|{&7~bvDb%&XzU`IVh1ra0G;9$l;dcU99YazN#%CGY`EoPVM zcu2ApViRrNF9W$CqL@u@H_e7dOK=0kZWvB$Tces>jdtWV{S6pahzI^?o#_2u-TNb} zIeMpIoiOiLd2}=^SFT`o7T{C`So82|Umq*zs6`r93gnOl^4lB4(a^aRO8hjD8Nu@f z3pYlHzD#K;5Uj3J1GPMWGe8Je0xbm>50H&MKbV@hbk#*CsCCP8$_tJvjRy5&=I19} zZ^-QJ?IY!*cxeSW_+4}KGwf?OI_M<<3e`c}eTZ@m-MPv{aOkRLN662XCBV#$!Yn7|DyFnJUjVC|rl_zlJ5sRYb z><{R|<3VGM-1sWO;oEX;xyjRmt{O>2QbJCLED8#3oHOnQ5zb#ZWyb@Fd(n)uXzy-Q zmt`cHogtqZ5$WjSO{0aLoz_R45sJ(8>md|TB^DyP0Ia( zOu87-xaz#|WJYfaGB?Xd^I~%>dIqlVoeDQ%#{_Ih5&_4prA9SxgyaiD6@yf}C{heQ z3K!#!=XL&hABS0rT4_@eQh{ye`IpXJfyzeo&PvOh=2U-bZ7{(TMi!^M;2 z02BfRQ`3mI^~ReAM}PAe*oit@1KHcX7>{P+U?cs=-5VJ5(?0(=*k zL7lbZU#hJ!Li8;^3edA0R(}I+zR11Kjzdgbr!NjSFU_GvI;(Q4!N@b}Z?AGO+Ekp2 zO!WJMn~Xh!j`9q5$Cre*?D7Hy(`kD=%lDXTT4c|49~Q3b{atGZ4%%RSMi2Qe&Mqa3 z&hLO`tb?73>c%||h-|M32|0520A*}FKtel`J(GYyz23zO{wFLuWM#Xl?L?oYC$i|a zw(Ldhd4c|TOsIaM*I<|I3q--Qc6vP}1KywSo1K&!x5>7xmNx6q+CZVu^lZ|lCD%!#F(7OB()qA;g4h^*{g6Pc`UgMT?k9TLBII8N-wSx@o9j`ChR z<6_1yLRR}4R~DNXtVhSh=NOtyG?;E1Wk})rGdSjBe@$1WZSP5-aFfG}mi(kYxir0R zhUa^4qWJ>HIX&Dti)p|8y8xd&RYDM&lQ`BfO^96zcLh(Ydvpwprf&dlHMUX~WWDm9 zz5zNiTZZfh{(e zIgX=OhEe0~iJl1gEAs8m6pPava*`ccJHbb{Q6@v2CN~3RVff+Dp$6;e9bg!An$#U; zEcmZ=`1ksuC9)Npgb(f+jgXva6*(%}=bk(@mt=0bH9q~&yETJ%99L_)K}m_GI>@)E zEQJGfUxR5Fs;ihBbaPBaenuqa{~J?UE9VwPhkb+p%n&XFc~^(uz$P@p0AlFN;Irbfwf=J!ucT5R6dO zz-t#`kHa4Zu3n`+fvaudk!{wU>*n0wi3h5C4s-8Rd#59$*oD@1XCUe#(Nq5z7Se;dyiac1O zMdV2u%b*4+mwVoezt#rjm4mS?`M%6T^;Y))w)~Yt`XZoalO(qeq!qo{;SlIOb=wzV zK>d)RTY(2{HEX=|fjT)STi@%a-%x(kWYcyS7pXAAS^NM)mSLm8WDYig7p|nQwZ0~L zxprDtIUz-J_f&|Nl4b(<+i1A^npSp9#eERc1f0@YJ`kwot5ae884!BTJQWafhr_HK WD0(!MKldlVvX;dpMz@R6-Tw#T!g|U8 delta 2246 zcmXw4e>juv9)F%^Kg?`QifuM5KT?*4<>;l(7#oG7l6MEa?@*aUlnc4u+B0TMqm^PI z&5zO#IxGF~c8X^zNs*P)rP3PZZAEfkdh2wYuJb7G{l|S>_kDdo-_Pg!`F`)~K3ES{ z)q`p`@Wn-U$V1wl(TuEiEP6dWVIcjL4mGhju_5q*`;*g&wZ7L&^0~KE3PeIsDS#aW zPKE!3pz_ZA3B4i>P{fU0?eP2UrVHhUxEVTyE{?R&^~+L}Ybub!TS^7_rx zS;?aXv<&pr3NiZ^Xas!t21QIKcPxlS73*nG6K+=0_a~de02Od;7d%8=Q&XU(m1e_h z-)~maA!!ks%Stk7`)YJdukV&Ec|Y8q!d+vdqyY8za4NRT8q)=wxBs1Eb%KX$sQpp- zA@F60p8gb@=w`tlASis@3xrKsTx>WI43`mtD!mX_AiToq{_2_o3Y0C5hpGH;*=jHK zO(Hv&%q77I(S$E2V{hg^({<2YkCq;iUOiY60wEy8*i=J-gSCsu$V@^@%3Z(aVrRdETS2J@v2ZI^)v+5Bn$XbxEHN7>7dX(CXOGL z!sQ70;xlsKUTQWVbb^b0b)%%9O+`aVAWG+P7NzsR!cDaNqodVK@+fNiE*38qEYB_V z8?-@O%`j775Hx;L z6j)liJA29Ce00ROXy9*GiW46fowW^E>R`Lbhu(}g(l0Sv zTU&?H3%k;TYFbNT2*74(lb(zb2hs!F35*p!}rF9due3{wXzI5 zvBC1wGTn2+&bC&ui%3v;uKM$q^Z7)xTqQUB^q%mb3b{lzDyS=uq2q5>0BIB2WjT?6 zcd8)z3(}CPCB)Q;DKU+C+~V@@uNfhbz(+)hsX`ju>d*yMhuM(==N<0l0GOln&04f`KN9=fc`v<+=F_{eHZ3T?06T~hQ&h7sNTHR&#XZdnG{vNWsT z2e|u$^W#%Y2IzFJ&?{Uz_n+WR@lbR+A9AMtKK(hYB00IjvdQh^7%{cuI2NN$T1*SCb_!?OH0`V$tnR!=~DjOf`SRbuHU*+vrkw5*{dx6f5NF2e+Uw4B(P*>Mly z_X!@pas1<>h1I@w&CLmGhpL;P`=<+4P;#`WSiVws7RT;a2KhUQdZCA_Jd9cSvtR~j zZ->NJ4w;JCQnz(9-VMLRghAv9{@KP;x8)?A25*1bVw3d)jZe{vFg%9_cx??5fWm8M zwmaPNq~2!nq_H5Gp~PJM4Yq{xKjXc-O!2wv=Ub48Gyt2Jc=iCibw}Y=vPp;6

=8 zSVnKHK$|PRruaPNuirtBdtQ?MR+W0ARwDu`AW#@|W)GkEbsNAKqOa}zMIP(L4)v$F zoWj$X3B4NTU{%t{at{xZ49$t->I|W|(zm2+OibV)Wo*^H?w86P)CgesE7JZ-*{cS~ zc^Ya=$6ts6rScXlT%9ElP4ZP09=3%-mkc6Ui-^735Lorf{8;Wxg(jVW4T8DH1qhyg0iyd-%{A@eIkE~&b+HG2D6 Date: Tue, 13 Apr 2021 16:57:10 +0200 Subject: [PATCH 16/17] Disable font tests Not worth the effort. --- radio/src/tests/lcd_480x272.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/radio/src/tests/lcd_480x272.cpp b/radio/src/tests/lcd_480x272.cpp index ba0d030fe..28f22365b 100644 --- a/radio/src/tests/lcd_480x272.cpp +++ b/radio/src/tests/lcd_480x272.cpp @@ -172,6 +172,13 @@ TEST(Lcd_colorlcd, transparency) EXPECT_TRUE(checkScreenshot_colorlcd(&dc, "transparency")); } +// +// Fonts test are disabled, as they cause +// too much trouble (font are generated and never +// really the same based on which software / radio +// combination. +// +#if 0 TEST(Lcd_colorlcd, fonts) { loadFonts(); @@ -196,6 +203,7 @@ TEST(Lcd_colorlcd, fonts) EXPECT_TRUE(checkScreenshot_colorlcd(&dc, "fonts")); } +#endif TEST(Lcd_colorlcd, clipping) { From 0fda6699a86a7dabe0658e7263d27dafd6f43a16 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 14 Apr 2021 04:15:33 +0200 Subject: [PATCH 17/17] Re-enabled font tests with images depending on language --- .../{fonts_480x272.png => fonts_CN_480x272.png} | Bin radio/src/tests/fonts_EN_480x272.png | Bin 0 -> 20235 bytes radio/src/tests/lcd_480x272.cpp | 8 +++++--- ...es_480x272.png => primitives_CN_480x272.png} | Bin radio/src/tests/primitives_EN_480x272.png | Bin 0 -> 8836 bytes ..._480x272.png => transparency_CN_480x272.png} | Bin radio/src/tests/transparency_EN_480x272.png | Bin 0 -> 8933 bytes 7 files changed, 5 insertions(+), 3 deletions(-) rename radio/src/tests/{fonts_480x272.png => fonts_CN_480x272.png} (100%) create mode 100644 radio/src/tests/fonts_EN_480x272.png rename radio/src/tests/{primitives_480x272.png => primitives_CN_480x272.png} (100%) create mode 100644 radio/src/tests/primitives_EN_480x272.png rename radio/src/tests/{transparency_480x272.png => transparency_CN_480x272.png} (100%) create mode 100644 radio/src/tests/transparency_EN_480x272.png diff --git a/radio/src/tests/fonts_480x272.png b/radio/src/tests/fonts_CN_480x272.png similarity index 100% rename from radio/src/tests/fonts_480x272.png rename to radio/src/tests/fonts_CN_480x272.png diff --git a/radio/src/tests/fonts_EN_480x272.png b/radio/src/tests/fonts_EN_480x272.png new file mode 100644 index 0000000000000000000000000000000000000000..bec2408243cc6d40300a5b99899f365d3e4e4b57 GIT binary patch literal 20235 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 * dc->height(); auto stride = dc->width() * 3; @@ -129,7 +131,7 @@ TEST(Lcd_colorlcd, primitives) dc.drawHorizontalLine(30, 85, 70, SOLID, DEFAULT_COLOR); - EXPECT_TRUE(checkScreenshot_colorlcd(&dc, "primitives")); + EXPECT_TRUE(checkScreenshot_colorlcd(&dc, "primitives_" TRANSLATIONS)); } TEST(Lcd_colorlcd, transparency) @@ -169,7 +171,7 @@ TEST(Lcd_colorlcd, transparency) } - EXPECT_TRUE(checkScreenshot_colorlcd(&dc, "transparency")); + EXPECT_TRUE(checkScreenshot_colorlcd(&dc, "transparency_" TRANSLATIONS)); } // @@ -201,7 +203,7 @@ TEST(Lcd_colorlcd, fonts) dc.drawText(8, 208, "The quick brown fox jumps over the lazy dog", TITLE_BGCOLOR|OPACITY(4)); dc.drawText(5, 205, "The quick brown fox jumps over the lazy dog", TITLE_BGCOLOR|OPACITY(12)); - EXPECT_TRUE(checkScreenshot_colorlcd(&dc, "fonts")); + EXPECT_TRUE(checkScreenshot_colorlcd(&dc, "fonts_" TRANSLATIONS)); } #endif diff --git a/radio/src/tests/primitives_480x272.png b/radio/src/tests/primitives_CN_480x272.png similarity index 100% rename from radio/src/tests/primitives_480x272.png rename to radio/src/tests/primitives_CN_480x272.png diff --git a/radio/src/tests/primitives_EN_480x272.png b/radio/src/tests/primitives_EN_480x272.png new file mode 100644 index 0000000000000000000000000000000000000000..260a1bb959c303509dcaefd85c0d6addc6bef803 GIT binary patch literal 8836 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 N*r=q)ec@=){{V$tsk8t9 literal 0 HcmV?d00001 diff --git a/radio/src/tests/transparency_480x272.png b/radio/src/tests/transparency_CN_480x272.png similarity index 100% rename from radio/src/tests/transparency_480x272.png rename to radio/src/tests/transparency_CN_480x272.png diff --git a/radio/src/tests/transparency_EN_480x272.png b/radio/src/tests/transparency_EN_480x272.png new file mode 100644 index 0000000000000000000000000000000000000000..c68c6585866adf22041d38b3ad393d9b17fc5a0e GIT binary patch literal 8933 zcmd5?c~}$I+Mk&ulYJ3FfDj&f=1ki9TXICLqHuMYH({*sz|NQum(j4ic0Ag z4Wd@GK`9rl+F=oJPuz+-xRqA4V6C;S;y0l2s`tCswztpoeSc)~oIII1?>X;pdC!~V z$4m(4vcxO^0JxD6A+Z1ewvjIZ10p~9POi8D0B)6$A%RoUXQe#t{*K<@;lRZ$f)|%n zGD0c#G|CiWsPB^HM-5|lSV~5`9CM7cM;f<(-XN7x!xGHKV{??$uwazDkb~6T_r5_e zmQ+m}UB-AhLOb$+-vr@dVKALdpO$v(G}o|ZK#1JS%d4Za+itr^vylU%!5jIFYC??0(l$>%;|7hRKi< z6g)mSXdDz`uB!)Ia+lE+mW6Tq0}+I1IGR%1G$SY^GizN#wxg|3DBO879MyrRK+%x? zicOT=%h2?~AziM6+j0XL275CBy9??@!`>o#u>0()Dy?6CFr5!0O`Zi-8Q`S=tH@mc?+ z@9{izSL^6CTiT=NrVqFEy*M4JBDw6!Xej3GfI^lhZFe(_8q4 z?Fg(MMc4RV4M$S&$3dOYLE8gk@y;m*s5V^6sa;c5NB}ghl7k(@p4sU>mVplaY{4Yy z751ZkG2E*ska@rX?GCp*N{qc6mZdnS*Nbj0FeFEmBNFC$T2cSnDnT$-m0M{ugq=Um zAXF$#0nF*>wx2ujjN)6o1#2^F9S2uPhk)b2kIx{(%z_98(&!mkC9sv|3r5_pvvn{9 zG)nBK8PpEob{dpFrV`JRv=NV(EBs14R`UykBk-6B`{^*-^KSjy#9fXo z07nAnNhvy2x=NZTZK!X8T0T?Eff}=EgG9gqBGAU*+d{PDIN6&y^yBpxkXB;C=n$^y zE1nSFy#k;xz9k5$R1UPj8wBHJHAqyeDTNZl((p|ESZaoj0qg++;N9O~HNYI)tNlS< z0;?E0C^;3597SB~X7ZFG8+SsQ83#r>2!_e8Vk~4xKLCs{2Ua&khb?z9KNHX?KSTb& zGsdKXR3AB2DuX9+x_v7q;`6b`&`~^)uH;T^4O+!zPBf$OrnhJ(kFOpI}1~Oh`!e#WK5YvxT@lOW_3*B03 zkj|WuPH`p3@x>7!_oN(|M`15X>c2+*hd15f6RdL z&oJTqIBv*e+PE9Rm>%wSx;~4t=lS08%w|fm+_)fzX z0+07&hY!&CF2FLVd<3&|3;4l&_FZqpK_qJ|H0 z-~hPA9r*oY`_qXk(|3SbWEhXcx_}aiL?jXk<{>a>ZVG67K>y+!?Hx!@y(@_L*2RtX zeISJkj;mV_{l@SW4w1>gYJ1fVdbPb~bw~uYd;@U$1UT}V9nB8$)ZIt!`^-87?30bj z%(J^#Rp>Z*F3}vIf2N!3WA;FY&@Qt%S1=u*e($F1J#1$qC&92AyPl`A=};K5Tt}^x zf(g5S0ytP=%Q(1poPo0VYAwhQFl@mefG}IJ7H>!<^k8ADwCH`L=~D4V`cgDg+__*F z67W5pCy4+iPNt-AH6upt(@z#ER>+!#JykqIe|*9m4BQawP4NOUY+Y99yZ2^hLbruS z&|{F{7rYFc#B_WFT;@IURIKo5@gR(LD=R63Cu7N?&7RnGx;V$*9FP&9w`Imp6VIg9 zD-wawk<7rMVWh+#R} zO^;1Mfyj&0;GgNWBlw=~*oboJ4xscSJbF6n*3)&DV&>{A=#3I|m}X}xFwCeO{2bV| z1>92x6y64$yKd2MqGP4ggTf#UPaQ(rryv?A)dkej@%UrNE^DpKKF@XbYV4_l4R9M` z+oK?TAqDb>{9N?eTYOrQXzGT%{q3_(w;aPB>12E4y0EAZ@OIc0xnWACG`bmZ!ox5s zA+tiFQepjAoT*YLjnq7nCVTBf>EC092?i+2jyb7sCM8vNUKFBkEIV z6l9pPdn@?J$E2iIOhAK3fp;9q>jWxBpscKtZ}jDGWHHhY9R^o~^17mCLJJqU_8^qG z90L?_m+C$T-9@byw`PMIkY!k?S9#T;lx7~f38<7~GlZ@6N9VYg-(i<5(rAF6&BZK@ zL8xxBxp_I06B7+AQ3nM<=2qu~<-qo~qf`?QSc<#Q=QwpqRVEX>Pv(Zerbl?5ox&$j zZIQrCS2BjRxAxh0EYLMz{#3;o$|W+cu=|E4Xnw?SM~AJ*IjoQxK#A*t8YE}O1ENJ* zI%O(M#V-G-uM?pQAtRvtrnzD3)~#TgjeIEPb(-&rB)Lhk=E;8UCxPFd)qP4QBF+N? z<%VSR1|yj$#DBg233x-tkm$fhS4Re94%7 z{O>1#o51bZVxWSK9)`SE;*7XMnY9VSfWxr2k#vg*nGTGS%z^2*qd?AZ@{&3-C7MaI zqajA=G5&^4i0!R%^?=+-3Skho4T=X-D=FtcOHRVnUT7T{HIIm;oDBmuM+=sq5RC;* z9(X8$?$d@W0!45{e0H`|B5Q;L?pTWFfEwt~368hD^dsBSsnTm&!{Fh2uWC(8kOz7< zC+Huvj@9T9D4u&Ba@>Lq-zMWqbPL*-2gWOjeGVJGrpl=pZMbO~Acl^wwy`1LF{Z1l+OdbAaa#;RMSVc;c? z#c;F#(p7SnHM0>gWRB47R3%Q!m1$dBbDMSmdUbg1e=I@`_dkaHo#Ou9TtpyIEzk?`VLHp(m(GcZV9PGgC=zO>Y#Z5p^qa znH=H?O%zPT+O^W@g@iIqNn8t)D!80!-f^IJF@9fzZl#c_vWxyj-cpvS!sB+ghjL!9 zjgx8dlfVvNR&uQOzILMAe`^Y4P9k)BJ$3;b46(KIpct;=s2Bs)N*^~V)LNbsVr#>1~p^sPL2eDtlI33VD6rVFht*3f0Qm;ve z%kWHOBR(dxm3Fd=fcuqSqaMp9EMW#Qx{*Qp&MCJ=#m`8AY_z z(cm6<8cZb74WXs$>MRgfr3S?TXFLek8tTRYiX)iaz<6`ueE&9pXY!>?J-NmKvv3PC zTH3rF&9~BPW>XSsk;8=Vl4clcrU*7B7dKE_ zPtq#{hT*o!Hdv0o=_GA)yg)RLBV~UTthoq$#kE;k3b*ll`lKK;9sA9?14bV;Z)c zst;!FKCGvGOSm~*y3;ZtlM2P7H<<~_aJMgm>0)-bZ(~%o8RN_YF94rX&*yZmHmp$C zZ)@jA8A1s&g)9C<1^mL>E6j24q4?+5e|x2_fAI$4x=$WsJ&?HY!pg3x_q8)3s)Ixt zHqvx+_9$eT*F|;iC!SqYv3vq9<458x2ThlKy;RvjSdQodv-|`c6-!KWGOeU76jCvguSb3f{sfGit4VP##o@%+RLL0Vv!7}E; zPXo`o#YnDi1J_Ew7L{CemMEM7rAvpRHnb()RC5Bm8{7%anb3MSQdgzlEIwoeQm?rE zzzARK(q`rdK|cIl0U7!RyDe_;+OXd}2gW z?M8(yKmItiA~K=_oXLyYi2SUjk^6AWANiSZT-v5*%ddwgm`gwUuwyM}bHZWVf2_Ku zEJ+ySPsIj@qtkvgob{{_>i!_jd@E7I?D5h4T`wraH2zZ zTr+gI7@ER+v2g2F{P~6VHmb_4jjG;F6I(aYB9IQ~YfXJ`?NhxSRqwq@YtzdpL&h-* zY7F7$PP|+z=d8eCupxZo-IwFu6}{f(DiJs=3M{JFVr`?9%M|fQk^3p@n7sF#HDv|4 zfTv7xInyQD$J%F$2u7xhEqo9yP$>&y7yb~ozLu5VK7cJOXt7GI7w{DO}rhr6TSam{5ZX&50sfAYE07sX-+G!;n%>~{Po){Gj+xh8LFJV zd}dhlX^|EFC&Kly?>{i6mp+t#F1S|%U(vTgN)5|Xg>CHqH{|Ul^R-CT$&Gh$dhnSn z`y;EQS1k5N5~T_6g=W`j0{z8MmRFx#Fqi%t4!uJz-in^kuwN5x1q1J0yn56>CHq~h zSz%LHVgHFtd+QWILa`r-~=6GW7?NM=I4{lP%IiHKYnEtuof@zpn{Oc{Y$b zk~vSPDu93Nl@VYrL!BL`clMS@ZvQA3w#jCHnOo4PnZkScf_vNkUvLjW2ez-L^)t5j zAr?6&Fqxf)mjlld|jjDn`lzZV$T+htF04fM(=NHcleN0J1PF<4?e_F7$NDrn+- z7}RTKNak<#TlnfLC%SLZe_hFXgVgu4%bZ_XNkEIv12ITrC+KKr+Vk!tKheRr|ZEm)_7` zRq=1c`LEUJpSG73HoTw8XY@-#aQ?9yIqBG{Jske57!$jjPQTRitou&$-(h-xe~sMd zByAjAc7R=+`DwOmPl0cNpZ^}V7DI2CG0E-TI^SApaI#*|x#^G7 zIc;FOCSz+PgVQ>s(Usrw_)ON=+c)O5)YT0w+YlCv{qQ2#oiFQ4^uG)C?c?5CKzX+p zl4cjVj5NE@uLWxkNw18h_dJy*d6qERHR%G7-47mF5wCP2>JIFATo49$lf3{^M3&T C{Ri~` literal 0 HcmV?d00001