From 4c13c6dab0b87e41fa6f0e1598da5977675c4bbc Mon Sep 17 00:00:00 2001 From: Joel16 Date: Sat, 7 Jul 2018 00:59:50 -0500 Subject: [PATCH 01/33] Switch to C2D (1) --- Makefile | 255 +- include/C2D_helper.h | 36 + include/cia.h | 8 - include/common.h | 39 +- include/config.h | 13 + include/dir_list.h | 30 - include/dirbrowse.h | 36 + include/fs.h | 4 +- include/keyboard.h | 6 + include/language.h | 20 - include/menus/menu_delete.h | 9 - include/menus/menu_file_options.h | 9 - include/menus/menu_ftp.h | 4 +- include/menus/menu_gallery.h | 6 +- include/menus/menu_home.h | 6 - include/menus/menu_main.h | 5 +- include/menus/menu_music.h | 8 +- include/menus/menu_properties.h | 9 - include/menus/menu_settings.h | 9 - include/menus/menu_sort.h | 9 - include/menus/menu_textviewer.h | 6 - include/menus/menu_update.h | 14 - include/misc/battery.h | 7 - include/misc/clock.h | 10 - include/misc/keyboard.h | 6 - include/misc/status_bar.h | 6 - include/misc/touch.h | 9 - include/misc/wifi.h | 6 - include/net.h | 8 - include/pp2d/LICENSE | 21 - include/pp2d/README.md | 15 - include/pp2d/libnsbmp.h | 250 - include/pp2d/lodepng.h | 1769 ---- include/pp2d/pp2d.h | 493 -- include/pp2d/stb_image.h | 7462 ----------------- include/progress_bar.h | 6 +- include/{misc => }/screenshot.h | 0 include/status_bar.h | 6 + include/textures.h | 95 +- include/theme.h | 27 - include/touch.h | 9 + include/utils.h | 23 +- .../btn_material_light_check_off_normal.png | Bin 0 -> 142 bytes ...n_material_light_check_off_normal_dark.png | Bin 0 -> 142 bytes .../btn_material_light_check_on_normal.png | Bin 0 -> 301 bytes ...tn_material_light_check_on_normal_dark.png | Bin 0 -> 297 bytes .../btn_material_light_radio_off_normal.png | Bin 0 -> 824 bytes ...n_material_light_radio_off_normal_dark.png | Bin 0 -> 683 bytes .../btn_material_light_radio_on_normal.png | Bin 0 -> 1007 bytes ...tn_material_light_radio_on_normal_dark.png | Bin 0 -> 809 bytes .../btn_material_light_toggle_off_normal.png | Bin 0 -> 1568 bytes .../btn_material_light_toggle_on_normal.png | Bin 0 -> 1694 bytes ...n_material_light_toggle_on_normal_dark.png | Bin 0 -> 1686 bytes res/drawable/btn_playback_forward.png | Bin 0 -> 1392 bytes res/drawable/btn_playback_pause.png | Bin 0 -> 112 bytes res/drawable/btn_playback_play.png | Bin 0 -> 557 bytes res/drawable/btn_playback_repeat.png | Bin 0 -> 1262 bytes res/drawable/btn_playback_repeat_overlay.png | Bin 0 -> 1262 bytes res/drawable/btn_playback_rewind.png | Bin 0 -> 1327 bytes res/drawable/btn_playback_shuffle.png | Bin 0 -> 1581 bytes res/drawable/btn_playback_shuffle_overlay.png | Bin 0 -> 1581 bytes res/drawable/ic_arrow_back_normal.png | Bin 0 -> 271 bytes res/drawable/ic_fso_default.png | Bin 0 -> 1061 bytes res/drawable/ic_fso_folder.png | Bin 0 -> 496 bytes res/drawable/ic_fso_folder_dark.png | Bin 0 -> 477 bytes res/drawable/ic_fso_type_audio.png | Bin 0 -> 797 bytes res/drawable/ic_fso_type_compress.png | Bin 0 -> 426 bytes res/drawable/ic_fso_type_document.png | Bin 0 -> 721 bytes res/drawable/ic_fso_type_executable.png | Bin 0 -> 1119 bytes res/drawable/ic_fso_type_image.png | Bin 0 -> 800 bytes res/drawable/ic_fso_type_text.png | Bin 0 -> 786 bytes .../drawable/ic_material_dialog.png | Bin 15230 -> 20552 bytes res/drawable/ic_material_dialog_dark.png | Bin 0 -> 5594 bytes .../ic_material_light_contextual_action.png | Bin 0 -> 625 bytes res/drawable/ic_material_light_filesystem.png | Bin 0 -> 180 bytes .../ic_material_light_filesystem_dark.png | Bin 0 -> 180 bytes res/drawable/ic_material_light_home.png | Bin 0 -> 268 bytes res/drawable/ic_material_light_home_dark.png | Bin 0 -> 263 bytes .../ic_material_light_navigation_drawer.png | Bin 0 -> 300 bytes res/drawable/ic_material_light_remote.png | Bin 0 -> 441 bytes .../ic_material_light_remote_dark.png | Bin 0 -> 429 bytes res/drawable/ic_material_light_sdcard.png | Bin 0 -> 240 bytes .../ic_material_light_sdcard_dark.png | Bin 0 -> 240 bytes res/drawable/ic_material_light_search.png | Bin 0 -> 351 bytes res/drawable/ic_material_light_secure.png | Bin 0 -> 527 bytes .../ic_material_light_secure_dark.png | Bin 0 -> 491 bytes res/drawable/ic_material_light_settings.png | Bin 0 -> 745 bytes .../ic_material_light_settings_dark.png | Bin 0 -> 669 bytes res/drawable/ic_material_options_dialog.png | Bin 0 -> 959 bytes .../ic_material_options_dialog_dark.png | Bin 0 -> 1036 bytes .../ic_material_properties_dialog.png | Bin 0 -> 769 bytes .../ic_material_properties_dialog_dark.png | Bin 0 -> 884 bytes res/drawable/ic_music_bg_bottom.png | Bin 0 -> 34667 bytes res/drawable/ic_music_bg_top.png | Bin 0 -> 46256 bytes res/drawable/sprites.t3s | 51 + resources/.build.sh | 9 - resources/.travis-deps.sh | 76 - resources/banner.png | Bin 10753 -> 0 bytes resources/banner.wav | Bin 362020 -> 0 bytes resources/cia.rsf | 286 - resources/ic_launcher_filemanager.png | Bin 18807 -> 0 bytes resources/logo.lz11 | Bin 8192 -> 0 bytes romfs/res/drawable/battery/0.png | Bin 14722 -> 0 bytes romfs/res/drawable/battery/100.png | Bin 14576 -> 0 bytes romfs/res/drawable/battery/15.png | Bin 15537 -> 0 bytes romfs/res/drawable/battery/28.png | Bin 14641 -> 0 bytes romfs/res/drawable/battery/43.png | Bin 14637 -> 0 bytes romfs/res/drawable/battery/57.png | Bin 14632 -> 0 bytes romfs/res/drawable/battery/71.png | Bin 14623 -> 0 bytes romfs/res/drawable/battery/85.png | Bin 14606 -> 0 bytes romfs/res/drawable/battery/charge.png | Bin 15669 -> 0 bytes romfs/res/drawable/btn_material_check_off.png | Bin 14607 -> 0 bytes romfs/res/drawable/btn_material_check_on.png | Bin 14852 -> 0 bytes romfs/res/drawable/btn_material_radio_off.png | Bin 3385 -> 0 bytes romfs/res/drawable/btn_material_radio_on.png | Bin 3213 -> 0 bytes .../res/drawable/btn_material_toggle_off.png | Bin 658 -> 0 bytes romfs/res/drawable/btn_material_toggle_on.png | Bin 976 -> 0 bytes romfs/res/drawable/gallery/ic_gallery_bar.png | Bin 1028 -> 0 bytes romfs/res/drawable/ic_background.png | Bin 2612 -> 0 bytes romfs/res/drawable/ic_dialog.png | Bin 15647 -> 0 bytes romfs/res/drawable/ic_fso_type_app.png | Bin 15833 -> 0 bytes romfs/res/drawable/ic_fso_type_archive.png | Bin 15268 -> 0 bytes romfs/res/drawable/ic_fso_type_audio.png | Bin 15599 -> 0 bytes romfs/res/drawable/ic_fso_type_file.png | Bin 16369 -> 0 bytes romfs/res/drawable/ic_fso_type_folder.png | Bin 16003 -> 0 bytes romfs/res/drawable/ic_fso_type_img.png | Bin 15640 -> 0 bytes romfs/res/drawable/ic_fso_type_system.png | Bin 15828 -> 0 bytes romfs/res/drawable/ic_fso_type_text.png | Bin 15349 -> 0 bytes romfs/res/drawable/ic_launcher_themes.png | Bin 15494 -> 0 bytes romfs/res/drawable/ic_material_light_ftp.png | Bin 15493 -> 0 bytes romfs/res/drawable/ic_material_light_home.png | Bin 15458 -> 0 bytes romfs/res/drawable/ic_material_light_nand.png | Bin 15548 -> 0 bytes .../drawable/ic_material_light_options.png | Bin 15134 -> 0 bytes romfs/res/drawable/ic_material_light_sd.png | Bin 15301 -> 0 bytes .../res/drawable/ic_material_light_search.png | Bin 14833 -> 0 bytes .../drawable/ic_material_light_settings.png | Bin 16258 -> 0 bytes romfs/res/drawable/ic_options.png | Bin 16025 -> 0 bytes romfs/res/drawable/ic_overlay_ftp.png | Bin 15421 -> 0 bytes romfs/res/drawable/ic_overlay_home.png | Bin 15403 -> 0 bytes romfs/res/drawable/ic_overlay_nand.png | Bin 15404 -> 0 bytes romfs/res/drawable/ic_overlay_options.png | Bin 15133 -> 0 bytes romfs/res/drawable/ic_overlay_sd.png | Bin 15251 -> 0 bytes romfs/res/drawable/ic_overlay_settings.png | Bin 16137 -> 0 bytes romfs/res/drawable/ic_overlay_update.png | Bin 15216 -> 0 bytes romfs/res/drawable/ic_properties.png | Bin 15777 -> 0 bytes romfs/res/drawable/ic_selector.png | Bin 14697 -> 0 bytes .../res/drawable/music/ic_music_bg_bottom.png | Bin 34938 -> 0 bytes romfs/res/drawable/music/ic_music_bg_top.png | Bin 61783 -> 0 bytes romfs/res/drawable/music/ic_music_forward.png | Bin 4454 -> 0 bytes romfs/res/drawable/music/ic_music_pause.png | Bin 3380 -> 0 bytes romfs/res/drawable/music/ic_music_play.png | Bin 3913 -> 0 bytes romfs/res/drawable/music/ic_music_repeat.png | Bin 5040 -> 0 bytes romfs/res/drawable/music/ic_music_rewind.png | Bin 4554 -> 0 bytes romfs/res/drawable/music/ic_music_shuffle.png | Bin 5427 -> 0 bytes romfs/res/drawable/music/ic_music_status.png | Bin 3014 -> 0 bytes romfs/res/drawable/textviewer/textview.png | Bin 1606 -> 0 bytes .../drawable/wifi/stat_sys_wifi_signal_0.png | Bin 14686 -> 0 bytes .../drawable/wifi/stat_sys_wifi_signal_1.png | Bin 14689 -> 0 bytes .../drawable/wifi/stat_sys_wifi_signal_2.png | Bin 14685 -> 0 bytes .../drawable/wifi/stat_sys_wifi_signal_3.png | Bin 14642 -> 0 bytes .../wifi/stat_sys_wifi_signal_null.png | Bin 14675 -> 0 bytes source/C2D_helper.c | 53 + source/cia.c | 116 - source/config.c | 105 + source/dir_list.c | 378 - source/dirbrowse.c | 354 + source/fs.c | 4 +- source/{misc => }/keyboard.c | 4 +- source/language.c | 194 - source/main.c | 77 +- source/menus/menu_delete.c | 96 - source/menus/menu_file_options.c | 448 - source/menus/menu_ftp.c | 95 +- source/menus/menu_gallery.c | 449 +- source/menus/menu_home.c | 6 - source/menus/menu_main.c | 411 +- source/menus/menu_music.c | 97 +- source/menus/menu_properties.c | 82 - source/menus/menu_settings.c | 95 - source/menus/menu_sort.c | 96 - source/menus/menu_textviewer.c | 273 - source/menus/menu_update.c | 153 - source/misc/battery.c | 52 - source/misc/clock.c | 67 - source/misc/status_bar.c | 22 - source/misc/wifi.c | 30 - source/net.c | 161 - source/pp2d/LICENSE | 21 - source/pp2d/README.md | 15 - source/pp2d/libnsbmp.c | 1371 --- source/pp2d/lodepng.c | 6245 -------------- source/pp2d/pp2d.c | 969 --- source/pp2d/vshader.v.pica | 39 - source/progress_bar.c | 37 +- source/{misc => }/screenshot.c | 0 source/status_bar.c | 42 + source/texture.c | 78 - source/textures.c | 68 + source/theme.c | 257 - source/{misc => }/touch.c | 4 +- source/utils.c | 201 +- 201 files changed, 1611 insertions(+), 22845 deletions(-) create mode 100644 include/C2D_helper.h delete mode 100644 include/cia.h create mode 100644 include/config.h delete mode 100644 include/dir_list.h create mode 100644 include/dirbrowse.h create mode 100644 include/keyboard.h delete mode 100644 include/language.h delete mode 100644 include/menus/menu_delete.h delete mode 100644 include/menus/menu_file_options.h delete mode 100644 include/menus/menu_home.h delete mode 100644 include/menus/menu_properties.h delete mode 100644 include/menus/menu_settings.h delete mode 100644 include/menus/menu_sort.h delete mode 100644 include/menus/menu_textviewer.h delete mode 100644 include/menus/menu_update.h delete mode 100644 include/misc/battery.h delete mode 100644 include/misc/clock.h delete mode 100644 include/misc/keyboard.h delete mode 100644 include/misc/status_bar.h delete mode 100644 include/misc/touch.h delete mode 100644 include/misc/wifi.h delete mode 100644 include/net.h delete mode 100644 include/pp2d/LICENSE delete mode 100644 include/pp2d/README.md delete mode 100644 include/pp2d/libnsbmp.h delete mode 100644 include/pp2d/lodepng.h delete mode 100644 include/pp2d/pp2d.h delete mode 100644 include/pp2d/stb_image.h rename include/{misc => }/screenshot.h (100%) create mode 100644 include/status_bar.h delete mode 100644 include/theme.h create mode 100644 include/touch.h create mode 100644 res/drawable/btn_material_light_check_off_normal.png create mode 100644 res/drawable/btn_material_light_check_off_normal_dark.png create mode 100644 res/drawable/btn_material_light_check_on_normal.png create mode 100644 res/drawable/btn_material_light_check_on_normal_dark.png create mode 100644 res/drawable/btn_material_light_radio_off_normal.png create mode 100644 res/drawable/btn_material_light_radio_off_normal_dark.png create mode 100644 res/drawable/btn_material_light_radio_on_normal.png create mode 100644 res/drawable/btn_material_light_radio_on_normal_dark.png create mode 100644 res/drawable/btn_material_light_toggle_off_normal.png create mode 100644 res/drawable/btn_material_light_toggle_on_normal.png create mode 100644 res/drawable/btn_material_light_toggle_on_normal_dark.png create mode 100644 res/drawable/btn_playback_forward.png create mode 100644 res/drawable/btn_playback_pause.png create mode 100644 res/drawable/btn_playback_play.png create mode 100644 res/drawable/btn_playback_repeat.png create mode 100644 res/drawable/btn_playback_repeat_overlay.png create mode 100644 res/drawable/btn_playback_rewind.png create mode 100644 res/drawable/btn_playback_shuffle.png create mode 100644 res/drawable/btn_playback_shuffle_overlay.png create mode 100644 res/drawable/ic_arrow_back_normal.png create mode 100644 res/drawable/ic_fso_default.png create mode 100644 res/drawable/ic_fso_folder.png create mode 100644 res/drawable/ic_fso_folder_dark.png create mode 100644 res/drawable/ic_fso_type_audio.png create mode 100644 res/drawable/ic_fso_type_compress.png create mode 100644 res/drawable/ic_fso_type_document.png create mode 100644 res/drawable/ic_fso_type_executable.png create mode 100644 res/drawable/ic_fso_type_image.png create mode 100644 res/drawable/ic_fso_type_text.png rename romfs/res/drawable/ic_material_light_update.png => res/drawable/ic_material_dialog.png (66%) create mode 100644 res/drawable/ic_material_dialog_dark.png create mode 100644 res/drawable/ic_material_light_contextual_action.png create mode 100644 res/drawable/ic_material_light_filesystem.png create mode 100644 res/drawable/ic_material_light_filesystem_dark.png create mode 100644 res/drawable/ic_material_light_home.png create mode 100644 res/drawable/ic_material_light_home_dark.png create mode 100644 res/drawable/ic_material_light_navigation_drawer.png create mode 100644 res/drawable/ic_material_light_remote.png create mode 100644 res/drawable/ic_material_light_remote_dark.png create mode 100644 res/drawable/ic_material_light_sdcard.png create mode 100644 res/drawable/ic_material_light_sdcard_dark.png create mode 100644 res/drawable/ic_material_light_search.png create mode 100644 res/drawable/ic_material_light_secure.png create mode 100644 res/drawable/ic_material_light_secure_dark.png create mode 100644 res/drawable/ic_material_light_settings.png create mode 100644 res/drawable/ic_material_light_settings_dark.png create mode 100644 res/drawable/ic_material_options_dialog.png create mode 100644 res/drawable/ic_material_options_dialog_dark.png create mode 100644 res/drawable/ic_material_properties_dialog.png create mode 100644 res/drawable/ic_material_properties_dialog_dark.png create mode 100644 res/drawable/ic_music_bg_bottom.png create mode 100644 res/drawable/ic_music_bg_top.png create mode 100644 res/drawable/sprites.t3s delete mode 100644 resources/.build.sh delete mode 100644 resources/.travis-deps.sh delete mode 100644 resources/banner.png delete mode 100644 resources/banner.wav delete mode 100644 resources/cia.rsf delete mode 100644 resources/ic_launcher_filemanager.png delete mode 100644 resources/logo.lz11 delete mode 100644 romfs/res/drawable/battery/0.png delete mode 100644 romfs/res/drawable/battery/100.png delete mode 100644 romfs/res/drawable/battery/15.png delete mode 100644 romfs/res/drawable/battery/28.png delete mode 100644 romfs/res/drawable/battery/43.png delete mode 100644 romfs/res/drawable/battery/57.png delete mode 100644 romfs/res/drawable/battery/71.png delete mode 100644 romfs/res/drawable/battery/85.png delete mode 100644 romfs/res/drawable/battery/charge.png delete mode 100644 romfs/res/drawable/btn_material_check_off.png delete mode 100644 romfs/res/drawable/btn_material_check_on.png delete mode 100644 romfs/res/drawable/btn_material_radio_off.png delete mode 100644 romfs/res/drawable/btn_material_radio_on.png delete mode 100644 romfs/res/drawable/btn_material_toggle_off.png delete mode 100644 romfs/res/drawable/btn_material_toggle_on.png delete mode 100644 romfs/res/drawable/gallery/ic_gallery_bar.png delete mode 100644 romfs/res/drawable/ic_background.png delete mode 100644 romfs/res/drawable/ic_dialog.png delete mode 100644 romfs/res/drawable/ic_fso_type_app.png delete mode 100644 romfs/res/drawable/ic_fso_type_archive.png delete mode 100644 romfs/res/drawable/ic_fso_type_audio.png delete mode 100644 romfs/res/drawable/ic_fso_type_file.png delete mode 100644 romfs/res/drawable/ic_fso_type_folder.png delete mode 100644 romfs/res/drawable/ic_fso_type_img.png delete mode 100644 romfs/res/drawable/ic_fso_type_system.png delete mode 100644 romfs/res/drawable/ic_fso_type_text.png delete mode 100644 romfs/res/drawable/ic_launcher_themes.png delete mode 100644 romfs/res/drawable/ic_material_light_ftp.png delete mode 100644 romfs/res/drawable/ic_material_light_home.png delete mode 100644 romfs/res/drawable/ic_material_light_nand.png delete mode 100644 romfs/res/drawable/ic_material_light_options.png delete mode 100644 romfs/res/drawable/ic_material_light_sd.png delete mode 100644 romfs/res/drawable/ic_material_light_search.png delete mode 100644 romfs/res/drawable/ic_material_light_settings.png delete mode 100644 romfs/res/drawable/ic_options.png delete mode 100644 romfs/res/drawable/ic_overlay_ftp.png delete mode 100644 romfs/res/drawable/ic_overlay_home.png delete mode 100644 romfs/res/drawable/ic_overlay_nand.png delete mode 100644 romfs/res/drawable/ic_overlay_options.png delete mode 100644 romfs/res/drawable/ic_overlay_sd.png delete mode 100644 romfs/res/drawable/ic_overlay_settings.png delete mode 100644 romfs/res/drawable/ic_overlay_update.png delete mode 100644 romfs/res/drawable/ic_properties.png delete mode 100644 romfs/res/drawable/ic_selector.png delete mode 100644 romfs/res/drawable/music/ic_music_bg_bottom.png delete mode 100644 romfs/res/drawable/music/ic_music_bg_top.png delete mode 100644 romfs/res/drawable/music/ic_music_forward.png delete mode 100644 romfs/res/drawable/music/ic_music_pause.png delete mode 100644 romfs/res/drawable/music/ic_music_play.png delete mode 100644 romfs/res/drawable/music/ic_music_repeat.png delete mode 100644 romfs/res/drawable/music/ic_music_rewind.png delete mode 100644 romfs/res/drawable/music/ic_music_shuffle.png delete mode 100644 romfs/res/drawable/music/ic_music_status.png delete mode 100644 romfs/res/drawable/textviewer/textview.png delete mode 100644 romfs/res/drawable/wifi/stat_sys_wifi_signal_0.png delete mode 100644 romfs/res/drawable/wifi/stat_sys_wifi_signal_1.png delete mode 100644 romfs/res/drawable/wifi/stat_sys_wifi_signal_2.png delete mode 100644 romfs/res/drawable/wifi/stat_sys_wifi_signal_3.png delete mode 100644 romfs/res/drawable/wifi/stat_sys_wifi_signal_null.png create mode 100644 source/C2D_helper.c delete mode 100644 source/cia.c create mode 100644 source/config.c delete mode 100644 source/dir_list.c create mode 100644 source/dirbrowse.c rename source/{misc => }/keyboard.c (91%) delete mode 100644 source/language.c delete mode 100644 source/menus/menu_delete.c delete mode 100644 source/menus/menu_file_options.c delete mode 100644 source/menus/menu_home.c delete mode 100644 source/menus/menu_properties.c delete mode 100644 source/menus/menu_settings.c delete mode 100644 source/menus/menu_sort.c delete mode 100644 source/menus/menu_textviewer.c delete mode 100644 source/menus/menu_update.c delete mode 100644 source/misc/battery.c delete mode 100644 source/misc/clock.c delete mode 100644 source/misc/status_bar.c delete mode 100644 source/misc/wifi.c delete mode 100644 source/net.c delete mode 100644 source/pp2d/LICENSE delete mode 100644 source/pp2d/README.md delete mode 100644 source/pp2d/libnsbmp.c delete mode 100644 source/pp2d/lodepng.c delete mode 100644 source/pp2d/pp2d.c delete mode 100644 source/pp2d/vshader.v.pica rename source/{misc => }/screenshot.c (100%) create mode 100644 source/status_bar.c delete mode 100644 source/texture.c create mode 100644 source/textures.c delete mode 100644 source/theme.c rename source/{misc => }/touch.c (79%) diff --git a/Makefile b/Makefile index 5c71026..48dee92 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,10 @@ include $(DEVKITARM)/3ds_rules # SOURCES is a list of directories containing source code # DATA is a list of directories containing data files # INCLUDES is a list of directories containing header files +# GRAPHICS is a list of directories containing graphics files +# GFXBUILD is the directory where converted graphics files will be placed +# If set to $(BUILD), it will statically link in the converted +# files as if they were data files. # # NO_SMDH: if set to anything, no SMDH file is generated. # ROMFS is the directory which contains the RomFS, relative to the Makefile (Optional) @@ -27,69 +31,38 @@ include $(DEVKITARM)/3ds_rules # - icon.png # - /default_icon.png #--------------------------------------------------------------------------------- - -# CIA -APP_TITLE := 3DShell -APP_PRODUCT_CODE := CTR-3D-SHEL -APP_ROMFS := romfs -APP_CATEGORY := Application -APP_UNIQUE_ID := 0x16200 -APP_USE_ON_SD := true -APP_ENCRYPTED := false -APP_MEMORY_TYPE := Application -APP_SYSTEM_MODE := 64MB -APP_SYSTEM_MODE_EXT := Legacy -APP_CPU_SPEED := 268MHz -APP_ENABLE_L2_CACHE := true -VERSION_MAJOR := 3 -VERSION_MINOR := 0 -VERSION_MICRO := 0 -APP_RSF_FILE := resources/cia.rsf - -APP_DESCRIPTION := Multi purpose file manager GUI -APP_AUTHOR := Joel16 - -TARGET := $(subst $e ,_,$(notdir $(APP_TITLE))) -OUTDIR := out -BUILD := build -SOURCES := source source/audio source/ftp source/menus source/minizip source/misc source/pp2d +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source source/audio source/ftp source/menus source/minizip source/menus/menu_book_reader DATA := data -INCLUDES := include include/audio include/dr_libs include/ftp include/menus include/minizip include/misc include/pp2d - -ICON := resources/ic_launcher_filemanager.png -BANNER := resources/banner.png -BANNER_AUDIO := resources/banner.wav -LOGO := resources/logo.lz11 -ICON_FLAGS := nosavebackups,visible - -GITVERSION := $(shell git log -1 --pretty='%h') +INCLUDES := include include/audio include/dr_libs include/ftp include/menus include/minizip mupdf/include mupdf/source/fitz +GRAPHICS := res/drawable +ROMFS := romfs +GFXBUILD := $(ROMFS)/res/drawable #--------------------------------------------------------------------------------- # options for code generation #--------------------------------------------------------------------------------- -ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft +ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft -CFLAGS := -g -Werror -O2 -mword-relocations \ - -fomit-frame-pointer -ffunction-sections \ - -DVERSION_MAJOR=$(VERSION_MAJOR) -DVERSION_MINOR=$(VERSION_MINOR) -DVERSION_MICRO=$(VERSION_MICRO) \ - -DAPP_TITLE="\"$(APP_TITLE)\"" \ - -DGITVERSION="\"${GITVERSION}\"" \ - $(ARCH) +CFLAGS := -g -Werror -O2 -mword-relocations \ + -fomit-frame-pointer -ffunction-sections \ + $(ARCH) -CFLAGS += $(INCLUDE) -DARM11 -D_3DS +CFLAGS += $(INCLUDE) -DARM11 -D_3DS -CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 -ASFLAGS := -g $(ARCH) -LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) -LIBS := -lcurl -lmbedtls -lmbedx509 -lmbedcrypto -lmpg123 -lvorbisidec -logg -lcitro3d -lctru -lm -lz +LIBS := -lturbojpeg -lpng -lcitro2d -lcitro3d -lctru -lm -lz #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing # include and lib #--------------------------------------------------------------------------------- -LIBDIRS := $(CTRULIB) $(PORTLIBS) +LIBDIRS := $(PORTLIBS) $(CTRULIB) #--------------------------------------------------------------------------------- # no real need to edit anything past this point unless you need to add additional @@ -98,44 +71,69 @@ LIBDIRS := $(CTRULIB) $(PORTLIBS) ifneq ($(BUILD),$(notdir $(CURDIR))) #--------------------------------------------------------------------------------- -export OUTPUT := $(CURDIR)/$(OUTDIR)/$(TARGET) -export TOPDIR := $(CURDIR) +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) -export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ - $(foreach dir,$(DATA),$(CURDIR)/$(dir)) +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) export DEPSDIR := $(CURDIR)/$(BUILD) -CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) -CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) -SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) -PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica))) -SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist))) -BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica))) +SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist))) +GFXFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.t3s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) #--------------------------------------------------------------------------------- # use CXX for linking C++ projects, CC for standard C #--------------------------------------------------------------------------------- ifeq ($(strip $(CPPFILES)),) #--------------------------------------------------------------------------------- - export LD := $(CC) + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +#--------------------------------------------------------------------------------- +ifeq ($(GFXBUILD),$(BUILD)) +#--------------------------------------------------------------------------------- +export T3XFILES := $(GFXFILES:.t3s=.t3x) #--------------------------------------------------------------------------------- else #--------------------------------------------------------------------------------- - export LD := $(CXX) +export ROMFS_T3XFILES := $(patsubst %.t3s, $(GFXBUILD)/%.t3x, $(GFXFILES)) +export T3XHFILES := $(patsubst %.t3s, $(BUILD)/%.h, $(GFXFILES)) #--------------------------------------------------------------------------------- endif #--------------------------------------------------------------------------------- -export OFILES := $(addsuffix .o,$(BINFILES)) \ +export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) \ $(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) \ - $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + $(addsuffix .o,$(T3XFILES)) + +export OFILES := $(OFILES_BIN) $(OFILES_SOURCES) -export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ +export HFILES := $(PICAFILES:.v.pica=_shbin.h) $(SHLISTFILES:.shlist=_shbin.h) \ + $(addsuffix .h,$(subst .,_,$(BINFILES))) \ + $(GFXFILES:.t3s=.h) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ -I$(CURDIR)/$(BUILD) -export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +export _3DSXDEPS := $(if $(NO_SMDH),,$(OUTPUT).smdh) ifeq ($(strip $(ICON)),) icons := $(wildcard *.png) @@ -151,106 +149,67 @@ else endif ifeq ($(strip $(NO_SMDH)),) - export _3DSXFLAGS += --smdh=$(OUTPUT).smdh + export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh endif -ifneq ("$(wildcard $(APP_ROMFS))","") - _3DSXFLAGS += --romfs=$(APP_ROMFS) +ifneq ($(ROMFS),) + export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS) endif -ifeq ($(strip $(VERSION_MAJOR)),) - VERSION_MAJOR := 0 -endif - -ifeq ($(strip $(VERSION_MINOR)),) - VERSION_MINOR := 0 -endif - -ifeq ($(strip $(VERSION_MICRO)),) - VERSION_MICRO := 0 -endif - -.PHONY: $(BUILD) clean all +.PHONY: all clean #--------------------------------------------------------------------------------- -all: 3dsx cia - @echo "${VERSION_MAJOR}${VERSION_MINOR}${VERSION_MICRO}" > UPDATE_MILESTONE.txt # For maintainer builds - @echo "${GITVERSION}" > UPDATE_NIGHTLY.txt # For maintainer builds +all: $(BUILD) $(GFXBUILD) $(DEPSDIR) $(ROMFS_T3XFILES) $(T3XHFILES) + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile -3dsx: $(BUILD) $(OUTPUT).3dsx +$(BUILD): + @mkdir -p $@ -cia : $(BUILD) $(OUTPUT).cia +ifneq ($(GFXBUILD),$(BUILD)) +$(GFXBUILD): + @mkdir -p $@ +endif -citra: export CITRA_MODE = 1 -citra: 3dsx -#--------------------------------------------------------------------------------- -$(BUILD): - @mkdir -p $(OUTDIR) - @[ -d "$@" ] || mkdir -p "$@" - @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile +ifneq ($(DEPSDIR),$(BUILD)) +$(DEPSDIR): + @mkdir -p $@ +endif #--------------------------------------------------------------------------------- clean: @echo clean ... - @rm -fr $(BUILD) $(OUTDIR) + @rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf $(GFXBUILD) #--------------------------------------------------------------------------------- -ifeq ($(strip $(NO_SMDH)),) -$(OUTPUT).3dsx : $(OUTPUT).elf $(OUTPUT).smdh -else -$(OUTPUT).3dsx : $(OUTPUT).elf -endif - +$(GFXBUILD)/%.t3x $(BUILD)/%.h : %.t3s #--------------------------------------------------------------------------------- -MAKEROM ?= makerom -MAKEROM_ARGS := -rsf "$(APP_RSF_FILE)" -target t -exefslogo -icon "$(BUILD)/icon.icn" -banner "$(BUILD)/banner.bnr" -major $(VERSION_MAJOR) -minor $(VERSION_MINOR) -micro $(VERSION_MICRO) -DAPP_TITLE="$(APP_TITLE)" -DAPP_PRODUCT_CODE="$(APP_PRODUCT_CODE)" -DAPP_UNIQUE_ID="$(APP_UNIQUE_ID)" -DAPP_SYSTEM_MODE="$(APP_SYSTEM_MODE)" -DAPP_SYSTEM_MODE_EXT="$(APP_SYSTEM_MODE_EXT)" -DAPP_CATEGORY="$(APP_CATEGORY)" -DAPP_USE_ON_SD="$(APP_USE_ON_SD)" -DAPP_MEMORY_TYPE="$(APP_MEMORY_TYPE)" -DAPP_CPU_SPEED="$(APP_CPU_SPEED)" -DAPP_ENABLE_L2_CACHE="$(APP_ENABLE_L2_CACHE)" -DAPP_VERSION_MAJOR=$(VERSION_MAJOR) - -ifneq ("$(wildcard $(LOGO))","") - MAKEROM_ARGS += -logo "$(LOGO)" -endif - -ifneq ("$(wildcard $(APP_ROMFS))","") - MAKEROM_ARGS += -DAPP_ROMFS="$(APP_ROMFS)" -endif - -$(OUTPUT).cia: $(OUTPUT).elf $(BUILD)/banner.bnr $(BUILD)/icon.icn - $(MAKEROM) -f cia -o $@ -elf $< -DAPP_ENCRYPTED=false $(MAKEROM_ARGS) - -BANNERTOOL ?= bannertool - -ifeq ($(suffix $(BANNER)),.cgfx) - BANNER_ARG := -ci -else - BANNER_ARG := -i -endif - -ifeq ($(suffix $(BANNER_AUDIO)),.cwav) - BANNER_AUDIO_ARG := -ca -else - BANNER_AUDIO_ARG := -a -endif - -$(BUILD)/banner.bnr : $(BANNER) $(BANNER_AUDIO) - $(BANNERTOOL) makebanner $(BANNER_ARG) "$(BANNER)" $(BANNER_AUDIO_ARG) "$(BANNER_AUDIO)" -o "$@" - -$(BUILD)/icon.icn : $(APP_ICON) - $(BANNERTOOL) makesmdh -s "$(APP_TITLE)" -l "$(APP_DESCRIPTION)" -p "$(APP_AUTHOR)" -i "$(APP_ICON)" -f "$(ICON_FLAGS)" -o "$@" + @echo $(notdir $<) + @tex3ds -i $< -H $(BUILD)/$*.h -d $(DEPSDIR)/$*.d -o $(GFXBUILD)/$*.t3x #--------------------------------------------------------------------------------- else -DEPENDS := $(OFILES:.o=.d) - #--------------------------------------------------------------------------------- # main targets #--------------------------------------------------------------------------------- +$(OUTPUT).3dsx : $(OUTPUT).elf $(_3DSXDEPS) -$(OUTPUT).elf : $(OFILES) +$(OFILES_SOURCES) : $(HFILES) + +$(OUTPUT).elf : $(OFILES) #--------------------------------------------------------------------------------- # you need a rule like this for each extension you use as binary data #--------------------------------------------------------------------------------- -%.bin.o : %.bin +%.bin.o %_bin.h : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +.PRECIOUS : %.t3x +#--------------------------------------------------------------------------------- +%.t3x.o %_t3x.h : %.t3x #--------------------------------------------------------------------------------- @echo $(notdir $<) @$(bin2o) @@ -259,27 +218,35 @@ $(OUTPUT).elf : $(OFILES) # rules for assembling GPU shaders #--------------------------------------------------------------------------------- define shader-as - $(eval CURBIN := $(patsubst %.shbin.o,%.shbin,$(notdir $@))) - picasso -o $(CURBIN) $1 - bin2s $(CURBIN) | $(AS) -o $@ + $(eval CURBIN := $*.shbin) + $(eval DEPSFILE := $(DEPSDIR)/$*.shbin.d) + echo "$(CURBIN).o: $< $1" > $(DEPSFILE) echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h + picasso -o $(CURBIN) $1 + bin2s $(CURBIN) | $(AS) -o $*.shbin.o endef -%.shbin.o : %.v.pica %.g.pica +%.shbin.o %_shbin.h : %.v.pica %.g.pica @echo $(notdir $^) @$(call shader-as,$^) -%.shbin.o : %.v.pica +%.shbin.o %_shbin.h : %.v.pica @echo $(notdir $<) @$(call shader-as,$<) -%.shbin.o : %.shlist +%.shbin.o %_shbin.h : %.shlist + @echo $(notdir $<) + @$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)$(file))) + +#--------------------------------------------------------------------------------- +%.t3x %.h : %.t3s +#--------------------------------------------------------------------------------- @echo $(notdir $<) - @$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)/$(file))) + @tex3ds -i $< -H $*.h -d $*.d -o $*.t3x --include $(DEPENDS) +-include $(DEPSDIR)/*.d #--------------------------------------------------------------------------------------- endif diff --git a/include/C2D_helper.h b/include/C2D_helper.h new file mode 100644 index 0000000..3e48376 --- /dev/null +++ b/include/C2D_helper.h @@ -0,0 +1,36 @@ +#ifndef _3D_SHELL_C2D_HELPER_H +#define _3D_SHELL_C2D_HELPER_H + +#include + +#define WHITE C2D_Color32(255, 255, 255, 255) +#define BLACK_BG C2D_Color32(48, 48, 48, 255) +#define STATUS_BAR_LIGHT C2D_Color32(37, 79, 174, 255) +#define STATUS_BAR_DARK C2D_Color32(38, 50, 56, 255) +#define MENU_BAR_LIGHT C2D_Color32(51, 103, 214, 255) +#define MENU_BAR_DARK C2D_Color32(55, 71, 79, 255) +#define BLACK C2D_Color32(0, 0, 0, 255) +#define SELECTOR_COLOUR_LIGHT C2D_Color32(241, 241, 241, 255) +#define SELECTOR_COLOUR_DARK C2D_Color32(76, 76, 76, 255) +#define TITLE_COLOUR C2D_Color32(30, 136, 229, 255) +#define TITLE_COLOUR_DARK C2D_Color32(0, 150, 136, 255) +#define TEXT_MIN_COLOUR_LIGHT C2D_Color32(32, 32, 32, 255) +#define TEXT_MIN_COLOUR_DARK C2D_Color32(185, 185, 185, 255) +#define BAR_COLOUR C2D_Color32(200, 200, 200, 255) +#define PROGRESS_COLOUR C2D_Color32(48, 174, 222, 255) + +C3D_RenderTarget *RENDER_TOP, *RENDER_BOTTOM; +C2D_TextBuf staticBuf, dynamicBuf, sizeBuf; + +typedef u32 Color; + +void Draw_EndFrame(void); +void Draw_Text(float x, float y, float size, Color color, const char *text); +void Draw_GetTextSize(float size, float *width, float *height, const char *text); +float Draw_GetTextWidth(float size, const char *text); +float Draw_GetTextHeight(float size, const char *text); +bool Draw_Rect(float x, float y, float w, float h, Color color); +bool Draw_Image(C2D_Image image, float x, float y); +bool Draw_ImageScale(C2D_Image image, float x, float y, float scaleX, float scaleY); + +#endif \ No newline at end of file diff --git a/include/cia.h b/include/cia.h deleted file mode 100644 index c6d51d1..0000000 --- a/include/cia.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef CIA_H -#define CIA_H - -#include <3ds.h> - -Result CIA_InstallTitle(const char * path, FS_MediaType media, bool update); - -#endif \ No newline at end of file diff --git a/include/common.h b/include/common.h index d7ad133..1087d1b 100644 --- a/include/common.h +++ b/include/common.h @@ -1,37 +1,34 @@ -#ifndef COMMON_H -#define COMMON_H +#ifndef NX_SHELL_COMMON_H +#define NX_SHELL_COMMON_H -#include <3ds.h> +#include + +#define wait(msec) svcSleepThread(10000000 * (s64)msec) #define ROOT_PATH "/" #define START_PATH ROOT_PATH #define MAX_FILES 1024 #define FILES_PER_PAGE 5 -#define wait(msec) svcSleepThread(10000000 * (s64)msec) - -int MENU_DEFAULT_STATE; - #define MENU_STATE_HOME 0 #define MENU_STATE_OPTIONS 1 -#define MENU_STATE_SETTINGS 2 -#define MENU_STATE_FTP 3 -#define MENU_STATE_SORT 4 -#define MENU_STATE_THEMES 5 -#define MENU_STATE_DIALOG 6 -#define MENU_STATE_PROPERTIES 7 -#define MENU_STATE_UPDATE 8 -#define MENU_STATE_UPDATE_2 9 - -int BROWSE_STATE; +#define MENU_STATE_MENUBAR 2 +#define MENU_STATE_SETTINGS 3 +#define MENU_STATE_FTP 4 +#define MENU_STATE_SORT 5 +#define MENU_STATE_THEMES 6 +#define MENU_STATE_DIALOG 7 +#define MENU_STATE_PROPERTIES 8 #define STATE_SD 0 #define STATE_NAND 1 -char cwd[512]; -char fileName[255]; +jmp_buf exitJmp; + +int MENU_DEFAULT_STATE; +int BROWSE_STATE; -bool recycleBin, galleryDisplay, isHiddenEnabled; -int sortBy; +char cwd[512]; +char fileName[256]; #endif \ No newline at end of file diff --git a/include/config.h b/include/config.h new file mode 100644 index 0000000..2b38190 --- /dev/null +++ b/include/config.h @@ -0,0 +1,13 @@ +#ifndef _3D_SHELL_CONFIG_H +#define _3D_SHELL_CONFIG_H + +#include <3ds.h> + +bool config_dark_theme, config_hidden_files; +int config_sort_by; + +Result Config_Save(bool config_dark_theme, bool config_hidden_files, int config_sort_by); +Result Config_Load(void); +Result Config_GetLastDirectory(void); + +#endif \ No newline at end of file diff --git a/include/dir_list.h b/include/dir_list.h deleted file mode 100644 index f49ed52..0000000 --- a/include/dir_list.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef DIRLIST_H -#define DIRLIST_H - -#include <3ds.h> - -typedef struct File -{ - struct File * next; // Next item - int isDir; // Folder flag - int isReadOnly; // Read-only flag - int isHidden; // Hidden file flag - u8 name[256]; // File name - char ext[4]; // File extension - u64 size; // File size -} File; - -extern File * files; - -extern int position; -extern int fileCount; -bool multi_select[255]; -char multi_select_dir[255]; - -Result Dirlist_PopulateFiles(bool clear); -void Dirlist_DisplayFiles(void); -File * Dirlist_GetFileIndex(int index); -void Dirlist_OpenFile(void); -int Dirlist_Navigate(int dir); - -#endif \ No newline at end of file diff --git a/include/dirbrowse.h b/include/dirbrowse.h new file mode 100644 index 0000000..c018203 --- /dev/null +++ b/include/dirbrowse.h @@ -0,0 +1,36 @@ +#ifndef _3D_SHELL_DIRBROWSE_H +#define _3D_SHELL_DIRBROWSE_H + +#include <3ds.h> + +typedef struct File +{ + struct File *next; // Next item + int isDir; // Folder flag + int isReadOnly; // Read-only flag + int isHidden; // Hidden file flag + u8 name[256]; // File name + char ext[4]; // File extension + u64 size; // File size +} File; + +extern File *files; + +extern int initialPosition; +extern int position; +extern int fileCount; + +int multi_select_index; // Multi-select index. +bool multi_select[256]; // Array of indices selected. +int multi_select_indices[256]; // Array to hold the indices. +char multi_select_dir[256]; // Holds the current dir where multi-select happens. +char multi_select_paths[51][256]; // Holds the file paths of those in the clipboard. + +void Dirbrowse_RecursiveFree(File *node); +Result Dirbrowse_PopulateFiles(bool clear); +void Dirbrowse_DisplayFiles(void); +File *Dirbrowse_GetFileIndex(int index); +void Dirbrowse_OpenFile(void); +int Dirbrowse_Navigate(int dir); + +#endif \ No newline at end of file diff --git a/include/fs.h b/include/fs.h index 2c99e3d..94898b4 100644 --- a/include/fs.h +++ b/include/fs.h @@ -1,5 +1,5 @@ -#ifndef FS_H -#define FS_H +#ifndef _3D_SHELL_FS_H +#define _3D_SHELL_FS_H #include <3ds.h> diff --git a/include/keyboard.h b/include/keyboard.h new file mode 100644 index 0000000..f67ab26 --- /dev/null +++ b/include/keyboard.h @@ -0,0 +1,6 @@ +#ifndef _3D_SHELL_KEYBOARD_H +#define _3D_SHELL_KEYBOARD_H + +char *OSK_Get(int maxTextLength, const char *initialText, const char *hintText); + +#endif \ No newline at end of file diff --git a/include/language.h b/include/language.h deleted file mode 100644 index 19fc025..0000000 --- a/include/language.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef LANGUAGE_H -#define LANGUAGE_H - -#include <3ds.h> - -CFG_Language language; - -const char lang_welcome[12][3][100]; -const char lang_months[12][12][50]; -const char lang_days[12][7][50]; -const char lang_files[12][11][50]; -const char lang_options[12][9][100]; -const char lang_properties[12][7][100]; -const char lang_deletion[12][5][100]; -const char lang_settings[12][9][100]; -const char lang_themes[12][1][100]; -const char lang_ftp[12][5][100]; -const char lang_update[12][4][100]; - -#endif \ No newline at end of file diff --git a/include/menus/menu_delete.h b/include/menus/menu_delete.h deleted file mode 100644 index 70d756b..0000000 --- a/include/menus/menu_delete.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef MENU_DELETE_H -#define MENU_DELETE_H - -#include <3ds.h> - -void Menu_DisplayDeleteDialog(void); -void Menu_ControlDeleteDialog(u32 input); - -#endif \ No newline at end of file diff --git a/include/menus/menu_file_options.h b/include/menus/menu_file_options.h deleted file mode 100644 index 9b50525..0000000 --- a/include/menus/menu_file_options.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef MENU_FILE_OPTIONS_H -#define MENU_FILE_OPTIONS_H - -#include <3ds.h> - -void Menu_DisplayFileOptions(void); -void Menu_ControlFileOptions(u32 input); - -#endif \ No newline at end of file diff --git a/include/menus/menu_ftp.h b/include/menus/menu_ftp.h index da4a9ff..dbb70a5 100644 --- a/include/menus/menu_ftp.h +++ b/include/menus/menu_ftp.h @@ -1,5 +1,5 @@ -#ifndef MENU_FTP_H -#define MENU_FTP_H +#ifndef _3D_SHELL_MENU_FTP_H +#define _3D_SHELL_MENU_FTP_H void Menu_DisplayFTP(void); diff --git a/include/menus/menu_gallery.h b/include/menus/menu_gallery.h index bb3e078..b326e3e 100644 --- a/include/menus/menu_gallery.h +++ b/include/menus/menu_gallery.h @@ -1,6 +1,6 @@ -#ifndef MENU_GALLERY_H -#define MENU_GALLERY_H +#ifndef _3D_SHELL_MENU_GALLERY_H +#define _3D_SHELL_MENU_GALLERY_H -void Gallery_DisplayImage(char * path); +void Gallery_DisplayImage(char *path); #endif \ No newline at end of file diff --git a/include/menus/menu_home.h b/include/menus/menu_home.h deleted file mode 100644 index 478a0d9..0000000 --- a/include/menus/menu_home.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef MENU_HOME_H -#define MENU_HOME_H - -void Menu_Home(void); - -#endif \ No newline at end of file diff --git a/include/menus/menu_main.h b/include/menus/menu_main.h index 075a9e2..e451bd7 100644 --- a/include/menus/menu_main.h +++ b/include/menus/menu_main.h @@ -1,7 +1,6 @@ -#ifndef MENU_MAIN_H -#define MENU_MAIN_H +#ifndef _3D_SHELL_MENU_MAIN_H +#define _3D_SHELL_MENU_MAIN_H -void Menu_Draw_MenuBar(void); void Menu_Main(void); #endif \ No newline at end of file diff --git a/include/menus/menu_music.h b/include/menus/menu_music.h index 360ec89..28770f0 100644 --- a/include/menus/menu_music.h +++ b/include/menus/menu_music.h @@ -1,5 +1,5 @@ -#ifndef MENU_MUSIC_H -#define MENU_MUSIC_H +#ifndef _3D_SHELL_MENU_MUSIC_H +#define _3D_SHELL_MENU_MUSIC_H #include <3ds.h> @@ -15,7 +15,7 @@ enum file_types struct decoder_fn { - int (* init)(const char* file); + int (* init)(const char *file); u32 (* rate)(void); u8 (* channels)(void); size_t buffSize; @@ -39,6 +39,6 @@ enum file_types Music_GetMusicFileType(const char *file); * * \param path File path. */ -void Music_Player(char * path); +void Music_Player(char *path); #endif diff --git a/include/menus/menu_properties.h b/include/menus/menu_properties.h deleted file mode 100644 index c53a2e1..0000000 --- a/include/menus/menu_properties.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef MENU_PROPERTIES_H -#define MENU_PROPERTIES_H - -#include <3ds.h> - -void Menu_DisplayProperties(void); -void Menu_ControlProperties(u32 input); - -#endif \ No newline at end of file diff --git a/include/menus/menu_settings.h b/include/menus/menu_settings.h deleted file mode 100644 index 82e04b1..0000000 --- a/include/menus/menu_settings.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef MENU_SETTINGS_H -#define MENU_SETTINGS_H - -#include <3ds.h> - -void Menu_DisplaySettings(void); -void Menu_ControlSettings(u32 input); - -#endif \ No newline at end of file diff --git a/include/menus/menu_sort.h b/include/menus/menu_sort.h deleted file mode 100644 index 298a3a3..0000000 --- a/include/menus/menu_sort.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef MENU_SORT_H -#define MENU_SORT_H - -#include <3ds.h> - -void Menu_DisplaySort(void); -void Menu_ControlSort(u32 input); - -#endif \ No newline at end of file diff --git a/include/menus/menu_textviewer.h b/include/menus/menu_textviewer.h deleted file mode 100644 index c092518..0000000 --- a/include/menus/menu_textviewer.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef MENU_TEXTVIEWER_H -#define MENU_TEXTVIEWER_H - -void TextViewer_DisplayText(char * path); - -#endif \ No newline at end of file diff --git a/include/menus/menu_update.h b/include/menus/menu_update.h deleted file mode 100644 index 6358b25..0000000 --- a/include/menus/menu_update.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef MENU_UPDATE_H -#define MENU_UPDATE_H - -#include <3ds.h> -#include - -jmp_buf exitJmp; - -void Menu_DisplayUpdate(void); -void Menu_DisplayUpdate2(void); -void Menu_ControlUpdate(u32 input); -void Menu_ControlUpdate2(u32 input); - -#endif \ No newline at end of file diff --git a/include/misc/battery.h b/include/misc/battery.h deleted file mode 100644 index f7a67aa..0000000 --- a/include/misc/battery.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef BATTERY_H -#define BATTERY_H - -char * Battery_GetBatteryPercentString(void); -void Battery_DrawIcons(int x, int y); - -#endif diff --git a/include/misc/clock.h b/include/misc/clock.h deleted file mode 100644 index 7abd6f9..0000000 --- a/include/misc/clock.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef CLOCK_H -#define CLOCK_H - -#include <3ds.h> - -char * Clock_GetCurrentTime(bool _12hour); -char * Clock_GetDayOfWeek(bool brief); -char * Clock_GetMonthOfYear(int style); - -#endif \ No newline at end of file diff --git a/include/misc/keyboard.h b/include/misc/keyboard.h deleted file mode 100644 index 52a77d0..0000000 --- a/include/misc/keyboard.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef KEYBOARD_H -#define KEYBOARD_H - -char * keyboard_3ds_get(int maxTextLength, const char* initialText, const char* hintText); - -#endif \ No newline at end of file diff --git a/include/misc/status_bar.h b/include/misc/status_bar.h deleted file mode 100644 index a7a5941..0000000 --- a/include/misc/status_bar.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef STATUS_BAR_H -#define STATUS_BAR_H - -void StatusBar_DisplayBar(void); - -#endif \ No newline at end of file diff --git a/include/misc/touch.h b/include/misc/touch.h deleted file mode 100644 index ae49915..0000000 --- a/include/misc/touch.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef TOUCH_H -#define TOUCH_H - -#define touchInRect(x1, y1, x2, y2) ((touchGetX() >= (x1) && touchGetX() <= (x2)) && (touchGetY() >= (y1) && touchGetY() <= (y2))) - -u16 touchGetX(void); -u16 touchGetY(void); - -#endif \ No newline at end of file diff --git a/include/misc/wifi.h b/include/misc/wifi.h deleted file mode 100644 index a0c6e33..0000000 --- a/include/misc/wifi.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef COMMON_H -#define COMMON_H - -void WiFi_DrawIcons(int x, int y); - -#endif \ No newline at end of file diff --git a/include/net.h b/include/net.h deleted file mode 100644 index 5a1ee60..0000000 --- a/include/net.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef NET_H -#define NET_H - -#include <3ds.h> - -Result Net_DownloadFile(const char *url, const char *path); - -#endif \ No newline at end of file diff --git a/include/pp2d/LICENSE b/include/pp2d/LICENSE deleted file mode 100644 index 51ef706..0000000 --- a/include/pp2d/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2017 Bernardo Giordano - -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. \ No newline at end of file diff --git a/include/pp2d/README.md b/include/pp2d/README.md deleted file mode 100644 index c1682cc..0000000 --- a/include/pp2d/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# [pp2d](https://discord.gg/bGKEyfY) - -Plug & Play 2D (unofficial) wrapper for Citro3D. - -## License - -pp2d is licensed under the MIT License. - -## Changes - -* depth parameter (Ryuzaki-MrL) -* standalone pp2d_free_texture (LiquidFenrir) -* 3d support (Robz8) -* load png from memory (ErmanSayin) -* BMP support (blujay) diff --git a/include/pp2d/libnsbmp.h b/include/pp2d/libnsbmp.h deleted file mode 100644 index 7e90b4a..0000000 --- a/include/pp2d/libnsbmp.h +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright 2006 Richard Wilson - * Copyright 2008 Sean Fox - * - * This file is part of NetSurf's libnsbmp, http://www.netsurf-browser.org/ - * Licenced under the MIT License, - * http://www.opensource.org/licenses/mit-license.php - */ - -/** - * \file - * Bitmap file decoding interface. - */ - -#ifndef libnsbmp_h_ -#define libnsbmp_h_ - -#include -#include -#include - -/* bmp flags */ -#define BMP_NEW 0 -/** image is opaque (as opposed to having an alpha mask) */ -#define BMP_OPAQUE (1 << 0) -/** memory should be wiped */ -#define BMP_CLEAR_MEMORY (1 << 1) - -/** - * error return values - */ -typedef enum { - BMP_OK = 0, - BMP_INSUFFICIENT_MEMORY = 1, - BMP_INSUFFICIENT_DATA = 2, - BMP_DATA_ERROR = 3 -} bmp_result; - -/** - * encoding types - */ -typedef enum { - BMP_ENCODING_RGB = 0, - BMP_ENCODING_RLE8 = 1, - BMP_ENCODING_RLE4 = 2, - BMP_ENCODING_BITFIELDS = 3 -} bmp_encoding; - -/* API for Bitmap callbacks */ -typedef void* (*bmp_bitmap_cb_create)(int width, int height, unsigned int state); -typedef void (*bmp_bitmap_cb_destroy)(void *bitmap); -typedef unsigned char* (*bmp_bitmap_cb_get_buffer)(void *bitmap); -typedef size_t (*bmp_bitmap_cb_get_bpp)(void *bitmap); - -/** - * The Bitmap callbacks function table - */ -typedef struct bmp_bitmap_callback_vt_s { - /** Callback to allocate bitmap storage. */ - bmp_bitmap_cb_create bitmap_create; - /** Called to free bitmap storage. */ - bmp_bitmap_cb_destroy bitmap_destroy; - /** Return a pointer to the pixel data in a bitmap. */ - bmp_bitmap_cb_get_buffer bitmap_get_buffer; - /** Find the width of a pixel row in bytes. */ - bmp_bitmap_cb_get_bpp bitmap_get_bpp; -} bmp_bitmap_callback_vt; - -/** - * bitmap image - */ -typedef struct bmp_image { - /** callbacks for bitmap functions */ - bmp_bitmap_callback_vt bitmap_callbacks; - /** pointer to BMP data */ - uint8_t *bmp_data; - /** width of BMP (valid after _analyse) */ - uint32_t width; - /** heigth of BMP (valid after _analyse) */ - uint32_t height; - /** whether the image has been decoded */ - bool decoded; - /** decoded image */ - void *bitmap; - - /* Internal members are listed below */ - /** total number of bytes of BMP data available */ - uint32_t buffer_size; - /** pixel encoding type */ - bmp_encoding encoding; - /** offset of bitmap data */ - uint32_t bitmap_offset; - /** bits per pixel */ - uint16_t bpp; - /** number of colours */ - uint32_t colours; - /** colour table */ - uint32_t *colour_table; - /** whether to use bmp's limited transparency */ - bool limited_trans; - /** colour to display for "transparent" pixels when using limited - * transparency - */ - uint32_t trans_colour; - /** scanlines are top to bottom */ - bool reversed; - /** image is part of an ICO, mask follows */ - bool ico; - /** true if the bitmap does not contain an alpha channel */ - bool opaque; - /** four bitwise mask */ - uint32_t mask[4]; - /** four bitwise shifts */ - int32_t shift[4]; - /** colour representing "transparency" in the bitmap */ - uint32_t transparent_index; -} bmp_image; - -typedef struct ico_image { - bmp_image bmp; - struct ico_image *next; -} ico_image; - -/** - * icon image collection - */ -typedef struct ico_collection { - /** callbacks for bitmap functions */ - bmp_bitmap_callback_vt bitmap_callbacks; - /** width of largest BMP */ - uint16_t width; - /** heigth of largest BMP */ - uint16_t height; - - /* Internal members are listed below */ - /** pointer to ICO data */ - uint8_t *ico_data; - /** total number of bytes of ICO data available */ - uint32_t buffer_size; - /** root of linked list of images */ - ico_image *first; -} ico_collection; - -/** - * Initialises bitmap ready for analysing the bitmap. - * - * \param bmp The Bitmap to initialise - * \param callbacks The callbacks the library will call on operations. - * \return BMP_OK on success or appropriate error code. - */ -bmp_result bmp_create(bmp_image *bmp, bmp_bitmap_callback_vt *callbacks); - -/** - * Initialises icon ready for analysing the icon - * - * \param bmp The Bitmap to initialise - * \param callbacks The callbacks the library will call on operations. - * \return BMP_OK on success or appropriate error code. - */ -bmp_result ico_collection_create(ico_collection *ico, - bmp_bitmap_callback_vt *callbacks); - -/** - * Analyse a BMP prior to decoding. - * - * This will scan the data provided and perform checks to ensure the data is a - * valid BMP and prepare the bitmap image structure ready for decode. - * - * This function must be called and resturn BMP_OK before bmp_decode() as it - * prepares the bmp internal state for the decode process. - * - * \param bmp the BMP image to analyse. - * \param size The size of data in cdata. - * \param data The bitmap source data. - * \return BMP_OK on success or error code on faliure. - */ -bmp_result bmp_analyse(bmp_image *bmp, size_t size, uint8_t *data); - -/** - * Analyse an ICO prior to decoding. - * - * This function will scan the data provided and perform checks to ensure the - * data is a valid ICO. - * - * This function must be called before ico_find(). - * - * \param ico the ICO image to analyse - * \param size The size of data in cdata. - * \param data The bitmap source data. - * \return BMP_OK on success - */ -bmp_result ico_analyse(ico_collection *ico, size_t size, uint8_t *data); - -/** - * Decode a BMP - * - * This function decodes the BMP data such that bmp->bitmap is a valid - * image. The state of bmp->decoded is set to TRUE on exit such that it - * can easily be identified which BMPs are in a fully decoded state. - * - * \param bmp the BMP image to decode - * \return BMP_OK on success - */ -bmp_result bmp_decode(bmp_image *bmp); - -/** - * Decode a BMP using "limited transparency" - * - * Bitmaps do not have native transparency support. However, there is a - * "trick" that is used in some instances in which the first pixel of the - * bitmap becomes the "transparency index". The decoding application can - * replace this index with whatever background colour it chooses to - * create the illusion of transparency. - * - * When to use transparency is at the discretion of the decoding - * application. - * - * \param bmp the BMP image to decode - * \param colour the colour to use as "transparent" - * \return BMP_OK on success - */ -bmp_result bmp_decode_trans(bmp_image *bmp, uint32_t transparent_colour); - -/** - * Finds the closest BMP within an ICO collection - * - * This function finds the BMP with dimensions as close to a specified set - * as possible from the images in the collection. - * - * \param ico the ICO collection to examine - * \param width the preferred width (0 to use ICO header width) - * \param height the preferred height (0 to use ICO header height) - */ -bmp_image *ico_find(ico_collection *ico, uint16_t width, uint16_t height); - -/** - * Finalise a BMP prior to destruction. - * - * \param bmp the BMP image to finalise. - */ -void bmp_finalise(bmp_image *bmp); - -/** - * Finalise an ICO prior to destruction. - * - * \param ico the ICO image to finalise, - */ -void ico_finalise(ico_collection *ico); - -#endif diff --git a/include/pp2d/lodepng.h b/include/pp2d/lodepng.h deleted file mode 100644 index cf6667c..0000000 --- a/include/pp2d/lodepng.h +++ /dev/null @@ -1,1769 +0,0 @@ -/* -LodePNG version 20180114 - -Copyright (c) 2005-2018 Lode Vandevenne - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*/ - -#ifndef LODEPNG_H -#define LODEPNG_H - -#include /*for size_t*/ - -extern const char* LODEPNG_VERSION_STRING; - -/* -The following #defines are used to create code sections. They can be disabled -to disable code sections, which can give faster compile time and smaller binary. -The "NO_COMPILE" defines are designed to be used to pass as defines to the -compiler command to disable them without modifying this header, e.g. --DLODEPNG_NO_COMPILE_ZLIB for gcc. -In addition to those below, you can also define LODEPNG_NO_COMPILE_CRC to -allow implementing a custom lodepng_crc32. -*/ -/*deflate & zlib. If disabled, you must specify alternative zlib functions in -the custom_zlib field of the compress and decompress settings*/ -#ifndef LODEPNG_NO_COMPILE_ZLIB -#define LODEPNG_COMPILE_ZLIB -#endif -/*png encoder and png decoder*/ -#ifndef LODEPNG_NO_COMPILE_PNG -#define LODEPNG_COMPILE_PNG -#endif -/*deflate&zlib decoder and png decoder*/ -#ifndef LODEPNG_NO_COMPILE_DECODER -#define LODEPNG_COMPILE_DECODER -#endif -/*deflate&zlib encoder and png encoder*/ -#ifndef LODEPNG_NO_COMPILE_ENCODER -#define LODEPNG_COMPILE_ENCODER -#endif -/*the optional built in harddisk file loading and saving functions*/ -#ifndef LODEPNG_NO_COMPILE_DISK -#define LODEPNG_COMPILE_DISK -#endif -/*support for chunks other than IHDR, IDAT, PLTE, tRNS, IEND: ancillary and unknown chunks*/ -#ifndef LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS -#define LODEPNG_COMPILE_ANCILLARY_CHUNKS -#endif -/*ability to convert error numerical codes to English text string*/ -#ifndef LODEPNG_NO_COMPILE_ERROR_TEXT -#define LODEPNG_COMPILE_ERROR_TEXT -#endif -/*Compile the default allocators (C's free, malloc and realloc). If you disable this, -you can define the functions lodepng_free, lodepng_malloc and lodepng_realloc in your -source files with custom allocators.*/ -#ifndef LODEPNG_NO_COMPILE_ALLOCATORS -#define LODEPNG_COMPILE_ALLOCATORS -#endif -/*compile the C++ version (you can disable the C++ wrapper here even when compiling for C++)*/ -#ifdef __cplusplus -#ifndef LODEPNG_NO_COMPILE_CPP -#define LODEPNG_COMPILE_CPP -#endif -#endif - -#ifdef LODEPNG_COMPILE_CPP -#include -#include -#endif /*LODEPNG_COMPILE_CPP*/ - -#ifdef LODEPNG_COMPILE_PNG -/*The PNG color types (also used for raw).*/ -typedef enum LodePNGColorType -{ - LCT_GREY = 0, /*greyscale: 1,2,4,8,16 bit*/ - LCT_RGB = 2, /*RGB: 8,16 bit*/ - LCT_PALETTE = 3, /*palette: 1,2,4,8 bit*/ - LCT_GREY_ALPHA = 4, /*greyscale with alpha: 8,16 bit*/ - LCT_RGBA = 6 /*RGB with alpha: 8,16 bit*/ -} LodePNGColorType; - -#ifdef LODEPNG_COMPILE_DECODER -/* -Converts PNG data in memory to raw pixel data. -out: Output parameter. Pointer to buffer that will contain the raw pixel data. - After decoding, its size is w * h * (bytes per pixel) bytes larger than - initially. Bytes per pixel depends on colortype and bitdepth. - Must be freed after usage with free(*out). - Note: for 16-bit per channel colors, uses big endian format like PNG does. -w: Output parameter. Pointer to width of pixel data. -h: Output parameter. Pointer to height of pixel data. -in: Memory buffer with the PNG file. -insize: size of the in buffer. -colortype: the desired color type for the raw output image. See explanation on PNG color types. -bitdepth: the desired bit depth for the raw output image. See explanation on PNG color types. -Return value: LodePNG error code (0 means no error). -*/ -unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, - const unsigned char* in, size_t insize, - LodePNGColorType colortype, unsigned bitdepth); - -/*Same as lodepng_decode_memory, but always decodes to 32-bit RGBA raw image*/ -unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, - const unsigned char* in, size_t insize); - -/*Same as lodepng_decode_memory, but always decodes to 24-bit RGB raw image*/ -unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, - const unsigned char* in, size_t insize); - -#ifdef LODEPNG_COMPILE_DISK -/* -Load PNG from disk, from file with given name. -Same as the other decode functions, but instead takes a filename as input. -*/ -unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, - const char* filename, - LodePNGColorType colortype, unsigned bitdepth); - -/*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.*/ -unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, - const char* filename); - -/*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.*/ -unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, - const char* filename); -#endif /*LODEPNG_COMPILE_DISK*/ -#endif /*LODEPNG_COMPILE_DECODER*/ - - -#ifdef LODEPNG_COMPILE_ENCODER -/* -Converts raw pixel data into a PNG image in memory. The colortype and bitdepth - of the output PNG image cannot be chosen, they are automatically determined - by the colortype, bitdepth and content of the input pixel data. - Note: for 16-bit per channel colors, needs big endian format like PNG does. -out: Output parameter. Pointer to buffer that will contain the PNG image data. - Must be freed after usage with free(*out). -outsize: Output parameter. Pointer to the size in bytes of the out buffer. -image: The raw pixel data to encode. The size of this buffer should be - w * h * (bytes per pixel), bytes per pixel depends on colortype and bitdepth. -w: width of the raw pixel data in pixels. -h: height of the raw pixel data in pixels. -colortype: the color type of the raw input image. See explanation on PNG color types. -bitdepth: the bit depth of the raw input image. See explanation on PNG color types. -Return value: LodePNG error code (0 means no error). -*/ -unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, - const unsigned char* image, unsigned w, unsigned h, - LodePNGColorType colortype, unsigned bitdepth); - -/*Same as lodepng_encode_memory, but always encodes from 32-bit RGBA raw image.*/ -unsigned lodepng_encode32(unsigned char** out, size_t* outsize, - const unsigned char* image, unsigned w, unsigned h); - -/*Same as lodepng_encode_memory, but always encodes from 24-bit RGB raw image.*/ -unsigned lodepng_encode24(unsigned char** out, size_t* outsize, - const unsigned char* image, unsigned w, unsigned h); - -#ifdef LODEPNG_COMPILE_DISK -/* -Converts raw pixel data into a PNG file on disk. -Same as the other encode functions, but instead takes a filename as output. -NOTE: This overwrites existing files without warning! -*/ -unsigned lodepng_encode_file(const char* filename, - const unsigned char* image, unsigned w, unsigned h, - LodePNGColorType colortype, unsigned bitdepth); - -/*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.*/ -unsigned lodepng_encode32_file(const char* filename, - const unsigned char* image, unsigned w, unsigned h); - -/*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.*/ -unsigned lodepng_encode24_file(const char* filename, - const unsigned char* image, unsigned w, unsigned h); -#endif /*LODEPNG_COMPILE_DISK*/ -#endif /*LODEPNG_COMPILE_ENCODER*/ - - -#ifdef LODEPNG_COMPILE_CPP -namespace lodepng -{ -#ifdef LODEPNG_COMPILE_DECODER -/*Same as lodepng_decode_memory, but decodes to an std::vector. The colortype -is the format to output the pixels to. Default is RGBA 8-bit per channel.*/ -unsigned decode(std::vector& out, unsigned& w, unsigned& h, - const unsigned char* in, size_t insize, - LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); -unsigned decode(std::vector& out, unsigned& w, unsigned& h, - const std::vector& in, - LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); -#ifdef LODEPNG_COMPILE_DISK -/* -Converts PNG file from disk to raw pixel data in memory. -Same as the other decode functions, but instead takes a filename as input. -*/ -unsigned decode(std::vector& out, unsigned& w, unsigned& h, - const std::string& filename, - LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); -#endif /* LODEPNG_COMPILE_DISK */ -#endif /* LODEPNG_COMPILE_DECODER */ - -#ifdef LODEPNG_COMPILE_ENCODER -/*Same as lodepng_encode_memory, but encodes to an std::vector. colortype -is that of the raw input data. The output PNG color type will be auto chosen.*/ -unsigned encode(std::vector& out, - const unsigned char* in, unsigned w, unsigned h, - LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); -unsigned encode(std::vector& out, - const std::vector& in, unsigned w, unsigned h, - LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); -#ifdef LODEPNG_COMPILE_DISK -/* -Converts 32-bit RGBA raw pixel data into a PNG file on disk. -Same as the other encode functions, but instead takes a filename as output. -NOTE: This overwrites existing files without warning! -*/ -unsigned encode(const std::string& filename, - const unsigned char* in, unsigned w, unsigned h, - LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); -unsigned encode(const std::string& filename, - const std::vector& in, unsigned w, unsigned h, - LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); -#endif /* LODEPNG_COMPILE_DISK */ -#endif /* LODEPNG_COMPILE_ENCODER */ -} /* namespace lodepng */ -#endif /*LODEPNG_COMPILE_CPP*/ -#endif /*LODEPNG_COMPILE_PNG*/ - -#ifdef LODEPNG_COMPILE_ERROR_TEXT -/*Returns an English description of the numerical error code.*/ -const char* lodepng_error_text(unsigned code); -#endif /*LODEPNG_COMPILE_ERROR_TEXT*/ - -#ifdef LODEPNG_COMPILE_DECODER -/*Settings for zlib decompression*/ -typedef struct LodePNGDecompressSettings LodePNGDecompressSettings; -struct LodePNGDecompressSettings -{ - /* Check LodePNGDecoderSettings for more ignorable errors */ - unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/ - - /*use custom zlib decoder instead of built in one (default: null)*/ - unsigned (*custom_zlib)(unsigned char**, size_t*, - const unsigned char*, size_t, - const LodePNGDecompressSettings*); - /*use custom deflate decoder instead of built in one (default: null) - if custom_zlib is used, custom_deflate is ignored since only the built in - zlib function will call custom_deflate*/ - unsigned (*custom_inflate)(unsigned char**, size_t*, - const unsigned char*, size_t, - const LodePNGDecompressSettings*); - - const void* custom_context; /*optional custom settings for custom functions*/ -}; - -extern const LodePNGDecompressSettings lodepng_default_decompress_settings; -void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings); -#endif /*LODEPNG_COMPILE_DECODER*/ - -#ifdef LODEPNG_COMPILE_ENCODER -/* -Settings for zlib compression. Tweaking these settings tweaks the balance -between speed and compression ratio. -*/ -typedef struct LodePNGCompressSettings LodePNGCompressSettings; -struct LodePNGCompressSettings /*deflate = compress*/ -{ - /*LZ77 related settings*/ - unsigned btype; /*the block type for LZ (0, 1, 2 or 3, see zlib standard). Should be 2 for proper compression.*/ - unsigned use_lz77; /*whether or not to use LZ77. Should be 1 for proper compression.*/ - unsigned windowsize; /*must be a power of two <= 32768. higher compresses more but is slower. Default value: 2048.*/ - unsigned minmatch; /*mininum lz77 length. 3 is normally best, 6 can be better for some PNGs. Default: 0*/ - unsigned nicematch; /*stop searching if >= this length found. Set to 258 for best compression. Default: 128*/ - unsigned lazymatching; /*use lazy matching: better compression but a bit slower. Default: true*/ - - /*use custom zlib encoder instead of built in one (default: null)*/ - unsigned (*custom_zlib)(unsigned char**, size_t*, - const unsigned char*, size_t, - const LodePNGCompressSettings*); - /*use custom deflate encoder instead of built in one (default: null) - if custom_zlib is used, custom_deflate is ignored since only the built in - zlib function will call custom_deflate*/ - unsigned (*custom_deflate)(unsigned char**, size_t*, - const unsigned char*, size_t, - const LodePNGCompressSettings*); - - const void* custom_context; /*optional custom settings for custom functions*/ -}; - -extern const LodePNGCompressSettings lodepng_default_compress_settings; -void lodepng_compress_settings_init(LodePNGCompressSettings* settings); -#endif /*LODEPNG_COMPILE_ENCODER*/ - -#ifdef LODEPNG_COMPILE_PNG -/* -Color mode of an image. Contains all information required to decode the pixel -bits to RGBA colors. This information is the same as used in the PNG file -format, and is used both for PNG and raw image data in LodePNG. -*/ -typedef struct LodePNGColorMode -{ - /*header (IHDR)*/ - LodePNGColorType colortype; /*color type, see PNG standard or documentation further in this header file*/ - unsigned bitdepth; /*bits per sample, see PNG standard or documentation further in this header file*/ - - /* - palette (PLTE and tRNS) - - Dynamically allocated with the colors of the palette, including alpha. - When encoding a PNG, to store your colors in the palette of the LodePNGColorMode, first use - lodepng_palette_clear, then for each color use lodepng_palette_add. - If you encode an image without alpha with palette, don't forget to put value 255 in each A byte of the palette. - - When decoding, by default you can ignore this palette, since LodePNG already - fills the palette colors in the pixels of the raw RGBA output. - - The palette is only supported for color type 3. - */ - unsigned char* palette; /*palette in RGBARGBA... order. When allocated, must be either 0, or have size 1024*/ - size_t palettesize; /*palette size in number of colors (amount of bytes is 4 * palettesize)*/ - - /* - transparent color key (tRNS) - - This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit. - For greyscale PNGs, r, g and b will all 3 be set to the same. - - When decoding, by default you can ignore this information, since LodePNG sets - pixels with this key to transparent already in the raw RGBA output. - - The color key is only supported for color types 0 and 2. - */ - unsigned key_defined; /*is a transparent color key given? 0 = false, 1 = true*/ - unsigned key_r; /*red/greyscale component of color key*/ - unsigned key_g; /*green component of color key*/ - unsigned key_b; /*blue component of color key*/ -} LodePNGColorMode; - -/*init, cleanup and copy functions to use with this struct*/ -void lodepng_color_mode_init(LodePNGColorMode* info); -void lodepng_color_mode_cleanup(LodePNGColorMode* info); -/*return value is error code (0 means no error)*/ -unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source); - -void lodepng_palette_clear(LodePNGColorMode* info); -/*add 1 color to the palette*/ -unsigned lodepng_palette_add(LodePNGColorMode* info, - unsigned char r, unsigned char g, unsigned char b, unsigned char a); - -/*get the total amount of bits per pixel, based on colortype and bitdepth in the struct*/ -unsigned lodepng_get_bpp(const LodePNGColorMode* info); -/*get the amount of color channels used, based on colortype in the struct. -If a palette is used, it counts as 1 channel.*/ -unsigned lodepng_get_channels(const LodePNGColorMode* info); -/*is it a greyscale type? (only colortype 0 or 4)*/ -unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info); -/*has it got an alpha channel? (only colortype 2 or 6)*/ -unsigned lodepng_is_alpha_type(const LodePNGColorMode* info); -/*has it got a palette? (only colortype 3)*/ -unsigned lodepng_is_palette_type(const LodePNGColorMode* info); -/*only returns true if there is a palette and there is a value in the palette with alpha < 255. -Loops through the palette to check this.*/ -unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info); -/* -Check if the given color info indicates the possibility of having non-opaque pixels in the PNG image. -Returns true if the image can have translucent or invisible pixels (it still be opaque if it doesn't use such pixels). -Returns false if the image can only have opaque pixels. -In detail, it returns true only if it's a color type with alpha, or has a palette with non-opaque values, -or if "key_defined" is true. -*/ -unsigned lodepng_can_have_alpha(const LodePNGColorMode* info); -/*Returns the byte size of a raw image buffer with given width, height and color mode*/ -size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color); - -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS -/*The information of a Time chunk in PNG.*/ -typedef struct LodePNGTime -{ - unsigned year; /*2 bytes used (0-65535)*/ - unsigned month; /*1-12*/ - unsigned day; /*1-31*/ - unsigned hour; /*0-23*/ - unsigned minute; /*0-59*/ - unsigned second; /*0-60 (to allow for leap seconds)*/ -} LodePNGTime; -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ - -/*Information about the PNG image, except pixels, width and height.*/ -typedef struct LodePNGInfo -{ - /*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/ - unsigned compression_method;/*compression method of the original file. Always 0.*/ - unsigned filter_method; /*filter method of the original file*/ - unsigned interlace_method; /*interlace method of the original file*/ - LodePNGColorMode color; /*color type and bits, palette and transparency of the PNG file*/ - -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS - /* - suggested background color chunk (bKGD) - This color uses the same color mode as the PNG (except alpha channel), which can be 1-bit to 16-bit. - - For greyscale PNGs, r, g and b will all 3 be set to the same. When encoding - the encoder writes the red one. For palette PNGs: When decoding, the RGB value - will be stored, not a palette index. But when encoding, specify the index of - the palette in background_r, the other two are then ignored. - - The decoder does not use this background color to edit the color of pixels. - */ - unsigned background_defined; /*is a suggested background color given?*/ - unsigned background_r; /*red component of suggested background color*/ - unsigned background_g; /*green component of suggested background color*/ - unsigned background_b; /*blue component of suggested background color*/ - - /* - non-international text chunks (tEXt and zTXt) - - The char** arrays each contain num strings. The actual messages are in - text_strings, while text_keys are keywords that give a short description what - the actual text represents, e.g. Title, Author, Description, or anything else. - - A keyword is minimum 1 character and maximum 79 characters long. It's - discouraged to use a single line length longer than 79 characters for texts. - - Don't allocate these text buffers yourself. Use the init/cleanup functions - correctly and use lodepng_add_text and lodepng_clear_text. - */ - size_t text_num; /*the amount of texts in these char** buffers (there may be more texts in itext)*/ - char** text_keys; /*the keyword of a text chunk (e.g. "Comment")*/ - char** text_strings; /*the actual text*/ - - /* - international text chunks (iTXt) - Similar to the non-international text chunks, but with additional strings - "langtags" and "transkeys". - */ - size_t itext_num; /*the amount of international texts in this PNG*/ - char** itext_keys; /*the English keyword of the text chunk (e.g. "Comment")*/ - char** itext_langtags; /*language tag for this text's language, ISO/IEC 646 string, e.g. ISO 639 language tag*/ - char** itext_transkeys; /*keyword translated to the international language - UTF-8 string*/ - char** itext_strings; /*the actual international text - UTF-8 string*/ - - /*time chunk (tIME)*/ - unsigned time_defined; /*set to 1 to make the encoder generate a tIME chunk*/ - LodePNGTime time; - - /*phys chunk (pHYs)*/ - unsigned phys_defined; /*if 0, there is no pHYs chunk and the values below are undefined, if 1 else there is one*/ - unsigned phys_x; /*pixels per unit in x direction*/ - unsigned phys_y; /*pixels per unit in y direction*/ - unsigned phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/ - - /* - unknown chunks - There are 3 buffers, one for each position in the PNG where unknown chunks can appear - each buffer contains all unknown chunks for that position consecutively - The 3 buffers are the unknown chunks between certain critical chunks: - 0: IHDR-PLTE, 1: PLTE-IDAT, 2: IDAT-IEND - Do not allocate or traverse this data yourself. Use the chunk traversing functions declared - later, such as lodepng_chunk_next and lodepng_chunk_append, to read/write this struct. - */ - unsigned char* unknown_chunks_data[3]; - size_t unknown_chunks_size[3]; /*size in bytes of the unknown chunks, given for protection*/ -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ -} LodePNGInfo; - -/*init, cleanup and copy functions to use with this struct*/ -void lodepng_info_init(LodePNGInfo* info); -void lodepng_info_cleanup(LodePNGInfo* info); -/*return value is error code (0 means no error)*/ -unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source); - -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS -void lodepng_clear_text(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/ -unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str); /*push back both texts at once*/ - -void lodepng_clear_itext(LodePNGInfo* info); /*use this to clear the itexts again after you filled them in*/ -unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, - const char* transkey, const char* str); /*push back the 4 texts of 1 chunk at once*/ -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ - -/* -Converts raw buffer from one color type to another color type, based on -LodePNGColorMode structs to describe the input and output color type. -See the reference manual at the end of this header file to see which color conversions are supported. -return value = LodePNG error code (0 if all went ok, an error if the conversion isn't supported) -The out buffer must have size (w * h * bpp + 7) / 8, where bpp is the bits per pixel -of the output color type (lodepng_get_bpp). -For < 8 bpp images, there should not be padding bits at the end of scanlines. -For 16-bit per channel colors, uses big endian format like PNG does. -Return value is LodePNG error code -*/ -unsigned lodepng_convert(unsigned char* out, const unsigned char* in, - const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, - unsigned w, unsigned h); - -#ifdef LODEPNG_COMPILE_DECODER -/* -Settings for the decoder. This contains settings for the PNG and the Zlib -decoder, but not the Info settings from the Info structs. -*/ -typedef struct LodePNGDecoderSettings -{ - LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/ - - /* Check LodePNGDecompressSettings for more ignorable errors */ - unsigned ignore_crc; /*ignore CRC checksums*/ - unsigned ignore_critical; /*ignore unknown critical chunks*/ - unsigned ignore_end; /*ignore issues at end of file if possible (missing IEND chunk, too large chunk, ...)*/ - - unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/ - -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS - unsigned read_text_chunks; /*if false but remember_unknown_chunks is true, they're stored in the unknown chunks*/ - /*store all bytes from unknown chunks in the LodePNGInfo (off by default, useful for a png editor)*/ - unsigned remember_unknown_chunks; -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ -} LodePNGDecoderSettings; - -void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings); -#endif /*LODEPNG_COMPILE_DECODER*/ - -#ifdef LODEPNG_COMPILE_ENCODER -/*automatically use color type with less bits per pixel if losslessly possible. Default: AUTO*/ -typedef enum LodePNGFilterStrategy -{ - /*every filter at zero*/ - LFS_ZERO, - /*Use filter that gives minimum sum, as described in the official PNG filter heuristic.*/ - LFS_MINSUM, - /*Use the filter type that gives smallest Shannon entropy for this scanline. Depending - on the image, this is better or worse than minsum.*/ - LFS_ENTROPY, - /* - Brute-force-search PNG filters by compressing each filter for each scanline. - Experimental, very slow, and only rarely gives better compression than MINSUM. - */ - LFS_BRUTE_FORCE, - /*use predefined_filters buffer: you specify the filter type for each scanline*/ - LFS_PREDEFINED -} LodePNGFilterStrategy; - -/*Gives characteristics about the colors of the image, which helps decide which color model to use for encoding. -Used internally by default if "auto_convert" is enabled. Public because it's useful for custom algorithms.*/ -typedef struct LodePNGColorProfile -{ - unsigned colored; /*not greyscale*/ - unsigned key; /*image is not opaque and color key is possible instead of full alpha*/ - unsigned short key_r; /*key values, always as 16-bit, in 8-bit case the byte is duplicated, e.g. 65535 means 255*/ - unsigned short key_g; - unsigned short key_b; - unsigned alpha; /*image is not opaque and alpha channel or alpha palette required*/ - unsigned numcolors; /*amount of colors, up to 257. Not valid if bits == 16.*/ - unsigned char palette[1024]; /*Remembers up to the first 256 RGBA colors, in no particular order*/ - unsigned bits; /*bits per channel (not for palette). 1,2 or 4 for greyscale only. 16 if 16-bit per channel required.*/ -} LodePNGColorProfile; - -void lodepng_color_profile_init(LodePNGColorProfile* profile); - -/*Get a LodePNGColorProfile of the image.*/ -unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, - const unsigned char* image, unsigned w, unsigned h, - const LodePNGColorMode* mode_in); -/*The function LodePNG uses internally to decide the PNG color with auto_convert. -Chooses an optimal color model, e.g. grey if only grey pixels, palette if < 256 colors, ...*/ -unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, - const unsigned char* image, unsigned w, unsigned h, - const LodePNGColorMode* mode_in); - -/*Settings for the encoder.*/ -typedef struct LodePNGEncoderSettings -{ - LodePNGCompressSettings zlibsettings; /*settings for the zlib encoder, such as window size, ...*/ - - unsigned auto_convert; /*automatically choose output PNG color type. Default: true*/ - - /*If true, follows the official PNG heuristic: if the PNG uses a palette or lower than - 8 bit depth, set all filters to zero. Otherwise use the filter_strategy. Note that to - completely follow the official PNG heuristic, filter_palette_zero must be true and - filter_strategy must be LFS_MINSUM*/ - unsigned filter_palette_zero; - /*Which filter strategy to use when not using zeroes due to filter_palette_zero. - Set filter_palette_zero to 0 to ensure always using your chosen strategy. Default: LFS_MINSUM*/ - LodePNGFilterStrategy filter_strategy; - /*used if filter_strategy is LFS_PREDEFINED. In that case, this must point to a buffer with - the same length as the amount of scanlines in the image, and each value must <= 5. You - have to cleanup this buffer, LodePNG will never free it. Don't forget that filter_palette_zero - must be set to 0 to ensure this is also used on palette or low bitdepth images.*/ - const unsigned char* predefined_filters; - - /*force creating a PLTE chunk if colortype is 2 or 6 (= a suggested palette). - If colortype is 3, PLTE is _always_ created.*/ - unsigned force_palette; -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS - /*add LodePNG identifier and version as a text chunk, for debugging*/ - unsigned add_id; - /*encode text chunks as zTXt chunks instead of tEXt chunks, and use compression in iTXt chunks*/ - unsigned text_compression; -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ -} LodePNGEncoderSettings; - -void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings); -#endif /*LODEPNG_COMPILE_ENCODER*/ - - -#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) -/*The settings, state and information for extended encoding and decoding.*/ -typedef struct LodePNGState -{ -#ifdef LODEPNG_COMPILE_DECODER - LodePNGDecoderSettings decoder; /*the decoding settings*/ -#endif /*LODEPNG_COMPILE_DECODER*/ -#ifdef LODEPNG_COMPILE_ENCODER - LodePNGEncoderSettings encoder; /*the encoding settings*/ -#endif /*LODEPNG_COMPILE_ENCODER*/ - LodePNGColorMode info_raw; /*specifies the format in which you would like to get the raw pixel buffer*/ - LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/ - unsigned error; -#ifdef LODEPNG_COMPILE_CPP - /* For the lodepng::State subclass. */ - virtual ~LodePNGState(){} -#endif -} LodePNGState; - -/*init, cleanup and copy functions to use with this struct*/ -void lodepng_state_init(LodePNGState* state); -void lodepng_state_cleanup(LodePNGState* state); -void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source); -#endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */ - -#ifdef LODEPNG_COMPILE_DECODER -/* -Same as lodepng_decode_memory, but uses a LodePNGState to allow custom settings and -getting much more information about the PNG image and color mode. -*/ -unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, - LodePNGState* state, - const unsigned char* in, size_t insize); - -/* -Read the PNG header, but not the actual data. This returns only the information -that is in the header chunk of the PNG, such as width, height and color type. The -information is placed in the info_png field of the LodePNGState. -*/ -unsigned lodepng_inspect(unsigned* w, unsigned* h, - LodePNGState* state, - const unsigned char* in, size_t insize); -#endif /*LODEPNG_COMPILE_DECODER*/ - - -#ifdef LODEPNG_COMPILE_ENCODER -/*This function allocates the out buffer with standard malloc and stores the size in *outsize.*/ -unsigned lodepng_encode(unsigned char** out, size_t* outsize, - const unsigned char* image, unsigned w, unsigned h, - LodePNGState* state); -#endif /*LODEPNG_COMPILE_ENCODER*/ - -/* -The lodepng_chunk functions are normally not needed, except to traverse the -unknown chunks stored in the LodePNGInfo struct, or add new ones to it. -It also allows traversing the chunks of an encoded PNG file yourself. - -PNG standard chunk naming conventions: -First byte: uppercase = critical, lowercase = ancillary -Second byte: uppercase = public, lowercase = private -Third byte: must be uppercase -Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy -*/ - -/* -Gets the length of the data of the chunk. Total chunk length has 12 bytes more. -There must be at least 4 bytes to read from. If the result value is too large, -it may be corrupt data. -*/ -unsigned lodepng_chunk_length(const unsigned char* chunk); - -/*puts the 4-byte type in null terminated string*/ -void lodepng_chunk_type(char type[5], const unsigned char* chunk); - -/*check if the type is the given type*/ -unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type); - -/*0: it's one of the critical chunk types, 1: it's an ancillary chunk (see PNG standard)*/ -unsigned char lodepng_chunk_ancillary(const unsigned char* chunk); - -/*0: public, 1: private (see PNG standard)*/ -unsigned char lodepng_chunk_private(const unsigned char* chunk); - -/*0: the chunk is unsafe to copy, 1: the chunk is safe to copy (see PNG standard)*/ -unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk); - -/*get pointer to the data of the chunk, where the input points to the header of the chunk*/ -unsigned char* lodepng_chunk_data(unsigned char* chunk); -const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk); - -/*returns 0 if the crc is correct, 1 if it's incorrect (0 for OK as usual!)*/ -unsigned lodepng_chunk_check_crc(const unsigned char* chunk); - -/*generates the correct CRC from the data and puts it in the last 4 bytes of the chunk*/ -void lodepng_chunk_generate_crc(unsigned char* chunk); - -/*iterate to next chunks. don't use on IEND chunk, as there is no next chunk then*/ -unsigned char* lodepng_chunk_next(unsigned char* chunk); -const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk); - -/* -Appends chunk to the data in out. The given chunk should already have its chunk header. -The out variable and outlength are updated to reflect the new reallocated buffer. -Returns error code (0 if it went ok) -*/ -unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk); - -/* -Appends new chunk to out. The chunk to append is given by giving its length, type -and data separately. The type is a 4-letter string. -The out variable and outlength are updated to reflect the new reallocated buffer. -Returne error code (0 if it went ok) -*/ -unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, - const char* type, const unsigned char* data); - - -/*Calculate CRC32 of buffer*/ -unsigned lodepng_crc32(const unsigned char* buf, size_t len); -#endif /*LODEPNG_COMPILE_PNG*/ - - -#ifdef LODEPNG_COMPILE_ZLIB -/* -This zlib part can be used independently to zlib compress and decompress a -buffer. It cannot be used to create gzip files however, and it only supports the -part of zlib that is required for PNG, it does not support dictionaries. -*/ - -#ifdef LODEPNG_COMPILE_DECODER -/*Inflate a buffer. Inflate is the decompression step of deflate. Out buffer must be freed after use.*/ -unsigned lodepng_inflate(unsigned char** out, size_t* outsize, - const unsigned char* in, size_t insize, - const LodePNGDecompressSettings* settings); - -/* -Decompresses Zlib data. Reallocates the out buffer and appends the data. The -data must be according to the zlib specification. -Either, *out must be NULL and *outsize must be 0, or, *out must be a valid -buffer and *outsize its size in bytes. out must be freed by user after usage. -*/ -unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, - const unsigned char* in, size_t insize, - const LodePNGDecompressSettings* settings); -#endif /*LODEPNG_COMPILE_DECODER*/ - -#ifdef LODEPNG_COMPILE_ENCODER -/* -Compresses data with Zlib. Reallocates the out buffer and appends the data. -Zlib adds a small header and trailer around the deflate data. -The data is output in the format of the zlib specification. -Either, *out must be NULL and *outsize must be 0, or, *out must be a valid -buffer and *outsize its size in bytes. out must be freed by user after usage. -*/ -unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, - const unsigned char* in, size_t insize, - const LodePNGCompressSettings* settings); - -/* -Find length-limited Huffman code for given frequencies. This function is in the -public interface only for tests, it's used internally by lodepng_deflate. -*/ -unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies, - size_t numcodes, unsigned maxbitlen); - -/*Compress a buffer with deflate. See RFC 1951. Out buffer must be freed after use.*/ -unsigned lodepng_deflate(unsigned char** out, size_t* outsize, - const unsigned char* in, size_t insize, - const LodePNGCompressSettings* settings); - -#endif /*LODEPNG_COMPILE_ENCODER*/ -#endif /*LODEPNG_COMPILE_ZLIB*/ - -#ifdef LODEPNG_COMPILE_DISK -/* -Load a file from disk into buffer. The function allocates the out buffer, and -after usage you should free it. -out: output parameter, contains pointer to loaded buffer. -outsize: output parameter, size of the allocated out buffer -filename: the path to the file to load -return value: error code (0 means ok) -*/ -unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename); - -/* -Save a file from buffer to disk. Warning, if it exists, this function overwrites -the file without warning! -buffer: the buffer to write -buffersize: size of the buffer to write -filename: the path to the file to save to -return value: error code (0 means ok) -*/ -unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename); -#endif /*LODEPNG_COMPILE_DISK*/ - -#ifdef LODEPNG_COMPILE_CPP -/* The LodePNG C++ wrapper uses std::vectors instead of manually allocated memory buffers. */ -namespace lodepng -{ -#ifdef LODEPNG_COMPILE_PNG -class State : public LodePNGState -{ - public: - State(); - State(const State& other); - virtual ~State(); - State& operator=(const State& other); -}; - -#ifdef LODEPNG_COMPILE_DECODER -/* Same as other lodepng::decode, but using a State for more settings and information. */ -unsigned decode(std::vector& out, unsigned& w, unsigned& h, - State& state, - const unsigned char* in, size_t insize); -unsigned decode(std::vector& out, unsigned& w, unsigned& h, - State& state, - const std::vector& in); -#endif /*LODEPNG_COMPILE_DECODER*/ - -#ifdef LODEPNG_COMPILE_ENCODER -/* Same as other lodepng::encode, but using a State for more settings and information. */ -unsigned encode(std::vector& out, - const unsigned char* in, unsigned w, unsigned h, - State& state); -unsigned encode(std::vector& out, - const std::vector& in, unsigned w, unsigned h, - State& state); -#endif /*LODEPNG_COMPILE_ENCODER*/ - -#ifdef LODEPNG_COMPILE_DISK -/* -Load a file from disk into an std::vector. -return value: error code (0 means ok) -*/ -unsigned load_file(std::vector& buffer, const std::string& filename); - -/* -Save the binary data in an std::vector to a file on disk. The file is overwritten -without warning. -*/ -unsigned save_file(const std::vector& buffer, const std::string& filename); -#endif /* LODEPNG_COMPILE_DISK */ -#endif /* LODEPNG_COMPILE_PNG */ - -#ifdef LODEPNG_COMPILE_ZLIB -#ifdef LODEPNG_COMPILE_DECODER -/* Zlib-decompress an unsigned char buffer */ -unsigned decompress(std::vector& out, const unsigned char* in, size_t insize, - const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); - -/* Zlib-decompress an std::vector */ -unsigned decompress(std::vector& out, const std::vector& in, - const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); -#endif /* LODEPNG_COMPILE_DECODER */ - -#ifdef LODEPNG_COMPILE_ENCODER -/* Zlib-compress an unsigned char buffer */ -unsigned compress(std::vector& out, const unsigned char* in, size_t insize, - const LodePNGCompressSettings& settings = lodepng_default_compress_settings); - -/* Zlib-compress an std::vector */ -unsigned compress(std::vector& out, const std::vector& in, - const LodePNGCompressSettings& settings = lodepng_default_compress_settings); -#endif /* LODEPNG_COMPILE_ENCODER */ -#endif /* LODEPNG_COMPILE_ZLIB */ -} /* namespace lodepng */ -#endif /*LODEPNG_COMPILE_CPP*/ - -/* -TODO: -[.] test if there are no memory leaks or security exploits - done a lot but needs to be checked often -[.] check compatibility with various compilers - done but needs to be redone for every newer version -[X] converting color to 16-bit per channel types -[ ] read all public PNG chunk types (but never let the color profile and gamma ones touch RGB values) -[ ] make sure encoder generates no chunks with size > (2^31)-1 -[ ] partial decoding (stream processing) -[X] let the "isFullyOpaque" function check color keys and transparent palettes too -[X] better name for the variables "codes", "codesD", "codelengthcodes", "clcl" and "lldl" -[ ] don't stop decoding on errors like 69, 57, 58 (make warnings) -[ ] make warnings like: oob palette, checksum fail, data after iend, wrong/unknown crit chunk, no null terminator in text, ... -[ ] let the C++ wrapper catch exceptions coming from the standard library and return LodePNG error codes -[ ] allow user to provide custom color conversion functions, e.g. for premultiplied alpha, padding bits or not, ... -[ ] allow user to give data (void*) to custom allocator -*/ - -#endif /*LODEPNG_H inclusion guard*/ - -/* -LodePNG Documentation ---------------------- - -0. table of contents --------------------- - - 1. about - 1.1. supported features - 1.2. features not supported - 2. C and C++ version - 3. security - 4. decoding - 5. encoding - 6. color conversions - 6.1. PNG color types - 6.2. color conversions - 6.3. padding bits - 6.4. A note about 16-bits per channel and endianness - 7. error values - 8. chunks and PNG editing - 9. compiler support - 10. examples - 10.1. decoder C++ example - 10.2. decoder C example - 11. state settings reference - 12. changes - 13. contact information - - -1. about --------- - -PNG is a file format to store raster images losslessly with good compression, -supporting different color types and alpha channel. - -LodePNG is a PNG codec according to the Portable Network Graphics (PNG) -Specification (Second Edition) - W3C Recommendation 10 November 2003. - -The specifications used are: - -*) Portable Network Graphics (PNG) Specification (Second Edition): - http://www.w3.org/TR/2003/REC-PNG-20031110 -*) RFC 1950 ZLIB Compressed Data Format version 3.3: - http://www.gzip.org/zlib/rfc-zlib.html -*) RFC 1951 DEFLATE Compressed Data Format Specification ver 1.3: - http://www.gzip.org/zlib/rfc-deflate.html - -The most recent version of LodePNG can currently be found at -http://lodev.org/lodepng/ - -LodePNG works both in C (ISO C90) and C++, with a C++ wrapper that adds -extra functionality. - -LodePNG exists out of two files: --lodepng.h: the header file for both C and C++ --lodepng.c(pp): give it the name lodepng.c or lodepng.cpp (or .cc) depending on your usage - -If you want to start using LodePNG right away without reading this doc, get the -examples from the LodePNG website to see how to use it in code, or check the -smaller examples in chapter 13 here. - -LodePNG is simple but only supports the basic requirements. To achieve -simplicity, the following design choices were made: There are no dependencies -on any external library. There are functions to decode and encode a PNG with -a single function call, and extended versions of these functions taking a -LodePNGState struct allowing to specify or get more information. By default -the colors of the raw image are always RGB or RGBA, no matter what color type -the PNG file uses. To read and write files, there are simple functions to -convert the files to/from buffers in memory. - -This all makes LodePNG suitable for loading textures in games, demos and small -programs, ... It's less suitable for full fledged image editors, loading PNGs -over network (it requires all the image data to be available before decoding can -begin), life-critical systems, ... - -1.1. supported features ------------------------ - -The following features are supported by the decoder: - -*) decoding of PNGs with any color type, bit depth and interlace mode, to a 24- or 32-bit color raw image, - or the same color type as the PNG -*) encoding of PNGs, from any raw image to 24- or 32-bit color, or the same color type as the raw image -*) Adam7 interlace and deinterlace for any color type -*) loading the image from harddisk or decoding it from a buffer from other sources than harddisk -*) support for alpha channels, including RGBA color model, translucent palettes and color keying -*) zlib decompression (inflate) -*) zlib compression (deflate) -*) CRC32 and ADLER32 checksums -*) handling of unknown chunks, allowing making a PNG editor that stores custom and unknown chunks. -*) the following chunks are supported (generated/interpreted) by both encoder and decoder: - IHDR: header information - PLTE: color palette - IDAT: pixel data - IEND: the final chunk - tRNS: transparency for palettized images - tEXt: textual information - zTXt: compressed textual information - iTXt: international textual information - bKGD: suggested background color - pHYs: physical dimensions - tIME: modification time - -1.2. features not supported ---------------------------- - -The following features are _not_ supported: - -*) some features needed to make a conformant PNG-Editor might be still missing. -*) partial loading/stream processing. All data must be available and is processed in one call. -*) The following public chunks are not supported but treated as unknown chunks by LodePNG - cHRM, gAMA, iCCP, sRGB, sBIT, hIST, sPLT - Some of these are not supported on purpose: LodePNG wants to provide the RGB values - stored in the pixels, not values modified by system dependent gamma or color models. - - -2. C and C++ version --------------------- - -The C version uses buffers allocated with alloc that you need to free() -yourself. You need to use init and cleanup functions for each struct whenever -using a struct from the C version to avoid exploits and memory leaks. - -The C++ version has extra functions with std::vectors in the interface and the -lodepng::State class which is a LodePNGState with constructor and destructor. - -These files work without modification for both C and C++ compilers because all -the additional C++ code is in "#ifdef __cplusplus" blocks that make C-compilers -ignore it, and the C code is made to compile both with strict ISO C90 and C++. - -To use the C++ version, you need to rename the source file to lodepng.cpp -(instead of lodepng.c), and compile it with a C++ compiler. - -To use the C version, you need to rename the source file to lodepng.c (instead -of lodepng.cpp), and compile it with a C compiler. - - -3. Security ------------ - -Even if carefully designed, it's always possible that LodePNG contains possible -exploits. If you discover one, please let me know, and it will be fixed. - -When using LodePNG, care has to be taken with the C version of LodePNG, as well -as the C-style structs when working with C++. The following conventions are used -for all C-style structs: - --if a struct has a corresponding init function, always call the init function when making a new one --if a struct has a corresponding cleanup function, call it before the struct disappears to avoid memory leaks --if a struct has a corresponding copy function, use the copy function instead of "=". - The destination must also be inited already. - - -4. Decoding ------------ - -Decoding converts a PNG compressed image to a raw pixel buffer. - -Most documentation on using the decoder is at its declarations in the header -above. For C, simple decoding can be done with functions such as -lodepng_decode32, and more advanced decoding can be done with the struct -LodePNGState and lodepng_decode. For C++, all decoding can be done with the -various lodepng::decode functions, and lodepng::State can be used for advanced -features. - -When using the LodePNGState, it uses the following fields for decoding: -*) LodePNGInfo info_png: it stores extra information about the PNG (the input) in here -*) LodePNGColorMode info_raw: here you can say what color mode of the raw image (the output) you want to get -*) LodePNGDecoderSettings decoder: you can specify a few extra settings for the decoder to use - -LodePNGInfo info_png --------------------- - -After decoding, this contains extra information of the PNG image, except the actual -pixels, width and height because these are already gotten directly from the decoder -functions. - -It contains for example the original color type of the PNG image, text comments, -suggested background color, etc... More details about the LodePNGInfo struct are -at its declaration documentation. - -LodePNGColorMode info_raw -------------------------- - -When decoding, here you can specify which color type you want -the resulting raw image to be. If this is different from the colortype of the -PNG, then the decoder will automatically convert the result. This conversion -always works, except if you want it to convert a color PNG to greyscale or to -a palette with missing colors. - -By default, 32-bit color is used for the result. - -LodePNGDecoderSettings decoder ------------------------------- - -The settings can be used to ignore the errors created by invalid CRC and Adler32 -chunks, and to disable the decoding of tEXt chunks. - -There's also a setting color_convert, true by default. If false, no conversion -is done, the resulting data will be as it was in the PNG (after decompression) -and you'll have to puzzle the colors of the pixels together yourself using the -color type information in the LodePNGInfo. - - -5. Encoding ------------ - -Encoding converts a raw pixel buffer to a PNG compressed image. - -Most documentation on using the encoder is at its declarations in the header -above. For C, simple encoding can be done with functions such as -lodepng_encode32, and more advanced decoding can be done with the struct -LodePNGState and lodepng_encode. For C++, all encoding can be done with the -various lodepng::encode functions, and lodepng::State can be used for advanced -features. - -Like the decoder, the encoder can also give errors. However it gives less errors -since the encoder input is trusted, the decoder input (a PNG image that could -be forged by anyone) is not trusted. - -When using the LodePNGState, it uses the following fields for encoding: -*) LodePNGInfo info_png: here you specify how you want the PNG (the output) to be. -*) LodePNGColorMode info_raw: here you say what color type of the raw image (the input) has -*) LodePNGEncoderSettings encoder: you can specify a few settings for the encoder to use - -LodePNGInfo info_png --------------------- - -When encoding, you use this the opposite way as when decoding: for encoding, -you fill in the values you want the PNG to have before encoding. By default it's -not needed to specify a color type for the PNG since it's automatically chosen, -but it's possible to choose it yourself given the right settings. - -The encoder will not always exactly match the LodePNGInfo struct you give, -it tries as close as possible. Some things are ignored by the encoder. The -encoder uses, for example, the following settings from it when applicable: -colortype and bitdepth, text chunks, time chunk, the color key, the palette, the -background color, the interlace method, unknown chunks, ... - -When encoding to a PNG with colortype 3, the encoder will generate a PLTE chunk. -If the palette contains any colors for which the alpha channel is not 255 (so -there are translucent colors in the palette), it'll add a tRNS chunk. - -LodePNGColorMode info_raw -------------------------- - -You specify the color type of the raw image that you give to the input here, -including a possible transparent color key and palette you happen to be using in -your raw image data. - -By default, 32-bit color is assumed, meaning your input has to be in RGBA -format with 4 bytes (unsigned chars) per pixel. - -LodePNGEncoderSettings encoder ------------------------------- - -The following settings are supported (some are in sub-structs): -*) auto_convert: when this option is enabled, the encoder will -automatically choose the smallest possible color mode (including color key) that -can encode the colors of all pixels without information loss. -*) btype: the block type for LZ77. 0 = uncompressed, 1 = fixed huffman tree, - 2 = dynamic huffman tree (best compression). Should be 2 for proper - compression. -*) use_lz77: whether or not to use LZ77 for compressed block types. Should be - true for proper compression. -*) windowsize: the window size used by the LZ77 encoder (1 - 32768). Has value - 2048 by default, but can be set to 32768 for better, but slow, compression. -*) force_palette: if colortype is 2 or 6, you can make the encoder write a PLTE - chunk if force_palette is true. This can used as suggested palette to convert - to by viewers that don't support more than 256 colors (if those still exist) -*) add_id: add text chunk "Encoder: LodePNG " to the image. -*) text_compression: default 1. If 1, it'll store texts as zTXt instead of tEXt chunks. - zTXt chunks use zlib compression on the text. This gives a smaller result on - large texts but a larger result on small texts (such as a single program name). - It's all tEXt or all zTXt though, there's no separate setting per text yet. - - -6. color conversions --------------------- - -An important thing to note about LodePNG, is that the color type of the PNG, and -the color type of the raw image, are completely independent. By default, when -you decode a PNG, you get the result as a raw image in the color type you want, -no matter whether the PNG was encoded with a palette, greyscale or RGBA color. -And if you encode an image, by default LodePNG will automatically choose the PNG -color type that gives good compression based on the values of colors and amount -of colors in the image. It can be configured to let you control it instead as -well, though. - -To be able to do this, LodePNG does conversions from one color mode to another. -It can convert from almost any color type to any other color type, except the -following conversions: RGB to greyscale is not supported, and converting to a -palette when the palette doesn't have a required color is not supported. This is -not supported on purpose: this is information loss which requires a color -reduction algorithm that is beyong the scope of a PNG encoder (yes, RGB to grey -is easy, but there are multiple ways if you want to give some channels more -weight). - -By default, when decoding, you get the raw image in 32-bit RGBA or 24-bit RGB -color, no matter what color type the PNG has. And by default when encoding, -LodePNG automatically picks the best color model for the output PNG, and expects -the input image to be 32-bit RGBA or 24-bit RGB. So, unless you want to control -the color format of the images yourself, you can skip this chapter. - -6.1. PNG color types --------------------- - -A PNG image can have many color types, ranging from 1-bit color to 64-bit color, -as well as palettized color modes. After the zlib decompression and unfiltering -in the PNG image is done, the raw pixel data will have that color type and thus -a certain amount of bits per pixel. If you want the output raw image after -decoding to have another color type, a conversion is done by LodePNG. - -The PNG specification gives the following color types: - -0: greyscale, bit depths 1, 2, 4, 8, 16 -2: RGB, bit depths 8 and 16 -3: palette, bit depths 1, 2, 4 and 8 -4: greyscale with alpha, bit depths 8 and 16 -6: RGBA, bit depths 8 and 16 - -Bit depth is the amount of bits per pixel per color channel. So the total amount -of bits per pixel is: amount of channels * bitdepth. - -6.2. color conversions ----------------------- - -As explained in the sections about the encoder and decoder, you can specify -color types and bit depths in info_png and info_raw to change the default -behaviour. - -If, when decoding, you want the raw image to be something else than the default, -you need to set the color type and bit depth you want in the LodePNGColorMode, -or the parameters colortype and bitdepth of the simple decoding function. - -If, when encoding, you use another color type than the default in the raw input -image, you need to specify its color type and bit depth in the LodePNGColorMode -of the raw image, or use the parameters colortype and bitdepth of the simple -encoding function. - -If, when encoding, you don't want LodePNG to choose the output PNG color type -but control it yourself, you need to set auto_convert in the encoder settings -to false, and specify the color type you want in the LodePNGInfo of the -encoder (including palette: it can generate a palette if auto_convert is true, -otherwise not). - -If the input and output color type differ (whether user chosen or auto chosen), -LodePNG will do a color conversion, which follows the rules below, and may -sometimes result in an error. - -To avoid some confusion: --the decoder converts from PNG to raw image --the encoder converts from raw image to PNG --the colortype and bitdepth in LodePNGColorMode info_raw, are those of the raw image --the colortype and bitdepth in the color field of LodePNGInfo info_png, are those of the PNG --when encoding, the color type in LodePNGInfo is ignored if auto_convert - is enabled, it is automatically generated instead --when decoding, the color type in LodePNGInfo is set by the decoder to that of the original - PNG image, but it can be ignored since the raw image has the color type you requested instead --if the color type of the LodePNGColorMode and PNG image aren't the same, a conversion - between the color types is done if the color types are supported. If it is not - supported, an error is returned. If the types are the same, no conversion is done. --even though some conversions aren't supported, LodePNG supports loading PNGs from any - colortype and saving PNGs to any colortype, sometimes it just requires preparing - the raw image correctly before encoding. --both encoder and decoder use the same color converter. - -Non supported color conversions: --color to greyscale: no error is thrown, but the result will look ugly because -only the red channel is taken --anything to palette when that palette does not have that color in it: in this -case an error is thrown - -Supported color conversions: --anything to 8-bit RGB, 8-bit RGBA, 16-bit RGB, 16-bit RGBA --any grey or grey+alpha, to grey or grey+alpha --anything to a palette, as long as the palette has the requested colors in it --removing alpha channel --higher to smaller bitdepth, and vice versa - -If you want no color conversion to be done (e.g. for speed or control): --In the encoder, you can make it save a PNG with any color type by giving the -raw color mode and LodePNGInfo the same color mode, and setting auto_convert to -false. --In the decoder, you can make it store the pixel data in the same color type -as the PNG has, by setting the color_convert setting to false. Settings in -info_raw are then ignored. - -The function lodepng_convert does the color conversion. It is available in the -interface but normally isn't needed since the encoder and decoder already call -it. - -6.3. padding bits ------------------ - -In the PNG file format, if a less than 8-bit per pixel color type is used and the scanlines -have a bit amount that isn't a multiple of 8, then padding bits are used so that each -scanline starts at a fresh byte. But that is NOT true for the LodePNG raw input and output. -The raw input image you give to the encoder, and the raw output image you get from the decoder -will NOT have these padding bits, e.g. in the case of a 1-bit image with a width -of 7 pixels, the first pixel of the second scanline will the the 8th bit of the first byte, -not the first bit of a new byte. - -6.4. A note about 16-bits per channel and endianness ----------------------------------------------------- - -LodePNG uses unsigned char arrays for 16-bit per channel colors too, just like -for any other color format. The 16-bit values are stored in big endian (most -significant byte first) in these arrays. This is the opposite order of the -little endian used by x86 CPU's. - -LodePNG always uses big endian because the PNG file format does so internally. -Conversions to other formats than PNG uses internally are not supported by -LodePNG on purpose, there are myriads of formats, including endianness of 16-bit -colors, the order in which you store R, G, B and A, and so on. Supporting and -converting to/from all that is outside the scope of LodePNG. - -This may mean that, depending on your use case, you may want to convert the big -endian output of LodePNG to little endian with a for loop. This is certainly not -always needed, many applications and libraries support big endian 16-bit colors -anyway, but it means you cannot simply cast the unsigned char* buffer to an -unsigned short* buffer on x86 CPUs. - - -7. error values ---------------- - -All functions in LodePNG that return an error code, return 0 if everything went -OK, or a non-zero code if there was an error. - -The meaning of the LodePNG error values can be retrieved with the function -lodepng_error_text: given the numerical error code, it returns a description -of the error in English as a string. - -Check the implementation of lodepng_error_text to see the meaning of each code. - - -8. chunks and PNG editing -------------------------- - -If you want to add extra chunks to a PNG you encode, or use LodePNG for a PNG -editor that should follow the rules about handling of unknown chunks, or if your -program is able to read other types of chunks than the ones handled by LodePNG, -then that's possible with the chunk functions of LodePNG. - -A PNG chunk has the following layout: - -4 bytes length -4 bytes type name -length bytes data -4 bytes CRC - -8.1. iterating through chunks ------------------------------ - -If you have a buffer containing the PNG image data, then the first chunk (the -IHDR chunk) starts at byte number 8 of that buffer. The first 8 bytes are the -signature of the PNG and are not part of a chunk. But if you start at byte 8 -then you have a chunk, and can check the following things of it. - -NOTE: none of these functions check for memory buffer boundaries. To avoid -exploits, always make sure the buffer contains all the data of the chunks. -When using lodepng_chunk_next, make sure the returned value is within the -allocated memory. - -unsigned lodepng_chunk_length(const unsigned char* chunk): - -Get the length of the chunk's data. The total chunk length is this length + 12. - -void lodepng_chunk_type(char type[5], const unsigned char* chunk): -unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type): - -Get the type of the chunk or compare if it's a certain type - -unsigned char lodepng_chunk_critical(const unsigned char* chunk): -unsigned char lodepng_chunk_private(const unsigned char* chunk): -unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk): - -Check if the chunk is critical in the PNG standard (only IHDR, PLTE, IDAT and IEND are). -Check if the chunk is private (public chunks are part of the standard, private ones not). -Check if the chunk is safe to copy. If it's not, then, when modifying data in a critical -chunk, unsafe to copy chunks of the old image may NOT be saved in the new one if your -program doesn't handle that type of unknown chunk. - -unsigned char* lodepng_chunk_data(unsigned char* chunk): -const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk): - -Get a pointer to the start of the data of the chunk. - -unsigned lodepng_chunk_check_crc(const unsigned char* chunk): -void lodepng_chunk_generate_crc(unsigned char* chunk): - -Check if the crc is correct or generate a correct one. - -unsigned char* lodepng_chunk_next(unsigned char* chunk): -const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk): - -Iterate to the next chunk. This works if you have a buffer with consecutive chunks. Note that these -functions do no boundary checking of the allocated data whatsoever, so make sure there is enough -data available in the buffer to be able to go to the next chunk. - -unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk): -unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, - const char* type, const unsigned char* data): - -These functions are used to create new chunks that are appended to the data in *out that has -length *outlength. The append function appends an existing chunk to the new data. The create -function creates a new chunk with the given parameters and appends it. Type is the 4-letter -name of the chunk. - -8.2. chunks in info_png ------------------------ - -The LodePNGInfo struct contains fields with the unknown chunk in it. It has 3 -buffers (each with size) to contain 3 types of unknown chunks: -the ones that come before the PLTE chunk, the ones that come between the PLTE -and the IDAT chunks, and the ones that come after the IDAT chunks. -It's necessary to make the distionction between these 3 cases because the PNG -standard forces to keep the ordering of unknown chunks compared to the critical -chunks, but does not force any other ordering rules. - -info_png.unknown_chunks_data[0] is the chunks before PLTE -info_png.unknown_chunks_data[1] is the chunks after PLTE, before IDAT -info_png.unknown_chunks_data[2] is the chunks after IDAT - -The chunks in these 3 buffers can be iterated through and read by using the same -way described in the previous subchapter. - -When using the decoder to decode a PNG, you can make it store all unknown chunks -if you set the option settings.remember_unknown_chunks to 1. By default, this -option is off (0). - -The encoder will always encode unknown chunks that are stored in the info_png. -If you need it to add a particular chunk that isn't known by LodePNG, you can -use lodepng_chunk_append or lodepng_chunk_create to the chunk data in -info_png.unknown_chunks_data[x]. - -Chunks that are known by LodePNG should not be added in that way. E.g. to make -LodePNG add a bKGD chunk, set background_defined to true and add the correct -parameters there instead. - - -9. compiler support -------------------- - -No libraries other than the current standard C library are needed to compile -LodePNG. For the C++ version, only the standard C++ library is needed on top. -Add the files lodepng.c(pp) and lodepng.h to your project, include -lodepng.h where needed, and your program can read/write PNG files. - -It is compatible with C90 and up, and C++03 and up. - -If performance is important, use optimization when compiling! For both the -encoder and decoder, this makes a large difference. - -Make sure that LodePNG is compiled with the same compiler of the same version -and with the same settings as the rest of the program, or the interfaces with -std::vectors and std::strings in C++ can be incompatible. - -CHAR_BITS must be 8 or higher, because LodePNG uses unsigned chars for octets. - -*) gcc and g++ - -LodePNG is developed in gcc so this compiler is natively supported. It gives no -warnings with compiler options "-Wall -Wextra -pedantic -ansi", with gcc and g++ -version 4.7.1 on Linux, 32-bit and 64-bit. - -*) Clang - -Fully supported and warning-free. - -*) Mingw - -The Mingw compiler (a port of gcc for Windows) should be fully supported by -LodePNG. - -*) Visual Studio and Visual C++ Express Edition - -LodePNG should be warning-free with warning level W4. Two warnings were disabled -with pragmas though: warning 4244 about implicit conversions, and warning 4996 -where it wants to use a non-standard function fopen_s instead of the standard C -fopen. - -Visual Studio may want "stdafx.h" files to be included in each source file and -give an error "unexpected end of file while looking for precompiled header". -This is not standard C++ and will not be added to the stock LodePNG. You can -disable it for lodepng.cpp only by right clicking it, Properties, C/C++, -Precompiled Headers, and set it to Not Using Precompiled Headers there. - -NOTE: Modern versions of VS should be fully supported, but old versions, e.g. -VS6, are not guaranteed to work. - -*) Compilers on Macintosh - -LodePNG has been reported to work both with gcc and LLVM for Macintosh, both for -C and C++. - -*) Other Compilers - -If you encounter problems on any compilers, feel free to let me know and I may -try to fix it if the compiler is modern and standards complient. - - -10. examples ------------- - -This decoder example shows the most basic usage of LodePNG. More complex -examples can be found on the LodePNG website. - -10.1. decoder C++ example -------------------------- - -#include "lodepng.h" -#include - -int main(int argc, char *argv[]) -{ - const char* filename = argc > 1 ? argv[1] : "test.png"; - - //load and decode - std::vector image; - unsigned width, height; - unsigned error = lodepng::decode(image, width, height, filename); - - //if there's an error, display it - if(error) std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl; - - //the pixels are now in the vector "image", 4 bytes per pixel, ordered RGBARGBA..., use it as texture, draw it, ... -} - -10.2. decoder C example ------------------------ - -#include "lodepng.h" - -int main(int argc, char *argv[]) -{ - unsigned error; - unsigned char* image; - size_t width, height; - const char* filename = argc > 1 ? argv[1] : "test.png"; - - error = lodepng_decode32_file(&image, &width, &height, filename); - - if(error) printf("decoder error %u: %s\n", error, lodepng_error_text(error)); - - / * use image here * / - - free(image); - return 0; -} - -11. state settings reference ----------------------------- - -A quick reference of some settings to set on the LodePNGState - -For decoding: - -state.decoder.zlibsettings.ignore_adler32: ignore ADLER32 checksums -state.decoder.zlibsettings.custom_...: use custom inflate function -state.decoder.ignore_crc: ignore CRC checksums -state.decoder.ignore_critical: ignore unknown critical chunks -state.decoder.ignore_end: ignore missing IEND chunk. May fail if this corruption causes other errors -state.decoder.color_convert: convert internal PNG color to chosen one -state.decoder.read_text_chunks: whether to read in text metadata chunks -state.decoder.remember_unknown_chunks: whether to read in unknown chunks -state.info_raw.colortype: desired color type for decoded image -state.info_raw.bitdepth: desired bit depth for decoded image -state.info_raw....: more color settings, see struct LodePNGColorMode -state.info_png....: no settings for decoder but ouput, see struct LodePNGInfo - -For encoding: - -state.encoder.zlibsettings.btype: disable compression by setting it to 0 -state.encoder.zlibsettings.use_lz77: use LZ77 in compression -state.encoder.zlibsettings.windowsize: tweak LZ77 windowsize -state.encoder.zlibsettings.minmatch: tweak min LZ77 length to match -state.encoder.zlibsettings.nicematch: tweak LZ77 match where to stop searching -state.encoder.zlibsettings.lazymatching: try one more LZ77 matching -state.encoder.zlibsettings.custom_...: use custom deflate function -state.encoder.auto_convert: choose optimal PNG color type, if 0 uses info_png -state.encoder.filter_palette_zero: PNG filter strategy for palette -state.encoder.filter_strategy: PNG filter strategy to encode with -state.encoder.force_palette: add palette even if not encoding to one -state.encoder.add_id: add LodePNG identifier and version as a text chunk -state.encoder.text_compression: use compressed text chunks for metadata -state.info_raw.colortype: color type of raw input image you provide -state.info_raw.bitdepth: bit depth of raw input image you provide -state.info_raw: more color settings, see struct LodePNGColorMode -state.info_png.color.colortype: desired color type if auto_convert is false -state.info_png.color.bitdepth: desired bit depth if auto_convert is false -state.info_png.color....: more color settings, see struct LodePNGColorMode -state.info_png....: more PNG related settings, see struct LodePNGInfo - - -12. changes ------------ - -The version number of LodePNG is the date of the change given in the format -yyyymmdd. - -Some changes aren't backwards compatible. Those are indicated with a (!) -symbol. - -*) 14 jan 2018: allow optionally ignoring a few more recoverable errors -*) 17 sep 2017: fix memory leak for some encoder input error cases -*) 27 nov 2016: grey+alpha auto color model detection bugfix -*) 18 apr 2016: Changed qsort to custom stable sort (for platforms w/o qsort). -*) 09 apr 2016: Fixed colorkey usage detection, and better file loading (within - the limits of pure C90). -*) 08 dec 2015: Made load_file function return error if file can't be opened. -*) 24 okt 2015: Bugfix with decoding to palette output. -*) 18 apr 2015: Boundary PM instead of just package-merge for faster encoding. -*) 23 aug 2014: Reduced needless memory usage of decoder. -*) 28 jun 2014: Removed fix_png setting, always support palette OOB for - simplicity. Made ColorProfile public. -*) 09 jun 2014: Faster encoder by fixing hash bug and more zeros optimization. -*) 22 dec 2013: Power of two windowsize required for optimization. -*) 15 apr 2013: Fixed bug with LAC_ALPHA and color key. -*) 25 mar 2013: Added an optional feature to ignore some PNG errors (fix_png). -*) 11 mar 2013 (!): Bugfix with custom free. Changed from "my" to "lodepng_" - prefix for the custom allocators and made it possible with a new #define to - use custom ones in your project without needing to change lodepng's code. -*) 28 jan 2013: Bugfix with color key. -*) 27 okt 2012: Tweaks in text chunk keyword length error handling. -*) 8 okt 2012 (!): Added new filter strategy (entropy) and new auto color mode. - (no palette). Better deflate tree encoding. New compression tweak settings. - Faster color conversions while decoding. Some internal cleanups. -*) 23 sep 2012: Reduced warnings in Visual Studio a little bit. -*) 1 sep 2012 (!): Removed #define's for giving custom (de)compression functions - and made it work with function pointers instead. -*) 23 jun 2012: Added more filter strategies. Made it easier to use custom alloc - and free functions and toggle #defines from compiler flags. Small fixes. -*) 6 may 2012 (!): Made plugging in custom zlib/deflate functions more flexible. -*) 22 apr 2012 (!): Made interface more consistent, renaming a lot. Removed - redundant C++ codec classes. Reduced amount of structs. Everything changed, - but it is cleaner now imho and functionality remains the same. Also fixed - several bugs and shrunk the implementation code. Made new samples. -*) 6 nov 2011 (!): By default, the encoder now automatically chooses the best - PNG color model and bit depth, based on the amount and type of colors of the - raw image. For this, autoLeaveOutAlphaChannel replaced by auto_choose_color. -*) 9 okt 2011: simpler hash chain implementation for the encoder. -*) 8 sep 2011: lz77 encoder lazy matching instead of greedy matching. -*) 23 aug 2011: tweaked the zlib compression parameters after benchmarking. - A bug with the PNG filtertype heuristic was fixed, so that it chooses much - better ones (it's quite significant). A setting to do an experimental, slow, - brute force search for PNG filter types is added. -*) 17 aug 2011 (!): changed some C zlib related function names. -*) 16 aug 2011: made the code less wide (max 120 characters per line). -*) 17 apr 2011: code cleanup. Bugfixes. Convert low to 16-bit per sample colors. -*) 21 feb 2011: fixed compiling for C90. Fixed compiling with sections disabled. -*) 11 dec 2010: encoding is made faster, based on suggestion by Peter Eastman - to optimize long sequences of zeros. -*) 13 nov 2010: added LodePNG_InfoColor_hasPaletteAlpha and - LodePNG_InfoColor_canHaveAlpha functions for convenience. -*) 7 nov 2010: added LodePNG_error_text function to get error code description. -*) 30 okt 2010: made decoding slightly faster -*) 26 okt 2010: (!) changed some C function and struct names (more consistent). - Reorganized the documentation and the declaration order in the header. -*) 08 aug 2010: only changed some comments and external samples. -*) 05 jul 2010: fixed bug thanks to warnings in the new gcc version. -*) 14 mar 2010: fixed bug where too much memory was allocated for char buffers. -*) 02 sep 2008: fixed bug where it could create empty tree that linux apps could - read by ignoring the problem but windows apps couldn't. -*) 06 jun 2008: added more error checks for out of memory cases. -*) 26 apr 2008: added a few more checks here and there to ensure more safety. -*) 06 mar 2008: crash with encoding of strings fixed -*) 02 feb 2008: support for international text chunks added (iTXt) -*) 23 jan 2008: small cleanups, and #defines to divide code in sections -*) 20 jan 2008: support for unknown chunks allowing using LodePNG for an editor. -*) 18 jan 2008: support for tIME and pHYs chunks added to encoder and decoder. -*) 17 jan 2008: ability to encode and decode compressed zTXt chunks added - Also various fixes, such as in the deflate and the padding bits code. -*) 13 jan 2008: Added ability to encode Adam7-interlaced images. Improved - filtering code of encoder. -*) 07 jan 2008: (!) changed LodePNG to use ISO C90 instead of C++. A - C++ wrapper around this provides an interface almost identical to before. - Having LodePNG be pure ISO C90 makes it more portable. The C and C++ code - are together in these files but it works both for C and C++ compilers. -*) 29 dec 2007: (!) changed most integer types to unsigned int + other tweaks -*) 30 aug 2007: bug fixed which makes this Borland C++ compatible -*) 09 aug 2007: some VS2005 warnings removed again -*) 21 jul 2007: deflate code placed in new namespace separate from zlib code -*) 08 jun 2007: fixed bug with 2- and 4-bit color, and small interlaced images -*) 04 jun 2007: improved support for Visual Studio 2005: crash with accessing - invalid std::vector element [0] fixed, and level 3 and 4 warnings removed -*) 02 jun 2007: made the encoder add a tag with version by default -*) 27 may 2007: zlib and png code separated (but still in the same file), - simple encoder/decoder functions added for more simple usage cases -*) 19 may 2007: minor fixes, some code cleaning, new error added (error 69), - moved some examples from here to lodepng_examples.cpp -*) 12 may 2007: palette decoding bug fixed -*) 24 apr 2007: changed the license from BSD to the zlib license -*) 11 mar 2007: very simple addition: ability to encode bKGD chunks. -*) 04 mar 2007: (!) tEXt chunk related fixes, and support for encoding - palettized PNG images. Plus little interface change with palette and texts. -*) 03 mar 2007: Made it encode dynamic Huffman shorter with repeat codes. - Fixed a bug where the end code of a block had length 0 in the Huffman tree. -*) 26 feb 2007: Huffman compression with dynamic trees (BTYPE 2) now implemented - and supported by the encoder, resulting in smaller PNGs at the output. -*) 27 jan 2007: Made the Adler-32 test faster so that a timewaste is gone. -*) 24 jan 2007: gave encoder an error interface. Added color conversion from any - greyscale type to 8-bit greyscale with or without alpha. -*) 21 jan 2007: (!) Totally changed the interface. It allows more color types - to convert to and is more uniform. See the manual for how it works now. -*) 07 jan 2007: Some cleanup & fixes, and a few changes over the last days: - encode/decode custom tEXt chunks, separate classes for zlib & deflate, and - at last made the decoder give errors for incorrect Adler32 or Crc. -*) 01 jan 2007: Fixed bug with encoding PNGs with less than 8 bits per channel. -*) 29 dec 2006: Added support for encoding images without alpha channel, and - cleaned out code as well as making certain parts faster. -*) 28 dec 2006: Added "Settings" to the encoder. -*) 26 dec 2006: The encoder now does LZ77 encoding and produces much smaller files now. - Removed some code duplication in the decoder. Fixed little bug in an example. -*) 09 dec 2006: (!) Placed output parameters of public functions as first parameter. - Fixed a bug of the decoder with 16-bit per color. -*) 15 okt 2006: Changed documentation structure -*) 09 okt 2006: Encoder class added. It encodes a valid PNG image from the - given image buffer, however for now it's not compressed. -*) 08 sep 2006: (!) Changed to interface with a Decoder class -*) 30 jul 2006: (!) LodePNG_InfoPng , width and height are now retrieved in different - way. Renamed decodePNG to decodePNGGeneric. -*) 29 jul 2006: (!) Changed the interface: image info is now returned as a - struct of type LodePNG::LodePNG_Info, instead of a vector, which was a bit clumsy. -*) 28 jul 2006: Cleaned the code and added new error checks. - Corrected terminology "deflate" into "inflate". -*) 23 jun 2006: Added SDL example in the documentation in the header, this - example allows easy debugging by displaying the PNG and its transparency. -*) 22 jun 2006: (!) Changed way to obtain error value. Added - loadFile function for convenience. Made decodePNG32 faster. -*) 21 jun 2006: (!) Changed type of info vector to unsigned. - Changed position of palette in info vector. Fixed an important bug that - happened on PNGs with an uncompressed block. -*) 16 jun 2006: Internally changed unsigned into unsigned where - needed, and performed some optimizations. -*) 07 jun 2006: (!) Renamed functions to decodePNG and placed them - in LodePNG namespace. Changed the order of the parameters. Rewrote the - documentation in the header. Renamed files to lodepng.cpp and lodepng.h -*) 22 apr 2006: Optimized and improved some code -*) 07 sep 2005: (!) Changed to std::vector interface -*) 12 aug 2005: Initial release (C++, decoder only) - - -13. contact information ------------------------ - -Feel free to contact me with suggestions, problems, comments, ... concerning -LodePNG. If you encounter a PNG image that doesn't work properly with this -decoder, feel free to send it and I'll use it to find and fix the problem. - -My email address is (puzzle the account and domain together with an @ symbol): -Domain: gmail dot com. -Account: lode dot vandevenne. - - -Copyright (c) 2005-2017 Lode Vandevenne -*/ diff --git a/include/pp2d/pp2d.h b/include/pp2d/pp2d.h deleted file mode 100644 index d3e5806..0000000 --- a/include/pp2d/pp2d.h +++ /dev/null @@ -1,493 +0,0 @@ -/* MIT License - * - * Copyright (c) 2017 Bernardo Giordano - * - * 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. - * - * https://discord.gg/bGKEyfY - */ - -/** - * Plug & Play 2D - * @file pp2d.c - * @author Bernardo Giordano - * @date 17 January 2018 - * @brief pp2d implementation - */ - -#ifndef PP2D_H -#define PP2D_H - -#include "lodepng.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#include <3ds.h> -#include -#include -#include -#include -#include -#include -#include "vshader_shbin.h" - -#define TOP_WIDTH 400 -#define BOTTOM_WIDTH 320 -#define SCREEN_HEIGHT 240 - -/** - * @brief Used to transfer the final rendered display to the framebuffer - */ -#define DISPLAY_TRANSFER_FLAGS \ - (GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | GX_TRANSFER_RAW_COPY(0) | \ - GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | \ - GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO)) - -/** - * @brief Creates a 8 byte RGBA color - * @param r red component of the color - * @param g green component of the color - * @param b blue component of the color - * @param a alpha component of the color - */ -#define RGBA8(r, g, b, a) ((((r)&0xFF)<<0) | (((g)&0xFF)<<8) | (((b)&0xFF)<<16) | (((a)&0xFF)<<24)) - -/** - * @brief Creates a 8 byte ABGR color - * @param a alpha component of the color - * @param b blue component of the color - * @param g green component of the color - * @param r red component of the color - */ -#define ABGR8(a, b, g, r) ((((a)&0xFF)<<0) | (((b)&0xFF)<<8) | (((g)&0xFF)<<16) | (((r)&0xFF)<<24)) - -/** - * @brief Converts a RGB565 color to RGBA8 color (adds maximum alpha) - * @param rgb 565 to be converted - * @param a alpha - */ -#define RGB565_TO_RGBA8(rgb, a) \ - (RGBA8(((rgb>>11)&0x1F)*0xFF/0x1F, ((rgb>>5)&0x3F)*0xFF/0x3F, (rgb&0x1F)*0xFF/0x1F, a&0xFF)) - -/** - * @brief Converts a RGB565 color to ABGR8 color (adds maximum alpha) - * @param rgb 565 to be converted - * @param a alpha - */ -#define RGB565_TO_ABGR8(rgb, a) \ - (RGBA8(a&0xFF, (rgb&0x1F)*0xFF/0x1F, ((rgb>>5)&0x3F)*0xFF/0x3F, ((rgb>>11)&0x1F)*0xFF/0x1F)) - -#define BACKGROUND_COLOR ABGR8(255, 0, 0, 0) -#define PP2D_NEUTRAL RGBA8(255, 255, 255, 255) - -#define DEFAULT_DEPTH 0.5f - -#define MAX_TEXTURES 1024 - -#define TEXT_VTX_ARRAY_COUNT (4*1024) - -typedef enum { - NONE, - HORIZONTAL, - VERTICAL, - BOTH -} flipType; - -typedef struct { - float position[3]; - float texcoord[2]; -} textVertex_s; - -/** - * @brief Starts a new frame on the specified screen - * @param target GFX_TOP or GFX_BOTTOM - */ -void pp2d_begin_draw(gfxScreen_t target, gfx3dSide_t side); - -/** - * @brief Changes target screen to the specified target - * @param target GFX_TOP or GFX_BOTTOM - */ -void pp2d_draw_on(gfxScreen_t target, gfx3dSide_t side); - -/** - * @brief Draw a rectangle - * @param x of the top left corner - * @param y of the top left corner - * @param width on the rectangle - * @param height of the rectangle - * @param color RGBA8 to fill the rectangle - */ -void pp2d_draw_rectangle(int x, int y, int width, int height, u32 color); - -/** - * @brief Prints a char pointer - * @param x position to start drawing - * @param y position to start drawing - * @param scaleX multiplier for the text width - * @param scaleY multiplier for the text height - * @param color RGBA8 the text will be drawn - * @param text to be printed on the screen - */ -void pp2d_draw_text(float x, float y, float scaleX, float scaleY, u32 color, const char* text); - -/** - * @brief Prints a char pointer in the middle of the target screen - * @param target screen to draw the text to - * @param y position to start drawing - * @param scaleX multiplier for the text width - * @param scaleY multiplier for the text height - * @param color RGBA8 the text will be drawn - * @param text to be printed on the screen - */ -void pp2d_draw_text_center(gfxScreen_t target, float y, float scaleX, float scaleY, u32 color, const char* text); - -/** - * @brief Prints a char pointer in the middle of the target screen - * @param x position to start drawing - * @param y position to start drawing - * @param scaleX multiplier for the text width - * @param scaleY multiplier for the text height - * @param color RGBA8 the text will be drawn - * @param wrapX wrap width - * @param text to be printed on the screen - */ -void pp2d_draw_text_wrap(float x, float y, float scaleX, float scaleY, u32 color, float wrapX, const char* text); - -/** - * @brief Prints a char pointer with arguments - * @param x position to start drawing - * @param y position to start drawing - * @param scaleX multiplier for the text width - * @param scaleY multiplier for the text height - * @param color RGBA8 the text will be drawn - * @param text to be printed on the screen - * @param ... arguments - */ -void pp2d_draw_textf(float x, float y, float scaleX, float scaleY, u32 color, const char* text, ...); - -/** - * @brief Prints a texture - * @param id of the texture - * @param x position on the screen to draw the texture - * @param y position on the screen to draw the texture - */ -void pp2d_draw_texture(size_t id, int x, int y); - -/** - * @brief Prints a texture with color modulation - * @param id of the texture - * @param x position on the screen to draw the texture - * @param y position on the screen to draw the texture - * @param color RGBA8 to modulate the texture with - */ -void pp2d_draw_texture_blend(size_t id, int x, int y, u32 color); - -/** - * @brief Prints a flipped texture - * @param id of the texture - * @param x position on the screen to draw the texture - * @param y position on the screen to draw the texture - * @param fliptype HORIZONTAL, VERTICAL or BOTH - */ - -void pp2d_draw_texture_flip(size_t id, int x, int y, flipType fliptype); - -/** - * @brief Prints a rotated texture - * @param id of the texture - * @param x position on the screen to draw the texture - * @param y position on the screen to draw the texture - * @param angle in degrees to rotate the texture - */ -void pp2d_draw_texture_rotate(size_t id, int x, int y, float angle); - -/** - * @brief Prints a texture with a scale factor - * @param id of the texture - * @param x position on the screen to draw the texture - * @param y position on the screen to draw the texture - * @param scaleX width scale factor - * @param scaleY height scale factor - */ -void pp2d_draw_texture_scale(size_t id, int x, int y, float scaleX, float scaleY); - -/** - * @brief Prints a portion of a texture - * @param id of the texture - * @param x position on the screen to draw the texture - * @param y position on the screen to draw the texture - * @param xbegin position to start drawing - * @param ybegin position to start drawing - * @param width to draw from the xbegin position - * @param height to draw from the ybegin position - */ -void pp2d_draw_texture_part(size_t id, int x, int y, int xbegin, int ybegin, int width, int height); - -/** - * @brief Prints a wchar_t pointer - * @param x position to start drawing - * @param y position to start drawing - * @param scaleX multiplier for the text width - * @param scaleY multiplier for the text height - * @param color RGBA8 the text will be drawn - * @param text to be printed on the screen - */ -void pp2d_draw_wtext(float x, float y, float scaleX, float scaleY, u32 color, const wchar_t* text); - -/** - * @brief Prints a wchar_t pointer in the middle of the target screen - * @param target screen to draw the text to - * @param y position to start drawing - * @param scaleX multiplier for the text width - * @param scaleY multiplier for the text height - * @param color RGBA8 the text will be drawn - * @param text to be printed on the screen - */ -void pp2d_draw_wtext_center(gfxScreen_t target, float y, float scaleX, float scaleY, u32 color, const wchar_t* text); - -/** - * @brief Prints a wchar_t pointer - * @param x position to start drawing - * @param y position to start drawing - * @param scaleX multiplier for the text width - * @param scaleY multiplier for the text height - * @param color RGBA8 the text will be drawn - * @param wrapX wrap width - * @param text to be printed on the screen - */ -void pp2d_draw_wtext_wrap(float x, float y, float scaleX, float scaleY, u32 color, float wrapX, const wchar_t* text); - -/** - * @brief Prints a wchar_t pointer with arguments - * @param x position to start drawing - * @param y position to start drawing - * @param scaleX multiplier for the text width - * @param scaleY multiplier for the text height - * @param color RGBA8 the text will be drawn - * @param text to be printed on the screen - * @param ... arguments - */ -void pp2d_draw_wtextf(float x, float y, float scaleX, float scaleY, u32 color, const wchar_t* text, ...); - -/// Ends a frame -void pp2d_end_draw(void); - -/// Frees the pp2d environment -void pp2d_exit(void); - -/** - * @brief Inits the pp2d environment - * @return 0 if everything went correctly, otherwise returns Result code - * @note This will trigger gfxInitDefault by default - */ -Result pp2d_init(void); - -/** - * @brief Calculates a char pointer height - * @param text char pointer to calculate the height of - * @param scaleX multiplier for the text width - * @param scaleY multiplier for the text height - * @return height the text will have if rendered in the supplied conditions - */ -float pp2d_get_text_height(const char* text, float scaleX, float scaleY); - -/** - * @brief Calculates a char pointer height - * @param text char pointer to calculate the height of - * @param scaleX multiplier for the text width - * @param scaleY multiplier for the text height - * @param wrapX wrap width - * @return height the text will have if rendered in the supplied conditions - */ -float pp2d_get_text_height_wrap(const char* text, float scaleX, float scaleY, int wrapX); - -/** - * @brief Calculates width and height for a char pointer - * @param width pointer to the width to return - * @param height pointer to the height to return - * @param scaleX multiplier for the text width - * @param scaleY multiplier for the text height - * @param text to calculate dimensions of - */ -void pp2d_get_text_size(float* width, float* height, float scaleX, float scaleY, const char* text); - -/** - * @brief Calculates a char pointer width - * @param text char pointer to calculate the width of - * @param scaleX multiplier for the text width - * @param scaleY multiplier for the text height - * @return width the text will have if rendered in the supplied conditions - */ -float pp2d_get_text_width(const char* text, float scaleX, float scaleY); - -/** - * @brief Calculates a wchar_t pointer height - * @param text wchar_t pointer to calculate the height of - * @param scaleX multiplier for the text width - * @param scaleY multiplier for the text height - * @return height the text will have if rendered in the supplied conditions - */ -float pp2d_get_wtext_height(const wchar_t* text, float scaleX, float scaleY); - -/** - * @brief Calculates a wchar_t pointer width - * @param text wchar_t pointer to calculate the width of - * @param scaleX multiplier for the text width - * @param scaleY multiplier for the text height - * @return width the text will have if rendered in the supplied conditions - */ -float pp2d_get_wtext_width(const wchar_t* text, float scaleX, float scaleY); - -/** - * @brief Gets the width of a texture - * @param id of the texture - */ -float pp2d_get_texture_width(size_t id); - -/** - * @brief Gets the height of a texture - * @param id of the texture - */ -float pp2d_get_texture_height(size_t id); - -/** - * @brief Frees a texture - * @param id of the texture to free - */ -void pp2d_free_texture(size_t id); - -/** - * @brief Loads a texture from a a buffer in memory - * @param id of the texture - * @param buf buffer where the texture is stored - * @param width of the texture - * @param height of the texture - */ -void pp2d_load_texture_memory(size_t id, void* buf, u32 size, u32 width, u32 height, GPU_TEXCOLOR format); - -/** - * @brief Loads a texture from a BMP file - * @param id of the texture - * @param path where the BMP file is located - */ -void pp2d_load_texture_bmp(size_t id, const char* path); - -/** - * @brief Loads a texture from a JPEG file - * @param id of the texture - * @param path where the JPEG file is located - */ -void pp2d_load_texture_jpg(size_t id, const char* path); - -/** - * @brief Loads a texture from a PSD, GIF, HDR, PIC and TGA file - * @param id of the texture - * @param path where the image file is located - */ -void pp2d_load_texture_other(size_t id, const char* path); - -/** - * @brief Loads a texture from a png file - * @param id of the texture - * @param path where the png file is located - */ -void pp2d_load_texture_png(size_t id, const char* path); - -/** - * @brief Enables 3D service - * @param enable integer - */ -void pp2d_set_3D(int enable); - -/** - * @brief Sets a background color for the specified screen - * @param target GFX_TOP or GFX_BOTTOM - * @param color ABGR8 which will be the background one - */ -void pp2d_set_screen_color(gfxScreen_t target, u32 color); - -/** - * @brief Sets filters to load texture with - * @param magFilter GPU_NEAREST or GPU_LINEAR - * @param minFilter GPU_NEAREST or GPU_LINEAR - */ -void pp2d_set_texture_filter(GPU_TEXTURE_FILTER_PARAM magFilter, GPU_TEXTURE_FILTER_PARAM minFilter); - -/** - * @brief Inits a texture to be drawn - * @param id of the texture - * @param x to draw the texture at - * @param y to draw the texture at - */ -void pp2d_texture_select(size_t id, int x, int y); - -/** - * @brief Inits a portion of a texture to be drawn - * @param id of the texture - * @param x position on the screen to draw the texture - * @param y position on the screen to draw the texture - * @param xbegin position to start drawing - * @param ybegin position to start drawing - * @param width to draw from the xbegin position - * @param height to draw from the ybegin position - */ -void pp2d_texture_select_part(size_t id, int x, int y, int xbegin, int ybegin, int width, int height); - -/** - * @brief Modulates a texture with a color - * @param color to modulate the texture - */ -void pp2d_texture_blend(u32 color); - -/** - * @brief Scales a texture - * @param scaleX width scale factor - * @param scaleY height scale factor - */ -void pp2d_texture_scale(float scaleX, float scaleY); - -/** - * @brief Flips a texture - * @param fliptype HORIZONTAL, VERTICAL or BOTH - */ -void pp2d_texture_flip(flipType fliptype); - -/** - * @brief Rotates a texture - * @param angle in degrees to rotate the texture - */ -void pp2d_texture_rotate(float angle); - -/** - * @brief Sets the depth of a texture - * @param depth factor of the texture - */ -void pp2d_texture_depth(float depth); - -/// Renders a texture -void pp2d_texture_draw(void); - -#ifdef __cplusplus -} -#endif - -#endif /*PP2D_H*/ diff --git a/include/pp2d/stb_image.h b/include/pp2d/stb_image.h deleted file mode 100644 index d9c21bc..0000000 --- a/include/pp2d/stb_image.h +++ /dev/null @@ -1,7462 +0,0 @@ -/* stb_image - v2.19 - public domain image loader - http://nothings.org/stb - no warranty implied; use at your own risk - - Do this: - #define STB_IMAGE_IMPLEMENTATION - before you include this file in *one* C or C++ file to create the implementation. - - // i.e. it should look like this: - #include ... - #include ... - #include ... - #define STB_IMAGE_IMPLEMENTATION - #include "stb_image.h" - - You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. - And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free - - - QUICK NOTES: - Primarily of interest to game developers and other people who can - avoid problematic images and only need the trivial interface - - JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) - PNG 1/2/4/8/16-bit-per-channel - - TGA (not sure what subset, if a subset) - BMP non-1bpp, non-RLE - PSD (composited view only, no extra channels, 8/16 bit-per-channel) - - GIF (*comp always reports as 4-channel) - HDR (radiance rgbE format) - PIC (Softimage PIC) - PNM (PPM and PGM binary only) - - Animated GIF still needs a proper API, but here's one way to do it: - http://gist.github.com/urraka/685d9a6340b26b830d49 - - - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - - decode from arbitrary I/O callbacks - - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) - - Full documentation under "DOCUMENTATION" below. - - -LICENSE - - See end of file for license information. - -RECENT REVISION HISTORY: - - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings - 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes - 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 - RGB-format JPEG; remove white matting in PSD; - allocate large structures on the stack; - correct channel count for PNG & BMP - 2.10 (2016-01-22) avoid warning introduced in 2.09 - 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED - - See end of file for full revision history. - - - ============================ Contributors ========================= - - Image formats Extensions, features - Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) - Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) - Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) - Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) - Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) - Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) - Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) - github:urraka (animated gif) Junggon Kim (PNM comments) - Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) - socks-the-fox (16-bit PNG) - Jeremy Sawicki (handle all ImageNet JPGs) - Optimizations & bugfixes Mikhail Morozov (1-bit BMP) - Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) - Arseny Kapoulkine - John-Mark Allen - - Bug & warning fixes - Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan - Dave Moore Roy Eltham Hayaki Saito Nathan Reed - Won Chun Luke Graham Johan Duparc Nick Verigakis - the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh - Janez Zemva John Bartholomew Michal Cichon github:romigrou - Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar - Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex - Ryamond Barbiero Paul Du Bois Engin Manap github:grim210 - Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw - Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus - Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo - Christian Floisand Kevin Schmidt github:darealshinji - Blazej Dariusz Roszkowski github:Michaelangel007 -*/ - -#ifndef STBI_INCLUDE_STB_IMAGE_H -#define STBI_INCLUDE_STB_IMAGE_H - -// DOCUMENTATION -// -// Limitations: -// - no 12-bit-per-channel JPEG -// - no JPEGs with arithmetic coding -// - GIF always returns *comp=4 -// -// Basic usage (see HDR discussion below for HDR usage): -// int x,y,n; -// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); -// // ... process data if not NULL ... -// // ... x = width, y = height, n = # 8-bit components per pixel ... -// // ... replace '0' with '1'..'4' to force that many components per pixel -// // ... but 'n' will always be the number that it would have been if you said 0 -// stbi_image_free(data) -// -// Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *channels_in_file -- outputs # of image components in image file -// int desired_channels -- if non-zero, # of image components requested in result -// -// The return value from an image loader is an 'unsigned char *' which points -// to the pixel data, or NULL on an allocation failure or if the image is -// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, -// with each pixel consisting of N interleaved 8-bit components; the first -// pixel pointed to is top-left-most in the image. There is no padding between -// image scanlines or between pixels, regardless of format. The number of -// components N is 'desired_channels' if desired_channels is non-zero, or -// *channels_in_file otherwise. If desired_channels is non-zero, -// *channels_in_file has the number of components that _would_ have been -// output otherwise. E.g. if you set desired_channels to 4, you will always -// get RGBA output, but you can check *channels_in_file to see if it's trivially -// opaque because e.g. there were only 3 channels in the source image. -// -// An output image with N components has the following components interleaved -// in this order in each pixel: -// -// N=#comp components -// 1 grey -// 2 grey, alpha -// 3 red, green, blue -// 4 red, green, blue, alpha -// -// If image loading fails for any reason, the return value will be NULL, -// and *x, *y, *channels_in_file will be unchanged. The function -// stbi_failure_reason() can be queried for an extremely brief, end-user -// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS -// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly -// more user-friendly ones. -// -// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. -// -// =========================================================================== -// -// Philosophy -// -// stb libraries are designed with the following priorities: -// -// 1. easy to use -// 2. easy to maintain -// 3. good performance -// -// Sometimes I let "good performance" creep up in priority over "easy to maintain", -// and for best performance I may provide less-easy-to-use APIs that give higher -// performance, in addition to the easy to use ones. Nevertheless, it's important -// to keep in mind that from the standpoint of you, a client of this library, -// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. -// -// Some secondary priorities arise directly from the first two, some of which -// make more explicit reasons why performance can't be emphasized. -// -// - Portable ("ease of use") -// - Small source code footprint ("easy to maintain") -// - No dependencies ("ease of use") -// -// =========================================================================== -// -// I/O callbacks -// -// I/O callbacks allow you to read from arbitrary sources, like packaged -// files or some other source. Data read from callbacks are processed -// through a small internal buffer (currently 128 bytes) to try to reduce -// overhead. -// -// The three functions you must define are "read" (reads some bytes of data), -// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). -// -// =========================================================================== -// -// SIMD support -// -// The JPEG decoder will try to automatically use SIMD kernels on x86 when -// supported by the compiler. For ARM Neon support, you must explicitly -// request it. -// -// (The old do-it-yourself SIMD API is no longer supported in the current -// code.) -// -// On x86, SSE2 will automatically be used when available based on a run-time -// test; if not, the generic C versions are used as a fall-back. On ARM targets, -// the typical path is to have separate builds for NEON and non-NEON devices -// (at least this is true for iOS and Android). Therefore, the NEON support is -// toggled by a build flag: define STBI_NEON to get NEON loops. -// -// If for some reason you do not want to use any of SIMD code, or if -// you have issues compiling it, you can disable it entirely by -// defining STBI_NO_SIMD. -// -// =========================================================================== -// -// HDR image support (disable by defining STBI_NO_HDR) -// -// stb_image now supports loading HDR images in general, and currently -// the Radiance .HDR file format, although the support is provided -// generically. You can still load any file through the existing interface; -// if you attempt to load an HDR file, it will be automatically remapped to -// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; -// both of these constants can be reconfigured through this interface: -// -// stbi_hdr_to_ldr_gamma(2.2f); -// stbi_hdr_to_ldr_scale(1.0f); -// -// (note, do not use _inverse_ constants; stbi_image will invert them -// appropriately). -// -// Additionally, there is a new, parallel interface for loading files as -// (linear) floats to preserve the full dynamic range: -// -// float *data = stbi_loadf(filename, &x, &y, &n, 0); -// -// If you load LDR images through this interface, those images will -// be promoted to floating point values, run through the inverse of -// constants corresponding to the above: -// -// stbi_ldr_to_hdr_scale(1.0f); -// stbi_ldr_to_hdr_gamma(2.2f); -// -// Finally, given a filename (or an open file or memory block--see header -// file for details) containing image data, you can query for the "most -// appropriate" interface to use (that is, whether the image is HDR or -// not), using: -// -// stbi_is_hdr(char *filename); -// -// =========================================================================== -// -// iPhone PNG support: -// -// By default we convert iphone-formatted PNGs back to RGB, even though -// they are internally encoded differently. You can disable this conversion -// by by calling stbi_convert_iphone_png_to_rgb(0), in which case -// you will always just get the native iphone "format" through (which -// is BGR stored in RGB). -// -// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per -// pixel to remove any premultiplied alpha *only* if the image file explicitly -// says there's premultiplied data (currently only happens in iPhone images, -// and only if iPhone convert-to-rgb processing is on). -// -// =========================================================================== -// -// ADDITIONAL CONFIGURATION -// -// - You can suppress implementation of any of the decoders to reduce -// your code footprint by #defining one or more of the following -// symbols before creating the implementation. -// -// STBI_NO_JPEG -// STBI_NO_PNG -// STBI_NO_BMP -// STBI_NO_PSD -// STBI_NO_TGA -// STBI_NO_GIF -// STBI_NO_HDR -// STBI_NO_PIC -// STBI_NO_PNM (.ppm and .pgm) -// -// - You can request *only* certain decoders and suppress all other ones -// (this will be more forward-compatible, as addition of new decoders -// doesn't require you to disable them explicitly): -// -// STBI_ONLY_JPEG -// STBI_ONLY_PNG -// STBI_ONLY_BMP -// STBI_ONLY_PSD -// STBI_ONLY_TGA -// STBI_ONLY_GIF -// STBI_ONLY_HDR -// STBI_ONLY_PIC -// STBI_ONLY_PNM (.ppm and .pgm) -// -// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still -// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB -// - - -#ifndef STBI_NO_STDIO -#include -#endif // STBI_NO_STDIO - -#define STBI_VERSION 1 - -enum -{ - STBI_default = 0, // only used for desired_channels - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -typedef unsigned char stbi_uc; -typedef unsigned short stbi_us; - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef STB_IMAGE_STATIC -#define STBIDEF static -#else -#define STBIDEF extern -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// PRIMARY API - works on images of any type -// - -// -// load image by filename, open file, or memory buffer -// - -typedef struct -{ - int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read - void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative - int (*eof) (void *user); // returns nonzero if we are at end of file/data -} stbi_io_callbacks; - -//////////////////////////////////// -// -// 8-bits-per-channel interface -// - -STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -#endif - - -#ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -// for stbi_load_from_file, file pointer is left pointing immediately after image -#endif - -//////////////////////////////////// -// -// 16-bits-per-channel interface -// - -STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -#endif - -//////////////////////////////////// -// -// float-per-channel interface -// -#ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - - #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); - #endif -#endif - -#ifndef STBI_NO_HDR - STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); - STBIDEF void stbi_hdr_to_ldr_scale(float scale); -#endif // STBI_NO_HDR - -#ifndef STBI_NO_LINEAR - STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); - STBIDEF void stbi_ldr_to_hdr_scale(float scale); -#endif // STBI_NO_LINEAR - -// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename); -STBIDEF int stbi_is_hdr_from_file(FILE *f); -#endif // STBI_NO_STDIO - - -// get a VERY brief reason for failure -// NOT THREADSAFE -STBIDEF const char *stbi_failure_reason (void); - -// free the loaded image -- this is just free() -STBIDEF void stbi_image_free (void *retval_from_stbi_load); - -// get image dimensions & components without fully decoding -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit (char const *filename); -STBIDEF int stbi_is_16_bit_from_file(FILE *f); -#endif - - - -// for image formats that explicitly notate that they have premultiplied alpha, -// we just return the colors as stored in the file. set this flag to force -// unpremultiplication. results are undefined if the unpremultiply overflow. -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); - -// indicate whether we should process iphone images back to canonical format, -// or just pass them through "as-is" -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); - -// flip the image vertically, so the first pixel in the output array is the bottom left -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); - -// ZLIB client - used by PNG, available for other purposes - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); -STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - -STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - - -#ifdef __cplusplus -} -#endif - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // STBI_INCLUDE_STB_IMAGE_H - -#ifdef STB_IMAGE_IMPLEMENTATION - -#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ - || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ - || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ - || defined(STBI_ONLY_ZLIB) - #ifndef STBI_ONLY_JPEG - #define STBI_NO_JPEG - #endif - #ifndef STBI_ONLY_PNG - #define STBI_NO_PNG - #endif - #ifndef STBI_ONLY_BMP - #define STBI_NO_BMP - #endif - #ifndef STBI_ONLY_PSD - #define STBI_NO_PSD - #endif - #ifndef STBI_ONLY_TGA - #define STBI_NO_TGA - #endif - #ifndef STBI_ONLY_GIF - #define STBI_NO_GIF - #endif - #ifndef STBI_ONLY_HDR - #define STBI_NO_HDR - #endif - #ifndef STBI_ONLY_PIC - #define STBI_NO_PIC - #endif - #ifndef STBI_ONLY_PNM - #define STBI_NO_PNM - #endif -#endif - -#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) -#define STBI_NO_ZLIB -#endif - - -#include -#include // ptrdiff_t on osx -#include -#include -#include - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp, pow -#endif - -#ifndef STBI_NO_STDIO -#include -#endif - -#ifndef STBI_ASSERT -#include -#define STBI_ASSERT(x) assert(x) -#endif - - -#ifndef _MSC_VER - #ifdef __cplusplus - #define stbi_inline inline - #else - #define stbi_inline - #endif -#else - #define stbi_inline __forceinline -#endif - - -#ifdef _MSC_VER -typedef unsigned short stbi__uint16; -typedef signed short stbi__int16; -typedef unsigned int stbi__uint32; -typedef signed int stbi__int32; -#else -#include -typedef uint16_t stbi__uint16; -typedef int16_t stbi__int16; -typedef uint32_t stbi__uint32; -typedef int32_t stbi__int32; -#endif - -// should produce compiler error if size is wrong -typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) -#else -#define STBI_NOTUSED(v) (void)sizeof(v) -#endif - -#ifdef _MSC_VER -#define STBI_HAS_LROTL -#endif - -#ifdef STBI_HAS_LROTL - #define stbi_lrot(x,y) _lrotl(x,y) -#else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) -#endif - -#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) -// ok -#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) -// ok -#else -#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." -#endif - -#ifndef STBI_MALLOC -#define STBI_MALLOC(sz) malloc(sz) -#define STBI_REALLOC(p,newsz) realloc(p,newsz) -#define STBI_FREE(p) free(p) -#endif - -#ifndef STBI_REALLOC_SIZED -#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) -#endif - -// x86/x64 detection -#if defined(__x86_64__) || defined(_M_X64) -#define STBI__X64_TARGET -#elif defined(__i386) || defined(_M_IX86) -#define STBI__X86_TARGET -#endif - -#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) -// gcc doesn't support sse2 intrinsics unless you compile with -msse2, -// which in turn means it gets to use SSE2 everywhere. This is unfortunate, -// but previous attempts to provide the SSE2 functions with runtime -// detection caused numerous issues. The way architecture extensions are -// exposed in GCC/Clang is, sadly, not really suited for one-file libs. -// New behavior: if compiled with -msse2, we use SSE2 without any -// detection; if not, we don't use it at all. -#define STBI_NO_SIMD -#endif - -#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) -// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET -// -// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the -// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. -// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not -// simultaneously enabling "-mstackrealign". -// -// See https://github.com/nothings/stb/issues/81 for more information. -// -// So default to no SSE2 on 32-bit MinGW. If you've read this far and added -// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. -#define STBI_NO_SIMD -#endif - -#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) -#define STBI_SSE2 -#include - -#ifdef _MSC_VER - -#if _MSC_VER >= 1400 // not VC6 -#include // __cpuid -static int stbi__cpuid3(void) -{ - int info[4]; - __cpuid(info,1); - return info[3]; -} -#else -static int stbi__cpuid3(void) -{ - int res; - __asm { - mov eax,1 - cpuid - mov res,edx - } - return res; -} -#endif - -#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name - -static int stbi__sse2_available(void) -{ - int info3 = stbi__cpuid3(); - return ((info3 >> 26) & 1) != 0; -} -#else // assume GCC-style if not VC++ -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) - -static int stbi__sse2_available(void) -{ - // If we're even attempting to compile this on GCC/Clang, that means - // -msse2 is on, which means the compiler is allowed to use SSE2 - // instructions at will, and so are we. - return 1; -} -#endif -#endif - -// ARM NEON -#if defined(STBI_NO_SIMD) && defined(STBI_NEON) -#undef STBI_NEON -#endif - -#ifdef STBI_NEON -#include -// assume GCC or Clang on ARM targets -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) -#endif - -#ifndef STBI_SIMD_ALIGN -#define STBI_SIMD_ALIGN(type, name) type name -#endif - -/////////////////////////////////////////////// -// -// stbi__context struct and start_xxx functions - -// stbi__context structure is our basic context used by all images, so it -// contains all the IO context, plus some basic image information -typedef struct -{ - stbi__uint32 img_x, img_y; - int img_n, img_out_n; - - stbi_io_callbacks io; - void *io_user_data; - - int read_from_callbacks; - int buflen; - stbi_uc buffer_start[128]; - - stbi_uc *img_buffer, *img_buffer_end; - stbi_uc *img_buffer_original, *img_buffer_original_end; -} stbi__context; - - -static void stbi__refill_buffer(stbi__context *s); - -// initialize a memory-decode context -static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) -{ - s->io.read = NULL; - s->read_from_callbacks = 0; - s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; - s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; -} - -// initialize a callback-based context -static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) -{ - s->io = *c; - s->io_user_data = user; - s->buflen = sizeof(s->buffer_start); - s->read_from_callbacks = 1; - s->img_buffer_original = s->buffer_start; - stbi__refill_buffer(s); - s->img_buffer_original_end = s->img_buffer_end; -} - -#ifndef STBI_NO_STDIO - -static int stbi__stdio_read(void *user, char *data, int size) -{ - return (int) fread(data,1,size,(FILE*) user); -} - -static void stbi__stdio_skip(void *user, int n) -{ - fseek((FILE*) user, n, SEEK_CUR); -} - -static int stbi__stdio_eof(void *user) -{ - return feof((FILE*) user); -} - -static stbi_io_callbacks stbi__stdio_callbacks = -{ - stbi__stdio_read, - stbi__stdio_skip, - stbi__stdio_eof, -}; - -static void stbi__start_file(stbi__context *s, FILE *f) -{ - stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); -} - -//static void stop_file(stbi__context *s) { } - -#endif // !STBI_NO_STDIO - -static void stbi__rewind(stbi__context *s) -{ - // conceptually rewind SHOULD rewind to the beginning of the stream, - // but we just rewind to the beginning of the initial buffer, because - // we only use it after doing 'test', which only ever looks at at most 92 bytes - s->img_buffer = s->img_buffer_original; - s->img_buffer_end = s->img_buffer_original_end; -} - -enum -{ - STBI_ORDER_RGB, - STBI_ORDER_BGR -}; - -typedef struct -{ - int bits_per_channel; - int num_channels; - int channel_order; -} stbi__result_info; - -#ifndef STBI_NO_JPEG -static int stbi__jpeg_test(stbi__context *s); -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNG -static int stbi__png_test(stbi__context *s); -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__png_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_BMP -static int stbi__bmp_test(stbi__context *s); -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_TGA -static int stbi__tga_test(stbi__context *s); -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s); -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__psd_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_HDR -static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_test(stbi__context *s); -static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_GIF -static int stbi__gif_test(stbi__context *s); -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNM -static int stbi__pnm_test(stbi__context *s); -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -// this is not threadsafe -static const char *stbi__g_failure_reason; - -STBIDEF const char *stbi_failure_reason(void) -{ - return stbi__g_failure_reason; -} - -static int stbi__err(const char *str) -{ - stbi__g_failure_reason = str; - return 0; -} - -static void *stbi__malloc(size_t size) -{ - return STBI_MALLOC(size); -} - -// stb_image uses ints pervasively, including for offset calculations. -// therefore the largest decoded image size we can support with the -// current code, even on 64-bit targets, is INT_MAX. this is not a -// significant limitation for the intended use case. -// -// we do, however, need to make sure our size calculations don't -// overflow. hence a few helper functions for size calculations that -// multiply integers together, making sure that they're non-negative -// and no overflow occurs. - -// return 1 if the sum is valid, 0 on overflow. -// negative terms are considered invalid. -static int stbi__addsizes_valid(int a, int b) -{ - if (b < 0) return 0; - // now 0 <= b <= INT_MAX, hence also - // 0 <= INT_MAX - b <= INTMAX. - // And "a + b <= INT_MAX" (which might overflow) is the - // same as a <= INT_MAX - b (no overflow) - return a <= INT_MAX - b; -} - -// returns 1 if the product is valid, 0 on overflow. -// negative factors are considered invalid. -static int stbi__mul2sizes_valid(int a, int b) -{ - if (a < 0 || b < 0) return 0; - if (b == 0) return 1; // mul-by-0 is always safe - // portable way to check for no overflows in a*b - return a <= INT_MAX/b; -} - -// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow -static int stbi__mad2sizes_valid(int a, int b, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); -} - -// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow -static int stbi__mad3sizes_valid(int a, int b, int c, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__addsizes_valid(a*b*c, add); -} - -// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); -} -#endif - -// mallocs with size overflow checking -static void *stbi__malloc_mad2(int a, int b, int add) -{ - if (!stbi__mad2sizes_valid(a, b, add)) return NULL; - return stbi__malloc(a*b + add); -} - -static void *stbi__malloc_mad3(int a, int b, int c, int add) -{ - if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; - return stbi__malloc(a*b*c + add); -} - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) -{ - if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; - return stbi__malloc(a*b*c*d + add); -} -#endif - -// stbi__err - error -// stbi__errpf - error returning pointer to float -// stbi__errpuc - error returning pointer to unsigned char - -#ifdef STBI_NO_FAILURE_STRINGS - #define stbi__err(x,y) 0 -#elif defined(STBI_FAILURE_USERMSG) - #define stbi__err(x,y) stbi__err(y) -#else - #define stbi__err(x,y) stbi__err(x) -#endif - -#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) -#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) - -STBIDEF void stbi_image_free(void *retval_from_stbi_load) -{ - STBI_FREE(retval_from_stbi_load); -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); -#endif - -#ifndef STBI_NO_HDR -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); -#endif - -static int stbi__vertically_flip_on_load = 0; - -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load = flag_true_if_should_flip; -} - -static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields - ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed - ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order - ri->num_channels = 0; - - #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); - #endif - #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); - return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } - #endif - - #ifndef STBI_NO_TGA - // test tga last because it's a crappy test! - if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp, ri); - #endif - - return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); -} - -static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi_uc *reduced; - - reduced = (stbi_uc *) stbi__malloc(img_len); - if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling - - STBI_FREE(orig); - return reduced; -} - -static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi__uint16 *enlarged; - - enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); - if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff - - STBI_FREE(orig); - return enlarged; -} - -static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) -{ - int row; - size_t bytes_per_row = (size_t)w * bytes_per_pixel; - stbi_uc temp[2048]; - stbi_uc *bytes = (stbi_uc *)image; - - for (row = 0; row < (h>>1); row++) { - stbi_uc *row0 = bytes + row*bytes_per_row; - stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; - // swap row0 with row1 - size_t bytes_left = bytes_per_row; - while (bytes_left) { - size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); - memcpy(temp, row0, bytes_copy); - memcpy(row0, row1, bytes_copy); - memcpy(row1, temp, bytes_copy); - row0 += bytes_copy; - row1 += bytes_copy; - bytes_left -= bytes_copy; - } - } -} - -static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) -{ - int slice; - int slice_size = w * h * bytes_per_pixel; - - stbi_uc *bytes = (stbi_uc *)image; - for (slice = 0; slice < z; ++slice) { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; - } -} - -static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); - - if (result == NULL) - return NULL; - - if (ri.bits_per_channel != 8) { - STBI_ASSERT(ri.bits_per_channel == 16); - result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 8; - } - - // @TODO: move stbi__convert_format to here - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); - } - - return (unsigned char *) result; -} - -static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); - - if (result == NULL) - return NULL; - - if (ri.bits_per_channel != 16) { - STBI_ASSERT(ri.bits_per_channel == 8); - result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 16; - } - - // @TODO: move stbi__convert_format16 to here - // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); - } - - return (stbi__uint16 *) result; -} - -#if !defined(STBI_NO_HDR) || !defined(STBI_NO_LINEAR) -static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) -{ - if (stbi__vertically_flip_on_load && result != NULL) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); - } -} -#endif - -#ifndef STBI_NO_STDIO - -static FILE *stbi__fopen(char const *filename, char const *mode) -{ - FILE *f; -#if defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != fopen_s(&f, filename, mode)) - f=0; -#else - f = fopen(filename, mode); -#endif - return f; -} - - -STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - unsigned char *result; - if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__uint16 *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - stbi__uint16 *result; - if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file_16(f,x,y,comp,req_comp); - fclose(f); - return result; -} - - -#endif //!STBI_NO_STDIO - -STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_mem(&s,buffer,len); - - result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); - if (stbi__vertically_flip_on_load) { - stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); - } - - return result; -} -#endif - -#ifndef STBI_NO_LINEAR -static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - stbi__result_info ri; - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); - if (hdr_data) - stbi__float_postprocess(hdr_data,x,y,comp,req_comp); - return hdr_data; - } - #endif - data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); - if (data) - return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); -} - -STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_STDIO -STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - float *result; - FILE *f = stbi__fopen(filename, "rb"); - if (!f) return stbi__errpf("can't fopen", "Unable to open file"); - result = stbi_loadf_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_file(&s,f); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} -#endif // !STBI_NO_STDIO - -#endif // !STBI_NO_LINEAR - -// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is -// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always -// reports false! - -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(buffer); - STBI_NOTUSED(len); - return 0; - #endif -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result=0; - if (f) { - result = stbi_is_hdr_from_file(f); - fclose(f); - } - return result; -} - -STBIDEF int stbi_is_hdr_from_file(FILE *f) -{ - #ifndef STBI_NO_HDR - long pos = ftell(f); - int res; - stbi__context s; - stbi__start_file(&s,f); - res = stbi__hdr_test(&s); - fseek(f, pos, SEEK_SET); - return res; - #else - STBI_NOTUSED(f); - return 0; - #endif -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(clbk); - STBI_NOTUSED(user); - return 0; - #endif -} - -#ifndef STBI_NO_LINEAR -static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; - -STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } -STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } -#endif - -static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; - -STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } -STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } - - -////////////////////////////////////////////////////////////////////////////// -// -// Common code used by all image loaders -// - -enum -{ - STBI__SCAN_load=0, - STBI__SCAN_type, - STBI__SCAN_header -}; - -static void stbi__refill_buffer(stbi__context *s) -{ - int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); - if (n == 0) { - // at end of file, treat same as if from memory, but need to handle case - // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file - s->read_from_callbacks = 0; - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start+1; - *s->img_buffer = 0; - } else { - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start + n; - } -} - -stbi_inline static stbi_uc stbi__get8(stbi__context *s) -{ - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - if (s->read_from_callbacks) { - stbi__refill_buffer(s); - return *s->img_buffer++; - } - return 0; -} - -stbi_inline static int stbi__at_eof(stbi__context *s) -{ - if (s->io.read) { - if (!(s->io.eof)(s->io_user_data)) return 0; - // if feof() is true, check if buffer = end - // special case: we've only got the special 0 character at the end - if (s->read_from_callbacks == 0) return 1; - } - - return s->img_buffer >= s->img_buffer_end; -} - -static void stbi__skip(stbi__context *s, int n) -{ - if (n < 0) { - s->img_buffer = s->img_buffer_end; - return; - } - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - s->img_buffer = s->img_buffer_end; - (s->io.skip)(s->io_user_data, n - blen); - return; - } - } - s->img_buffer += n; -} - -static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) -{ - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - int res, count; - - memcpy(buffer, s->img_buffer, blen); - - count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); - res = (count == (n-blen)); - s->img_buffer = s->img_buffer_end; - return res; - } - } - - if (s->img_buffer+n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} - -static int stbi__get16be(stbi__context *s) -{ - int z = stbi__get8(s); - return (z << 8) + stbi__get8(s); -} - -static stbi__uint32 stbi__get32be(stbi__context *s) -{ - stbi__uint32 z = stbi__get16be(s); - return (z << 16) + stbi__get16be(s); -} - -#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) -// nothing -#else -static int stbi__get16le(stbi__context *s) -{ - int z = stbi__get8(s); - return z + (stbi__get8(s) << 8); -} -#endif - -#ifndef STBI_NO_BMP -static stbi__uint32 stbi__get32le(stbi__context *s) -{ - stbi__uint32 z = stbi__get16le(s); - return z + (stbi__get16le(s) << 16); -} -#endif - -#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - - -////////////////////////////////////////////////////////////////////////////// -// -// generic converter from built-in img_n to req_comp -// individual types do this automatically as much as possible (e.g. jpeg -// does all cases internally since it needs to colorspace convert anyway, -// and it never has alpha, so very few cases ). png can automatically -// interleave an alpha=255 channel, but falls back to this for other cases -// -// assume data buffer is malloced, so malloc a new one and free that one -// only failure mode is malloc failing - -static stbi_uc stbi__compute_y(int r, int g, int b) -{ - return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); -} - -static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - unsigned char *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); - if (good == NULL) { - STBI_FREE(data); - return stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - unsigned char *src = data + j * x * img_n ; - unsigned char *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; - default: STBI_ASSERT(0); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} - -static stbi__uint16 stbi__compute_y_16(int r, int g, int b) -{ - return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); -} - -static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - stbi__uint16 *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); - if (good == NULL) { - STBI_FREE(data); - return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - stbi__uint16 *src = data + j * x * img_n ; - stbi__uint16 *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; - default: STBI_ASSERT(0); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) -{ - int i,k,n; - float *output; - if (!data) return NULL; - output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); - } - if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; - } - STBI_FREE(data); - return output; -} -#endif - -#ifndef STBI_NO_HDR -#define stbi__float2int(x) ((int) (x)) -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) -{ - int i,k,n; - stbi_uc *output; - if (!data) return NULL; - output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - if (k < comp) { - float z = data[i*comp+k] * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - } - STBI_FREE(data); - return output; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// "baseline" JPEG/JFIF decoder -// -// simple implementation -// - doesn't support delayed output of y-dimension -// - simple interface (only one output format: 8-bit interleaved RGB) -// - doesn't try to recover corrupt jpegs -// - doesn't allow partial loading, loading multiple at once -// - still fast on x86 (copying globals into locals doesn't help x86) -// - allocates lots of intermediate memory (full size of all components) -// - non-interleaved case requires this anyway -// - allows good upsampling (see next) -// high-quality -// - upsampled channels are bilinearly interpolated, even across blocks -// - quality integer IDCT derived from IJG's 'slow' -// performance -// - fast huffman; reasonable integer IDCT -// - some SIMD kernels for common paths on targets with SSE2/NEON -// - uses a lot of intermediate memory, could cache poorly - -#ifndef STBI_NO_JPEG - -// huffman decoding acceleration -#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache - -typedef struct -{ - stbi_uc fast[1 << FAST_BITS]; - // weirdly, repacking this into AoS is a 10% speed loss, instead of a win - stbi__uint16 code[256]; - stbi_uc values[256]; - stbi_uc size[257]; - unsigned int maxcode[18]; - int delta[17]; // old 'firstsymbol' - old 'firstcode' -} stbi__huffman; - -typedef struct -{ - stbi__context *s; - stbi__huffman huff_dc[4]; - stbi__huffman huff_ac[4]; - stbi__uint16 dequant[4][64]; - stbi__int16 fast_ac[4][1 << FAST_BITS]; - -// sizes for components, interleaved MCUs - int img_h_max, img_v_max; - int img_mcu_x, img_mcu_y; - int img_mcu_w, img_mcu_h; - -// definition of jpeg image component - struct - { - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; - - int x,y,w2,h2; - stbi_uc *data; - void *raw_data, *raw_coeff; - stbi_uc *linebuf; - short *coeff; // progressive only - int coeff_w, coeff_h; // number of 8x8 coefficient blocks - } img_comp[4]; - - stbi__uint32 code_buffer; // jpeg entropy-coded buffer - int code_bits; // number of valid bits - unsigned char marker; // marker seen while filling entropy buffer - int nomore; // flag if we saw a marker so must stop - - int progressive; - int spec_start; - int spec_end; - int succ_high; - int succ_low; - int eob_run; - int jfif; - int app14_color_transform; // Adobe APP14 tag - int rgb; - - int scan_n, order[4]; - int restart_interval, todo; - -// kernels - void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); - void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); - stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); -} stbi__jpeg; - -static int stbi__build_huffman(stbi__huffman *h, int *count) -{ - int i,j,k=0; - unsigned int code; - // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) - h->size[k++] = (stbi_uc) (i+1); - h->size[k] = 0; - - // compute actual symbols (from jpeg spec) - code = 0; - k = 0; - for(j=1; j <= 16; ++j) { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) { - while (h->size[k] == j) - h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16-j); - code <<= 1; - } - h->maxcode[j] = 0xffffffff; - - // build non-spec acceleration table; 255 is flag for not-accelerated - memset(h->fast, 255, 1 << FAST_BITS); - for (i=0; i < k; ++i) { - int s = h->size[i]; - if (s <= FAST_BITS) { - int c = h->code[i] << (FAST_BITS-s); - int m = 1 << (FAST_BITS-s); - for (j=0; j < m; ++j) { - h->fast[c+j] = (stbi_uc) i; - } - } - } - return 1; -} - -// build a table that decodes both magnitude and value of small ACs in -// one go. -static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) -{ - int i; - for (i=0; i < (1 << FAST_BITS); ++i) { - stbi_uc fast = h->fast[i]; - fast_ac[i] = 0; - if (fast < 255) { - int rs = h->values[fast]; - int run = (rs >> 4) & 15; - int magbits = rs & 15; - int len = h->size[fast]; - - if (magbits && len + magbits <= FAST_BITS) { - // magnitude code followed by receive_extend code - int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); - int m = 1 << (magbits - 1); - if (k < m) k += (~0U << magbits) + 1; - // if the result is small enough, we can fit it in fast_ac table - if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); - } - } - } -} - -static void stbi__grow_buffer_unsafe(stbi__jpeg *j) -{ - do { - unsigned int b = j->nomore ? 0 : stbi__get8(j->s); - if (b == 0xff) { - int c = stbi__get8(j->s); - while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes - if (c != 0) { - j->marker = (unsigned char) c; - j->nomore = 1; - return; - } - } - j->code_buffer |= b << (24 - j->code_bits); - j->code_bits += 8; - } while (j->code_bits <= 24); -} - -// (1 << n) - 1 -static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; - -// decode a jpeg huffman value from the bitstream -stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) -{ - unsigned int temp; - int c,k; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - // look at the top FAST_BITS and determine what symbol ID it is, - // if the code is <= FAST_BITS - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - k = h->fast[c]; - if (k < 255) { - int s = h->size[k]; - if (s > j->code_bits) - return -1; - j->code_buffer <<= s; - j->code_bits -= s; - return h->values[k]; - } - - // naive test is to shift the code_buffer down so k bits are - // valid, then test against maxcode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - temp = j->code_buffer >> 16; - for (k=FAST_BITS+1 ; ; ++k) - if (temp < h->maxcode[k]) - break; - if (k == 17) { - // error! code not found - j->code_bits -= 16; - return -1; - } - - if (k > j->code_bits) - return -1; - - // convert the huffman code to the symbol id - c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; - STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); - - // convert the id to a symbol - j->code_bits -= k; - j->code_buffer <<= k; - return h->values[c]; -} - -// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); - - sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB - k = stbi_lrot(j->code_buffer, n); - STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k + (stbi__jbias[n] & ~sgn); -} - -// get some unsigned bits -stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) -{ - unsigned int k; - if (j->code_bits < n) stbi__grow_buffer_unsafe(j); - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k; -} - -stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) -{ - unsigned int k; - if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); - k = j->code_buffer; - j->code_buffer <<= 1; - --j->code_bits; - return k & 0x80000000; -} - -// given a value that's at position X in the zigzag stream, -// where does it appear in the 8x8 matrix coded as row-major? -static const stbi_uc stbi__jpeg_dezigzag[64+15] = -{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - // let corrupt input sample past end - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63 -}; - -// decode one 64-entry block-- -static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) -{ - int diff,dc,k; - int t; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - - // 0 all the ac values now so we can do it 32-bits at a time - memset(data,0,64*sizeof(data[0])); - - diff = t ? stbi__extend_receive(j, t) : 0; - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc * dequant[0]); - - // decode AC components, see JPEG spec - k = 1; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) * dequant[zig]); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (rs != 0xf0) break; // end block - k += 16; - } else { - k += r; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); - } - } - } while (k < 64); - return 1; -} - -static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) -{ - int diff,dc; - int t; - if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - if (j->succ_high == 0) { - // first scan for DC coefficient, must be first - memset(data,0,64*sizeof(data[0])); // 0 all the ac values now - t = stbi__jpeg_huff_decode(j, hdc); - diff = t ? stbi__extend_receive(j, t) : 0; - - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc << j->succ_low); - } else { - // refinement scan for DC coefficient - if (stbi__jpeg_get_bit(j)) - data[0] += (short) (1 << j->succ_low); - } - return 1; -} - -// @OPTIMIZE: store non-zigzagged during the decode passes, -// and only de-zigzag when dequantizing -static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) -{ - int k; - if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->succ_high == 0) { - int shift = j->succ_low; - - if (j->eob_run) { - --j->eob_run; - return 1; - } - - k = j->spec_start; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) << shift); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r); - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - --j->eob_run; - break; - } - k += 16; - } else { - k += r; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) << shift); - } - } - } while (k <= j->spec_end); - } else { - // refinement scan for these AC coefficients - - short bit = (short) (1 << j->succ_low); - - if (j->eob_run) { - --j->eob_run; - for (k = j->spec_start; k <= j->spec_end; ++k) { - short *p = &data[stbi__jpeg_dezigzag[k]]; - if (*p != 0) - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } - } else { - k = j->spec_start; - do { - int r,s; - int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r) - 1; - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - r = 64; // force end of block - } else { - // r=15 s=0 should write 16 0s, so we just do - // a run of 15 0s and then write s (which is 0), - // so we don't have to do anything special here - } - } else { - if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); - // sign bit - if (stbi__jpeg_get_bit(j)) - s = bit; - else - s = -bit; - } - - // advance by r - while (k <= j->spec_end) { - short *p = &data[stbi__jpeg_dezigzag[k++]]; - if (*p != 0) { - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } else { - if (r == 0) { - *p = (short) s; - break; - } - --r; - } - } - } while (k <= j->spec_end); - } - } - return 1; -} - -// take a -128..127 value and stbi__clamp it and convert to 0..255 -stbi_inline static stbi_uc stbi__clamp(int x) -{ - // trick to use a single test to catch both cases - if ((unsigned int) x > 255) { - if (x < 0) return 0; - if (x > 255) return 255; - } - return (stbi_uc) x; -} - -#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) -#define stbi__fsh(x) ((x) * 4096) - -// derived from jidctint -- DCT_ISLOW -#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ - int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ - p2 = s2; \ - p3 = s6; \ - p1 = (p2+p3) * stbi__f2f(0.5411961f); \ - t2 = p1 + p3*stbi__f2f(-1.847759065f); \ - t3 = p1 + p2*stbi__f2f( 0.765366865f); \ - p2 = s0; \ - p3 = s4; \ - t0 = stbi__fsh(p2+p3); \ - t1 = stbi__fsh(p2-p3); \ - x0 = t0+t3; \ - x3 = t0-t3; \ - x1 = t1+t2; \ - x2 = t1-t2; \ - t0 = s7; \ - t1 = s5; \ - t2 = s3; \ - t3 = s1; \ - p3 = t0+t2; \ - p4 = t1+t3; \ - p1 = t0+t3; \ - p2 = t1+t2; \ - p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ - t0 = t0*stbi__f2f( 0.298631336f); \ - t1 = t1*stbi__f2f( 2.053119869f); \ - t2 = t2*stbi__f2f( 3.072711026f); \ - t3 = t3*stbi__f2f( 1.501321110f); \ - p1 = p5 + p1*stbi__f2f(-0.899976223f); \ - p2 = p5 + p2*stbi__f2f(-2.562915447f); \ - p3 = p3*stbi__f2f(-1.961570560f); \ - p4 = p4*stbi__f2f(-0.390180644f); \ - t3 += p1+p4; \ - t2 += p2+p3; \ - t1 += p2+p4; \ - t0 += p1+p3; - -static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) -{ - int i,val[64],*v=val; - stbi_uc *o; - short *d = data; - - // columns - for (i=0; i < 8; ++i,++d, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0]*4; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } - } - - for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - // so we want to round that, which means adding 0.5 * 1<<17, - // aka 65536. Also, we'll end up with -128 to 127 that we want - // to encode as 0..255 by adding 128, so we'll add that before the shift - x0 += 65536 + (128<<17); - x1 += 65536 + (128<<17); - x2 += 65536 + (128<<17); - x3 += 65536 + (128<<17); - // tried computing the shifts into temps, or'ing the temps to see - // if any were out of range, but that was slower - o[0] = stbi__clamp((x0+t3) >> 17); - o[7] = stbi__clamp((x0-t3) >> 17); - o[1] = stbi__clamp((x1+t2) >> 17); - o[6] = stbi__clamp((x1-t2) >> 17); - o[2] = stbi__clamp((x2+t1) >> 17); - o[5] = stbi__clamp((x2-t1) >> 17); - o[3] = stbi__clamp((x3+t0) >> 17); - o[4] = stbi__clamp((x3-t0) >> 17); - } -} - -#ifdef STBI_SSE2 -// sse2 integer IDCT. not the fastest possible implementation but it -// produces bit-identical results to the generic C version so it's -// fully "transparent". -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - // This is constructed to match our regular (generic) integer IDCT exactly. - __m128i row0, row1, row2, row3, row4, row5, row6, row7; - __m128i tmp; - - // dot product constant: even elems=x, odd elems=y - #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) - - // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) - // out(1) = c1[even]*x + c1[odd]*y - #define dct_rot(out0,out1, x,y,c0,c1) \ - __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ - __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ - __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ - __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ - __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ - __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) - - // out = in << 12 (in 16-bit, out 32-bit) - #define dct_widen(out, in) \ - __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ - __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) - - // wide add - #define dct_wadd(out, a, b) \ - __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_add_epi32(a##_h, b##_h) - - // wide sub - #define dct_wsub(out, a, b) \ - __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) - - // butterfly a/b, add bias, then shift by "s" and pack - #define dct_bfly32o(out0, out1, a,b,bias,s) \ - { \ - __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ - __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ - dct_wadd(sum, abiased, b); \ - dct_wsub(dif, abiased, b); \ - out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ - out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ - } - - // 8-bit interleave step (for transposes) - #define dct_interleave8(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi8(a, b); \ - b = _mm_unpackhi_epi8(tmp, b) - - // 16-bit interleave step (for transposes) - #define dct_interleave16(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi16(a, b); \ - b = _mm_unpackhi_epi16(tmp, b) - - #define dct_pass(bias,shift) \ - { \ - /* even part */ \ - dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ - __m128i sum04 = _mm_add_epi16(row0, row4); \ - __m128i dif04 = _mm_sub_epi16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ - dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ - __m128i sum17 = _mm_add_epi16(row1, row7); \ - __m128i sum35 = _mm_add_epi16(row3, row5); \ - dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ - dct_wadd(x4, y0o, y4o); \ - dct_wadd(x5, y1o, y5o); \ - dct_wadd(x6, y2o, y5o); \ - dct_wadd(x7, y3o, y4o); \ - dct_bfly32o(row0,row7, x0,x7,bias,shift); \ - dct_bfly32o(row1,row6, x1,x6,bias,shift); \ - dct_bfly32o(row2,row5, x2,x5,bias,shift); \ - dct_bfly32o(row3,row4, x3,x4,bias,shift); \ - } - - __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); - __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); - __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); - __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); - __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); - __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); - __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); - __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); - - // rounding biases in column/row passes, see stbi__idct_block for explanation. - __m128i bias_0 = _mm_set1_epi32(512); - __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); - - // load - row0 = _mm_load_si128((const __m128i *) (data + 0*8)); - row1 = _mm_load_si128((const __m128i *) (data + 1*8)); - row2 = _mm_load_si128((const __m128i *) (data + 2*8)); - row3 = _mm_load_si128((const __m128i *) (data + 3*8)); - row4 = _mm_load_si128((const __m128i *) (data + 4*8)); - row5 = _mm_load_si128((const __m128i *) (data + 5*8)); - row6 = _mm_load_si128((const __m128i *) (data + 6*8)); - row7 = _mm_load_si128((const __m128i *) (data + 7*8)); - - // column pass - dct_pass(bias_0, 10); - - { - // 16bit 8x8 transpose pass 1 - dct_interleave16(row0, row4); - dct_interleave16(row1, row5); - dct_interleave16(row2, row6); - dct_interleave16(row3, row7); - - // transpose pass 2 - dct_interleave16(row0, row2); - dct_interleave16(row1, row3); - dct_interleave16(row4, row6); - dct_interleave16(row5, row7); - - // transpose pass 3 - dct_interleave16(row0, row1); - dct_interleave16(row2, row3); - dct_interleave16(row4, row5); - dct_interleave16(row6, row7); - } - - // row pass - dct_pass(bias_1, 17); - - { - // pack - __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 - __m128i p1 = _mm_packus_epi16(row2, row3); - __m128i p2 = _mm_packus_epi16(row4, row5); - __m128i p3 = _mm_packus_epi16(row6, row7); - - // 8bit 8x8 transpose pass 1 - dct_interleave8(p0, p2); // a0e0a1e1... - dct_interleave8(p1, p3); // c0g0c1g1... - - // transpose pass 2 - dct_interleave8(p0, p1); // a0c0e0g0... - dct_interleave8(p2, p3); // b0d0f0h0... - - // transpose pass 3 - dct_interleave8(p0, p2); // a0b0c0d0... - dct_interleave8(p1, p3); // a4b4c4d4... - - // store - _mm_storel_epi64((__m128i *) out, p0); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p2); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p1); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p3); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); - } - -#undef dct_const -#undef dct_rot -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_interleave8 -#undef dct_interleave16 -#undef dct_pass -} - -#endif // STBI_SSE2 - -#ifdef STBI_NEON - -// NEON integer IDCT. should produce bit-identical -// results to the generic C version. -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; - - int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); - int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); - int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); - int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); - int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); - int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); - int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); - int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); - int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); - int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); - int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); - int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); - -#define dct_long_mul(out, inq, coeff) \ - int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) - -#define dct_long_mac(out, acc, inq, coeff) \ - int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) - -#define dct_widen(out, inq) \ - int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ - int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) - -// wide add -#define dct_wadd(out, a, b) \ - int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vaddq_s32(a##_h, b##_h) - -// wide sub -#define dct_wsub(out, a, b) \ - int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vsubq_s32(a##_h, b##_h) - -// butterfly a/b, then shift using "shiftop" by "s" and pack -#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ - { \ - dct_wadd(sum, a, b); \ - dct_wsub(dif, a, b); \ - out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ - out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ - } - -#define dct_pass(shiftop, shift) \ - { \ - /* even part */ \ - int16x8_t sum26 = vaddq_s16(row2, row6); \ - dct_long_mul(p1e, sum26, rot0_0); \ - dct_long_mac(t2e, p1e, row6, rot0_1); \ - dct_long_mac(t3e, p1e, row2, rot0_2); \ - int16x8_t sum04 = vaddq_s16(row0, row4); \ - int16x8_t dif04 = vsubq_s16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - int16x8_t sum15 = vaddq_s16(row1, row5); \ - int16x8_t sum17 = vaddq_s16(row1, row7); \ - int16x8_t sum35 = vaddq_s16(row3, row5); \ - int16x8_t sum37 = vaddq_s16(row3, row7); \ - int16x8_t sumodd = vaddq_s16(sum17, sum35); \ - dct_long_mul(p5o, sumodd, rot1_0); \ - dct_long_mac(p1o, p5o, sum17, rot1_1); \ - dct_long_mac(p2o, p5o, sum35, rot1_2); \ - dct_long_mul(p3o, sum37, rot2_0); \ - dct_long_mul(p4o, sum15, rot2_1); \ - dct_wadd(sump13o, p1o, p3o); \ - dct_wadd(sump24o, p2o, p4o); \ - dct_wadd(sump23o, p2o, p3o); \ - dct_wadd(sump14o, p1o, p4o); \ - dct_long_mac(x4, sump13o, row7, rot3_0); \ - dct_long_mac(x5, sump24o, row5, rot3_1); \ - dct_long_mac(x6, sump23o, row3, rot3_2); \ - dct_long_mac(x7, sump14o, row1, rot3_3); \ - dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ - dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ - dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ - dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ - } - - // load - row0 = vld1q_s16(data + 0*8); - row1 = vld1q_s16(data + 1*8); - row2 = vld1q_s16(data + 2*8); - row3 = vld1q_s16(data + 3*8); - row4 = vld1q_s16(data + 4*8); - row5 = vld1q_s16(data + 5*8); - row6 = vld1q_s16(data + 6*8); - row7 = vld1q_s16(data + 7*8); - - // add DC bias - row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); - - // column pass - dct_pass(vrshrn_n_s32, 10); - - // 16bit 8x8 transpose - { -// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. -// whether compilers actually get this is another story, sadly. -#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } -#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } - - // pass 1 - dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 - dct_trn16(row2, row3); - dct_trn16(row4, row5); - dct_trn16(row6, row7); - - // pass 2 - dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 - dct_trn32(row1, row3); - dct_trn32(row4, row6); - dct_trn32(row5, row7); - - // pass 3 - dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 - dct_trn64(row1, row5); - dct_trn64(row2, row6); - dct_trn64(row3, row7); - -#undef dct_trn16 -#undef dct_trn32 -#undef dct_trn64 - } - - // row pass - // vrshrn_n_s32 only supports shifts up to 16, we need - // 17. so do a non-rounding shift of 16 first then follow - // up with a rounding shift by 1. - dct_pass(vshrn_n_s32, 16); - - { - // pack and round - uint8x8_t p0 = vqrshrun_n_s16(row0, 1); - uint8x8_t p1 = vqrshrun_n_s16(row1, 1); - uint8x8_t p2 = vqrshrun_n_s16(row2, 1); - uint8x8_t p3 = vqrshrun_n_s16(row3, 1); - uint8x8_t p4 = vqrshrun_n_s16(row4, 1); - uint8x8_t p5 = vqrshrun_n_s16(row5, 1); - uint8x8_t p6 = vqrshrun_n_s16(row6, 1); - uint8x8_t p7 = vqrshrun_n_s16(row7, 1); - - // again, these can translate into one instruction, but often don't. -#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } -#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } - - // sadly can't use interleaved stores here since we only write - // 8 bytes to each scan line! - - // 8x8 8-bit transpose pass 1 - dct_trn8_8(p0, p1); - dct_trn8_8(p2, p3); - dct_trn8_8(p4, p5); - dct_trn8_8(p6, p7); - - // pass 2 - dct_trn8_16(p0, p2); - dct_trn8_16(p1, p3); - dct_trn8_16(p4, p6); - dct_trn8_16(p5, p7); - - // pass 3 - dct_trn8_32(p0, p4); - dct_trn8_32(p1, p5); - dct_trn8_32(p2, p6); - dct_trn8_32(p3, p7); - - // store - vst1_u8(out, p0); out += out_stride; - vst1_u8(out, p1); out += out_stride; - vst1_u8(out, p2); out += out_stride; - vst1_u8(out, p3); out += out_stride; - vst1_u8(out, p4); out += out_stride; - vst1_u8(out, p5); out += out_stride; - vst1_u8(out, p6); out += out_stride; - vst1_u8(out, p7); - -#undef dct_trn8_8 -#undef dct_trn8_16 -#undef dct_trn8_32 - } - -#undef dct_long_mul -#undef dct_long_mac -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_pass -} - -#endif // STBI_NEON - -#define STBI__MARKER_none 0xff -// if there's a pending marker from the entropy stream, return that -// otherwise, fetch from the stream and get a marker. if there's no -// marker, return 0xff, which is never a valid marker value -static stbi_uc stbi__get_marker(stbi__jpeg *j) -{ - stbi_uc x; - if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } - x = stbi__get8(j->s); - if (x != 0xff) return STBI__MARKER_none; - while (x == 0xff) - x = stbi__get8(j->s); // consume repeated 0xff fill bytes - return x; -} - -// in each scan, we'll have scan_n components, and the order -// of the components is specified by order[] -#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) - -// after a restart interval, stbi__jpeg_reset the entropy decoder and -// the dc prediction -static void stbi__jpeg_reset(stbi__jpeg *j) -{ - j->code_bits = 0; - j->code_buffer = 0; - j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; - j->marker = STBI__MARKER_none; - j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; - j->eob_run = 0; - // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, - // since we don't even allow 1<<30 pixels -} - -static int stbi__parse_entropy_coded_data(stbi__jpeg *z) -{ - stbi__jpeg_reset(z); - if (!z->progressive) { - if (z->scan_n == 1) { - int i,j; - STBI_SIMD_ALIGN(short, data[64]); - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - STBI_SIMD_ALIGN(short, data[64]); - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x)*8; - int y2 = (j*z->img_comp[n].v + y)*8; - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } else { - if (z->scan_n == 1) { - int i,j; - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - if (z->spec_start == 0) { - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } else { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) - return 0; - } - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x); - int y2 = (j*z->img_comp[n].v + y); - short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } -} - -static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) -{ - int i; - for (i=0; i < 64; ++i) - data[i] *= dequant[i]; -} - -static void stbi__jpeg_finish(stbi__jpeg *z) -{ - if (z->progressive) { - // dequantize and idct the data - int i,j,n; - for (n=0; n < z->s->img_n; ++n) { - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - } - } - } - } -} - -static int stbi__process_marker(stbi__jpeg *z, int m) -{ - int L; - switch (m) { - case STBI__MARKER_none: // no marker found - return stbi__err("expected marker","Corrupt JPEG"); - - case 0xDD: // DRI - specify restart interval - if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); - z->restart_interval = stbi__get16be(z->s); - return 1; - - case 0xDB: // DQT - define quantization table - L = stbi__get16be(z->s)-2; - while (L > 0) { - int q = stbi__get8(z->s); - int p = q >> 4, sixteen = (p != 0); - int t = q & 15,i; - if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); - if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); - - for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); - L -= (sixteen ? 129 : 65); - } - return L==0; - - case 0xC4: // DHT - define huffman table - L = stbi__get16be(z->s)-2; - while (L > 0) { - stbi_uc *v; - int sizes[16],i,n=0; - int q = stbi__get8(z->s); - int tc = q >> 4; - int th = q & 15; - if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); - for (i=0; i < 16; ++i) { - sizes[i] = stbi__get8(z->s); - n += sizes[i]; - } - L -= 17; - if (tc == 0) { - if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; - v = z->huff_dc[th].values; - } else { - if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; - v = z->huff_ac[th].values; - } - for (i=0; i < n; ++i) - v[i] = stbi__get8(z->s); - if (tc != 0) - stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); - L -= n; - } - return L==0; - } - - // check for comment block or APP blocks - if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - L = stbi__get16be(z->s); - if (L < 2) { - if (m == 0xFE) - return stbi__err("bad COM len","Corrupt JPEG"); - else - return stbi__err("bad APP len","Corrupt JPEG"); - } - L -= 2; - - if (m == 0xE0 && L >= 5) { // JFIF APP0 segment - static const unsigned char tag[5] = {'J','F','I','F','\0'}; - int ok = 1; - int i; - for (i=0; i < 5; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 5; - if (ok) - z->jfif = 1; - } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment - static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; - int ok = 1; - int i; - for (i=0; i < 6; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 6; - if (ok) { - stbi__get8(z->s); // version - stbi__get16be(z->s); // flags0 - stbi__get16be(z->s); // flags1 - z->app14_color_transform = stbi__get8(z->s); // color transform - L -= 6; - } - } - - stbi__skip(z->s, L); - return 1; - } - - return stbi__err("unknown marker","Corrupt JPEG"); -} - -// after we see SOS -static int stbi__process_scan_header(stbi__jpeg *z) -{ - int i; - int Ls = stbi__get16be(z->s); - z->scan_n = stbi__get8(z->s); - if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); - if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); - for (i=0; i < z->scan_n; ++i) { - int id = stbi__get8(z->s), which; - int q = stbi__get8(z->s); - for (which = 0; which < z->s->img_n; ++which) - if (z->img_comp[which].id == id) - break; - if (which == z->s->img_n) return 0; // no match - z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); - z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); - z->order[i] = which; - } - - { - int aa; - z->spec_start = stbi__get8(z->s); - z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 - aa = stbi__get8(z->s); - z->succ_high = (aa >> 4); - z->succ_low = (aa & 15); - if (z->progressive) { - if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) - return stbi__err("bad SOS", "Corrupt JPEG"); - } else { - if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); - if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); - z->spec_end = 63; - } - } - - return 1; -} - -static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) -{ - int i; - for (i=0; i < ncomp; ++i) { - if (z->img_comp[i].raw_data) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].raw_data = NULL; - z->img_comp[i].data = NULL; - } - if (z->img_comp[i].raw_coeff) { - STBI_FREE(z->img_comp[i].raw_coeff); - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].coeff = 0; - } - if (z->img_comp[i].linebuf) { - STBI_FREE(z->img_comp[i].linebuf); - z->img_comp[i].linebuf = NULL; - } - } - return why; -} - -static int stbi__process_frame_header(stbi__jpeg *z, int scan) -{ - stbi__context *s = z->s; - int Lf,p,i,q, h_max=1,v_max=1,c; - Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG - p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline - s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires - c = stbi__get8(s); - if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); - s->img_n = c; - for (i=0; i < c; ++i) { - z->img_comp[i].data = NULL; - z->img_comp[i].linebuf = NULL; - } - - if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); - - z->rgb = 0; - for (i=0; i < s->img_n; ++i) { - static const unsigned char rgb[3] = { 'R', 'G', 'B' }; - z->img_comp[i].id = stbi__get8(s); - if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) - ++z->rgb; - q = stbi__get8(s); - z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); - z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); - z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); - } - - if (scan != STBI__SCAN_load) return 1; - - if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); - - for (i=0; i < s->img_n; ++i) { - if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; - if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; - } - - // compute interleaved mcu info - z->img_h_max = h_max; - z->img_v_max = v_max; - z->img_mcu_w = h_max * 8; - z->img_mcu_h = v_max * 8; - // these sizes can't be more than 17 bits - z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; - z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; - - for (i=0; i < s->img_n; ++i) { - // number of effective pixels (e.g. for non-interleaved MCU) - z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; - z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - // - // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) - // so these muls can't overflow with 32-bit ints (which we require) - z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; - z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].linebuf = NULL; - z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); - if (z->img_comp[i].raw_data == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - // align blocks for idct using mmx/sse - z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - if (z->progressive) { - // w2, h2 are multiples of 8 (see above) - z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; - z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; - z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); - if (z->img_comp[i].raw_coeff == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } - } - - return 1; -} - -// use comparisons since in some cases we handle more than one case (e.g. SOF) -#define stbi__DNL(x) ((x) == 0xdc) -#define stbi__SOI(x) ((x) == 0xd8) -#define stbi__EOI(x) ((x) == 0xd9) -#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) -#define stbi__SOS(x) ((x) == 0xda) - -#define stbi__SOF_progressive(x) ((x) == 0xc2) - -static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) -{ - int m; - z->jfif = 0; - z->app14_color_transform = -1; // valid values are 0,1,2 - z->marker = STBI__MARKER_none; // initialize cached marker to empty - m = stbi__get_marker(z); - if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); - if (scan == STBI__SCAN_type) return 1; - m = stbi__get_marker(z); - while (!stbi__SOF(m)) { - if (!stbi__process_marker(z,m)) return 0; - m = stbi__get_marker(z); - while (m == STBI__MARKER_none) { - // some files have extra padding after their blocks, so ok, we'll scan - if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); - m = stbi__get_marker(z); - } - } - z->progressive = stbi__SOF_progressive(m); - if (!stbi__process_frame_header(z, scan)) return 0; - return 1; -} - -// decode image to YCbCr format -static int stbi__decode_jpeg_image(stbi__jpeg *j) -{ - int m; - for (m = 0; m < 4; m++) { - j->img_comp[m].raw_data = NULL; - j->img_comp[m].raw_coeff = NULL; - } - j->restart_interval = 0; - if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; - m = stbi__get_marker(j); - while (!stbi__EOI(m)) { - if (stbi__SOS(m)) { - if (!stbi__process_scan_header(j)) return 0; - if (!stbi__parse_entropy_coded_data(j)) return 0; - if (j->marker == STBI__MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } - } - // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 - } - } else if (stbi__DNL(m)) { - int Ld = stbi__get16be(j->s); - stbi__uint32 NL = stbi__get16be(j->s); - if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); - if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); - } else { - if (!stbi__process_marker(j, m)) return 0; - } - m = stbi__get_marker(j); - } - if (j->progressive) - stbi__jpeg_finish(j); - return 1; -} - -// static jfif-centered resampling (across block boundaries) - -typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, - int w, int hs); - -#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) - -static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - STBI_NOTUSED(out); - STBI_NOTUSED(in_far); - STBI_NOTUSED(w); - STBI_NOTUSED(hs); - return in_near; -} - -static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples vertically for every one in input - int i; - STBI_NOTUSED(hs); - for (i=0; i < w; ++i) - out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); - return out; -} - -static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples horizontally for every one in input - int i; - stbi_uc *input = in_near; - - if (w == 1) { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; - } - - out[0] = input[0]; - out[1] = stbi__div4(input[0]*3 + input[1] + 2); - for (i=1; i < w-1; ++i) { - int n = 3*input[i]+2; - out[i*2+0] = stbi__div4(n+input[i-1]); - out[i*2+1] = stbi__div4(n+input[i+1]); - } - out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); - out[i*2+1] = input[w-1]; - - STBI_NOTUSED(in_far); - STBI_NOTUSED(hs); - - return out; -} - -#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) - -static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i,t0,t1; - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - out[0] = stbi__div4(t1+2); - for (i=1; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i=0,t0,t1; - - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - // process groups of 8 pixels for as long as we can. - // note we can't handle the last pixel in a row in this loop - // because we need to handle the filter boundary conditions. - for (; i < ((w-1) & ~7); i += 8) { -#if defined(STBI_SSE2) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - __m128i zero = _mm_setzero_si128(); - __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); - __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); - __m128i farw = _mm_unpacklo_epi8(farb, zero); - __m128i nearw = _mm_unpacklo_epi8(nearb, zero); - __m128i diff = _mm_sub_epi16(farw, nearw); - __m128i nears = _mm_slli_epi16(nearw, 2); - __m128i curr = _mm_add_epi16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - __m128i prv0 = _mm_slli_si128(curr, 2); - __m128i nxt0 = _mm_srli_si128(curr, 2); - __m128i prev = _mm_insert_epi16(prv0, t1, 0); - __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - __m128i bias = _mm_set1_epi16(8); - __m128i curs = _mm_slli_epi16(curr, 2); - __m128i prvd = _mm_sub_epi16(prev, curr); - __m128i nxtd = _mm_sub_epi16(next, curr); - __m128i curb = _mm_add_epi16(curs, bias); - __m128i even = _mm_add_epi16(prvd, curb); - __m128i odd = _mm_add_epi16(nxtd, curb); - - // interleave even and odd pixels, then undo scaling. - __m128i int0 = _mm_unpacklo_epi16(even, odd); - __m128i int1 = _mm_unpackhi_epi16(even, odd); - __m128i de0 = _mm_srli_epi16(int0, 4); - __m128i de1 = _mm_srli_epi16(int1, 4); - - // pack and write output - __m128i outv = _mm_packus_epi16(de0, de1); - _mm_storeu_si128((__m128i *) (out + i*2), outv); -#elif defined(STBI_NEON) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - uint8x8_t farb = vld1_u8(in_far + i); - uint8x8_t nearb = vld1_u8(in_near + i); - int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); - int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); - int16x8_t curr = vaddq_s16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - int16x8_t prv0 = vextq_s16(curr, curr, 7); - int16x8_t nxt0 = vextq_s16(curr, curr, 1); - int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); - int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - int16x8_t curs = vshlq_n_s16(curr, 2); - int16x8_t prvd = vsubq_s16(prev, curr); - int16x8_t nxtd = vsubq_s16(next, curr); - int16x8_t even = vaddq_s16(curs, prvd); - int16x8_t odd = vaddq_s16(curs, nxtd); - - // undo scaling and round, then store with even/odd phases interleaved - uint8x8x2_t o; - o.val[0] = vqrshrun_n_s16(even, 4); - o.val[1] = vqrshrun_n_s16(odd, 4); - vst2_u8(out + i*2, o); -#endif - - // "previous" value for next iter - t1 = 3*in_near[i+7] + in_far[i+7]; - } - - t0 = t1; - t1 = 3*in_near[i] + in_far[i]; - out[i*2] = stbi__div16(3*t1 + t0 + 8); - - for (++i; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} -#endif - -static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // resample with nearest-neighbor - int i,j; - STBI_NOTUSED(in_far); - for (i=0; i < w; ++i) - for (j=0; j < hs; ++j) - out[i*hs+j] = in_near[i]; - return out; -} - -// this is a reduced-precision calculation of YCbCr-to-RGB introduced -// to make sure the code produces the same results in both SIMD and scalar -#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) -{ - int i = 0; - -#ifdef STBI_SSE2 - // step == 3 is pretty ugly on the final interleave, and i'm not convinced - // it's useful in practice (you wouldn't use it for textures, for example). - // so just accelerate step == 4 case. - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - __m128i signflip = _mm_set1_epi8(-0x80); - __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); - __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); - __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); - __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); - __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); - __m128i xw = _mm_set1_epi16(255); // alpha channel - - for (; i+7 < count; i += 8) { - // load - __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); - __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); - __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); - __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 - __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 - - // unpack to short (and left-shift cr, cb by 8) - __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); - __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); - __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); - - // color transform - __m128i yws = _mm_srli_epi16(yw, 4); - __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); - __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); - __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); - __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); - __m128i rws = _mm_add_epi16(cr0, yws); - __m128i gwt = _mm_add_epi16(cb0, yws); - __m128i bws = _mm_add_epi16(yws, cb1); - __m128i gws = _mm_add_epi16(gwt, cr1); - - // descale - __m128i rw = _mm_srai_epi16(rws, 4); - __m128i bw = _mm_srai_epi16(bws, 4); - __m128i gw = _mm_srai_epi16(gws, 4); - - // back to byte, set up for transpose - __m128i brb = _mm_packus_epi16(rw, bw); - __m128i gxb = _mm_packus_epi16(gw, xw); - - // transpose to interleave channels - __m128i t0 = _mm_unpacklo_epi8(brb, gxb); - __m128i t1 = _mm_unpackhi_epi8(brb, gxb); - __m128i o0 = _mm_unpacklo_epi16(t0, t1); - __m128i o1 = _mm_unpackhi_epi16(t0, t1); - - // store - _mm_storeu_si128((__m128i *) (out + 0), o0); - _mm_storeu_si128((__m128i *) (out + 16), o1); - out += 32; - } - } -#endif - -#ifdef STBI_NEON - // in this version, step=3 support would be easy to add. but is there demand? - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - uint8x8_t signflip = vdup_n_u8(0x80); - int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); - int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); - int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); - int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); - - for (; i+7 < count; i += 8) { - // load - uint8x8_t y_bytes = vld1_u8(y + i); - uint8x8_t cr_bytes = vld1_u8(pcr + i); - uint8x8_t cb_bytes = vld1_u8(pcb + i); - int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); - int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); - - // expand to s16 - int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); - int16x8_t crw = vshll_n_s8(cr_biased, 7); - int16x8_t cbw = vshll_n_s8(cb_biased, 7); - - // color transform - int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); - int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); - int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); - int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); - int16x8_t rws = vaddq_s16(yws, cr0); - int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); - int16x8_t bws = vaddq_s16(yws, cb1); - - // undo scaling, round, convert to byte - uint8x8x4_t o; - o.val[0] = vqrshrun_n_s16(rws, 4); - o.val[1] = vqrshrun_n_s16(gws, 4); - o.val[2] = vqrshrun_n_s16(bws, 4); - o.val[3] = vdup_n_u8(255); - - // store, interleaving r/g/b/a - vst4_u8(out, o); - out += 8*4; - } - } -#endif - - for (; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#endif - -// set up the kernels -static void stbi__setup_jpeg(stbi__jpeg *j) -{ - j->idct_block_kernel = stbi__idct_block; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; - -#ifdef STBI_SSE2 - if (stbi__sse2_available()) { - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; - } -#endif - -#ifdef STBI_NEON - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; -#endif -} - -// clean up the temporary component buffers -static void stbi__cleanup_jpeg(stbi__jpeg *j) -{ - stbi__free_jpeg_components(j, j->s->img_n, 0); -} - -typedef struct -{ - resample_row_func resample; - stbi_uc *line0,*line1; - int hs,vs; // expansion factor in each axis - int w_lores; // horizontal pixels pre-expansion - int ystep; // how far through vertical expansion we are - int ypos; // which pre-expansion row we're on -} stbi__resample; - -// fast 0..255 * 0..255 => 0..255 rounded multiplication -static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) -{ - unsigned int t = x*y + 128; - return (stbi_uc) ((t + (t >>8)) >> 8); -} - -static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n, is_rgb; - z->s->img_n = 0; // make stbi__cleanup_jpeg safe - - // validate req_comp - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - - // load a jpeg image from whichever source, but leave in YCbCr format - if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } - - // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; - - is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); - - if (z->s->img_n == 3 && n < 3 && !is_rgb) - decode_n = 1; - else - decode_n = z->s->img_n; - - // resample and color-convert - { - int k; - unsigned int i,j; - stbi_uc *output; - stbi_uc *coutput[4]; - - stbi__resample res_comp[4]; - - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); - if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - r->hs = z->img_h_max / z->img_comp[k].h; - r->vs = z->img_v_max / z->img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (z->s->img_x + r->hs-1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = z->img_comp[k].data; - - if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; - else r->resample = stbi__resample_row_generic; - } - - // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); - if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - // now go ahead and resample - for (j=0; j < z->s->img_y; ++j) { - stbi_uc *out = output + n * z->s->img_x * j; - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(z->img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < z->img_comp[k].y) - r->line1 += z->img_comp[k].w2; - } - } - if (n >= 3) { - stbi_uc *y = coutput[0]; - if (z->s->img_n == 3) { - if (is_rgb) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = y[i]; - out[1] = coutput[1][i]; - out[2] = coutput[2][i]; - out[3] = 255; - out += n; - } - } else { - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else if (z->s->img_n == 4) { - if (z->app14_color_transform == 0) { // CMYK - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(coutput[0][i], m); - out[1] = stbi__blinn_8x8(coutput[1][i], m); - out[2] = stbi__blinn_8x8(coutput[2][i], m); - out[3] = 255; - out += n; - } - } else if (z->app14_color_transform == 2) { // YCCK - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(255 - out[0], m); - out[1] = stbi__blinn_8x8(255 - out[1], m); - out[2] = stbi__blinn_8x8(255 - out[2], m); - out += n; - } - } else { // YCbCr + alpha? Ignore the fourth channel for now - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else - for (i=0; i < z->s->img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } else { - if (is_rgb) { - if (n == 1) - for (i=0; i < z->s->img_x; ++i) - *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - else { - for (i=0; i < z->s->img_x; ++i, out += 2) { - out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - out[1] = 255; - } - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); - stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); - stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); - out[0] = stbi__compute_y(r, g, b); - out[1] = 255; - out += n; - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); - out[1] = 255; - out += n; - } - } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; - } - } - } - stbi__cleanup_jpeg(z); - *out_x = z->s->img_x; - *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output - return output; - } -} - -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - unsigned char* result; - stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); - STBI_NOTUSED(ri); - j->s = s; - stbi__setup_jpeg(j); - result = load_jpeg_image(j, x,y,comp,req_comp); - STBI_FREE(j); - return result; -} - -static int stbi__jpeg_test(stbi__context *s) -{ - int r; - stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); - j->s = s; - stbi__setup_jpeg(j); - r = stbi__decode_jpeg_header(j, STBI__SCAN_type); - stbi__rewind(s); - STBI_FREE(j); - return r; -} - -static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) -{ - if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { - stbi__rewind( j->s ); - return 0; - } - if (x) *x = j->s->img_x; - if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; - return 1; -} - -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) -{ - int result; - stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); - j->s = s; - result = stbi__jpeg_info_raw(j, x, y, comp); - STBI_FREE(j); - return result; -} -#endif - -// public domain zlib decode v0.2 Sean Barrett 2006-11-18 -// simple implementation -// - all input must be provided in an upfront buffer -// - all output is written to a single output buffer (can malloc/realloc) -// performance -// - fast huffman - -#ifndef STBI_NO_ZLIB - -// fast-way is faster to check than jpeg huffman, but slow way is slower -#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables -#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) - -// zlib-style huffman encoding -// (jpegs packs from left, zlib from right, so can't share code) -typedef struct -{ - stbi__uint16 fast[1 << STBI__ZFAST_BITS]; - stbi__uint16 firstcode[16]; - int maxcode[17]; - stbi__uint16 firstsymbol[16]; - stbi_uc size[288]; - stbi__uint16 value[288]; -} stbi__zhuffman; - -stbi_inline static int stbi__bitreverse16(int n) -{ - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -stbi_inline static int stbi__bit_reverse(int v, int bits) -{ - STBI_ASSERT(bits <= 16); - // to bit reverse n bits, reverse 16 and shift - // e.g. 11 bits, bit reverse and shift away 5 - return stbi__bitreverse16(v) >> (16-bits); -} - -static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) -{ - int i,k=0; - int code, next_code[16], sizes[17]; - - // DEFLATE spec for generating codes - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 0, sizeof(z->fast)); - for (i=0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i=1; i < 16; ++i) - if (sizes[i] > (1 << i)) - return stbi__err("bad sizes", "Corrupt PNG"); - code = 0; - for (i=1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (stbi__uint16) code; - z->firstsymbol[i] = (stbi__uint16) k; - code = (code + sizes[i]); - if (sizes[i]) - if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); - z->maxcode[i] = code << (16-i); // preshift for inner loop - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; // sentinel - for (i=0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); - z->size [c] = (stbi_uc ) s; - z->value[c] = (stbi__uint16) i; - if (s <= STBI__ZFAST_BITS) { - int j = stbi__bit_reverse(next_code[s],s); - while (j < (1 << STBI__ZFAST_BITS)) { - z->fast[j] = fastv; - j += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -// zlib-from-memory implementation for PNG reading -// because PNG allows splitting the zlib stream arbitrarily, -// and it's annoying structurally to have PNG call ZLIB call PNG, -// we require PNG read all the IDATs and combine them into a single -// memory buffer - -typedef struct -{ - stbi_uc *zbuffer, *zbuffer_end; - int num_bits; - stbi__uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - stbi__zhuffman z_length, z_distance; -} stbi__zbuf; - -stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) -{ - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; -} - -static void stbi__fill_bits(stbi__zbuf *z) -{ - do { - STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); - z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) -{ - unsigned int k; - if (z->num_bits < n) stbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s,k; - // not resolved by fast table, so compute it the slow way - // use jpeg approach, which requires MSbits at top - k = stbi__bit_reverse(a->code_buffer, 16); - for (s=STBI__ZFAST_BITS+1; ; ++s) - if (k < z->maxcode[s]) - break; - if (s == 16) return -1; // invalid code! - // code size is s, so: - b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - STBI_ASSERT(z->size[b] == s); - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s; - if (a->num_bits < 16) stbi__fill_bits(a); - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b) { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; - } - return stbi__zhuffman_decode_slowpath(a, z); -} - -static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes -{ - char *q; - int cur, limit, old_limit; - z->zout = zout; - if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (int) (z->zout - z->zout_start); - limit = old_limit = (int) (z->zout_end - z->zout_start); - while (cur + n > limit) - limit *= 2; - q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); - STBI_NOTUSED(old_limit); - if (q == NULL) return stbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static const int stbi__zlength_base[31] = { - 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,0,0 }; - -static const int stbi__zlength_extra[31]= -{ 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,0,0 }; - -static const int stbi__zdist_base[32] = { 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,0,0}; - -static const int stbi__zdist_extra[32] = -{ 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}; - -static int stbi__parse_huffman_block(stbi__zbuf *a) -{ - char *zout = a->zout; - for(;;) { - int z = stbi__zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes - if (zout >= a->zout_end) { - if (!stbi__zexpand(a, zout, 1)) return 0; - zout = a->zout; - } - *zout++ = (char) z; - } else { - stbi_uc *p; - int len,dist; - if (z == 256) { - a->zout = zout; - return 1; - } - z -= 257; - len = stbi__zlength_base[z]; - if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); - z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); - dist = stbi__zdist_base[z]; - if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); - if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); - if (zout + len > a->zout_end) { - if (!stbi__zexpand(a, zout, len)) return 0; - zout = a->zout; - } - p = (stbi_uc *) (zout - dist); - if (dist == 1) { // run of one byte; common in images. - stbi_uc v = *p; - if (len) { do *zout++ = v; while (--len); } - } else { - if (len) { do *zout++ = *p++; while (--len); } - } - } - } -} - -static int stbi__compute_huffman_codes(stbi__zbuf *a) -{ - static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - stbi__zhuffman z_codelength; - stbi_uc lencodes[286+32+137];//padding for maximum single op - stbi_uc codelength_sizes[19]; - int i,n; - - int hlit = stbi__zreceive(a,5) + 257; - int hdist = stbi__zreceive(a,5) + 1; - int hclen = stbi__zreceive(a,4) + 4; - int ntot = hlit + hdist; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i=0; i < hclen; ++i) { - int s = stbi__zreceive(a,3); - codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; - } - if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < ntot) { - int c = stbi__zhuffman_decode(a, &z_codelength); - if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); - if (c < 16) - lencodes[n++] = (stbi_uc) c; - else { - stbi_uc fill = 0; - if (c == 16) { - c = stbi__zreceive(a,2)+3; - if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); - fill = lencodes[n-1]; - } else if (c == 17) - c = stbi__zreceive(a,3)+3; - else { - STBI_ASSERT(c == 18); - c = stbi__zreceive(a,7)+11; - } - if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); - memset(lencodes+n, fill, c); - n += c; - } - } - if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); - if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; - return 1; -} - -static int stbi__parse_uncompressed_block(stbi__zbuf *a) -{ - stbi_uc header[4]; - int len,nlen,k; - if (a->num_bits & 7) - stbi__zreceive(a, a->num_bits & 7); // discard - // drain the bit-packed data into header - k = 0; - while (a->num_bits > 0) { - header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check - a->code_buffer >>= 8; - a->num_bits -= 8; - } - STBI_ASSERT(a->num_bits == 0); - // now fill header the normal way - while (k < 4) - header[k++] = stbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!stbi__zexpand(a, a->zout, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int stbi__parse_zlib_header(stbi__zbuf *a) -{ - int cmf = stbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = stbi__zget8(a); - if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png - if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; -} - -static const stbi_uc stbi__zdefault_length[288] = -{ - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 -}; -static const stbi_uc stbi__zdefault_distance[32] = -{ - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 -}; -/* -Init algorithm: -{ - int i; // use <= to match clearly with spec - for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; - for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; - for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; - for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; - - for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; -} -*/ - -static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) -{ - int final, type; - if (parse_header) - if (!stbi__parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - do { - final = stbi__zreceive(a,1); - type = stbi__zreceive(a,2); - if (type == 0) { - if (!stbi__parse_uncompressed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - // use fixed code lengths - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; - } else { - if (!stbi__compute_huffman_codes(a)) return 0; - } - if (!stbi__parse_huffman_block(a)) return 0; - } - } while (!final); - return 1; -} - -static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) -{ - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - - return stbi__parse_zlib(a, parse_header); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) -{ - return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) - return (int) (a.zout - a.zout_start); - else - return -1; -} - -STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(16384); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer+len; - if (stbi__do_zlib(&a, p, 16384, 1, 0)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) - return (int) (a.zout - a.zout_start); - else - return -1; -} -#endif - -// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 -// simple implementation -// - only 8-bit samples -// - no CRC checking -// - allocates lots of intermediate memory -// - avoids problem of streaming data between subsystems -// - avoids explicit window management -// performance -// - uses stb_zlib, a PD zlib implementation with fast huffman decoding - -#ifndef STBI_NO_PNG -typedef struct -{ - stbi__uint32 length; - stbi__uint32 type; -} stbi__pngchunk; - -static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) -{ - stbi__pngchunk c; - c.length = stbi__get32be(s); - c.type = stbi__get32be(s); - return c; -} - -static int stbi__check_png_header(stbi__context *s) -{ - static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; - int i; - for (i=0; i < 8; ++i) - if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); - return 1; -} - -typedef struct -{ - stbi__context *s; - stbi_uc *idata, *expanded, *out; - int depth; -} stbi__png; - - -enum { - STBI__F_none=0, - STBI__F_sub=1, - STBI__F_up=2, - STBI__F_avg=3, - STBI__F_paeth=4, - // synthetic filters used for first scanline to avoid needing a dummy row of 0s - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static stbi_uc first_row_filter[5] = -{ - STBI__F_none, - STBI__F_sub, - STBI__F_none, - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static int stbi__paeth(int a, int b, int c) -{ - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; -} - -static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; - -// create the png data from post-deflated data -static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) -{ - int bytes = (depth == 16? 2 : 1); - stbi__context *s = a->s; - stbi__uint32 i,j,stride = x*out_n*bytes; - stbi__uint32 img_len, img_width_bytes; - int k; - int img_n = s->img_n; // copy it into a local for later - - int output_bytes = out_n*bytes; - int filter_bytes = img_n*bytes; - int width = x; - - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into - if (!a->out) return stbi__err("outofmem", "Out of memory"); - - if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - img_len = (img_width_bytes + 1) * y; - - // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, - // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), - // so just check for raw_len < img_len always. - if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); - - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *prior; - int filter = *raw++; - - if (filter > 4) - return stbi__err("invalid filter","Corrupt PNG"); - - if (depth < 8) { - STBI_ASSERT(img_width_bytes <= x); - cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place - filter_bytes = 1; - width = img_width_bytes; - } - prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above - - // if first row, use special filter that doesn't sample previous row - if (j == 0) filter = first_row_filter[filter]; - - // handle first byte explicitly - for (k=0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none : cur[k] = raw[k]; break; - case STBI__F_sub : cur[k] = raw[k]; break; - case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; - case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; - case STBI__F_avg_first : cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } - } - - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; // first pixel - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; // first pixel top byte - cur[filter_bytes+1] = 255; // first pixel bottom byte - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; - } - - // this is a little gross, so that we don't switch per-pixel or per-component - if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*filter_bytes; - #define STBI__CASE(f) \ - case f: \ - for (k=0; k < nk; ++k) - switch (filter) { - // "none" filter turns into a memcpy here; make that explicit. - case STBI__F_none: memcpy(cur, raw, nk); break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; - } - #undef STBI__CASE - raw += nk; - } else { - STBI_ASSERT(img_n+1 == out_n); - #define STBI__CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ - for (k=0; k < filter_bytes; ++k) - switch (filter) { - STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; - } - #undef STBI__CASE - - // the loop above sets the high byte of the pixels' alpha, but for - // 16 bit png files we also need the low byte set. we'll do that here. - if (depth == 16) { - cur = a->out + stride*j; // start at the beginning of the row again - for (i=0; i < x; ++i,cur+=output_bytes) { - cur[filter_bytes+1] = 255; - } - } - } - } - - // we make a separate pass to expand bits to pixels; for performance, - // this could run two scanlines behind the above code, so it won't - // intefere with filtering but will still be in the cache. - if (depth < 8) { - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; - // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit - // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop - stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range - - // note that the final byte might overshoot and write more data than desired. - // we can allocate enough data that this never writes out of memory, but it - // could also overwrite the next scanline. can it overwrite non-empty data - // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. - // so we need to explicitly clamp the final ones - - if (depth == 4) { - for (k=x*img_n; k >= 2; k-=2, ++in) { - *cur++ = scale * ((*in >> 4) ); - *cur++ = scale * ((*in ) & 0x0f); - } - if (k > 0) *cur++ = scale * ((*in >> 4) ); - } else if (depth == 2) { - for (k=x*img_n; k >= 4; k-=4, ++in) { - *cur++ = scale * ((*in >> 6) ); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in ) & 0x03); - } - if (k > 0) *cur++ = scale * ((*in >> 6) ); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k=x*img_n; k >= 8; k-=8, ++in) { - *cur++ = scale * ((*in >> 7) ); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in ) & 0x01); - } - if (k > 0) *cur++ = scale * ((*in >> 7) ); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); - } - if (img_n != out_n) { - int q; - // insert alpha = 255 - cur = a->out + stride*j; - if (img_n == 1) { - for (q=x-1; q >= 0; --q) { - cur[q*2+1] = 255; - cur[q*2+0] = cur[q]; - } - } else { - STBI_ASSERT(img_n == 3); - for (q=x-1; q >= 0; --q) { - cur[q*4+3] = 255; - cur[q*4+2] = cur[q*3+2]; - cur[q*4+1] = cur[q*3+1]; - cur[q*4+0] = cur[q*3+0]; - } - } - } - } - } else if (depth == 16) { - // force the image data from big-endian to platform-native. - // this is done in a separate pass due to the decoding relying - // on the data being untouched, but could probably be done - // per-line during decode if care is taken. - stbi_uc *cur = a->out; - stbi__uint16 *cur16 = (stbi__uint16*)cur; - - for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { - *cur16 = (cur[0] << 8) | cur[1]; - } - } - - return 1; -} - -static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) -{ - int bytes = (depth == 16 ? 2 : 1); - int out_bytes = out_n * bytes; - stbi_uc *final; - int p; - if (!interlaced) - return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); - - // de-interlacing - final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); - for (p=0; p < 7; ++p) { - int xorig[] = { 0,4,0,2,0,1,0 }; - int yorig[] = { 0,0,4,0,2,0,1 }; - int xspc[] = { 8,8,4,4,2,2,1 }; - int yspc[] = { 8,8,8,4,4,2,2 }; - int i,j,x,y; - // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 - x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; - if (x && y) { - stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { - STBI_FREE(final); - return 0; - } - for (j=0; j < y; ++j) { - for (i=0; i < x; ++i) { - int out_y = j*yspc[p]+yorig[p]; - int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, - a->out + (j*x+i)*out_bytes, out_bytes); - } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } - } - a->out = final; - - return 1; -} - -static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - // compute color-based transparency, assuming we've - // already got 255 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i=0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i=0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi__uint16 *p = (stbi__uint16*) z->out; - - // compute color-based transparency, assuming we've - // already got 65535 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 65535); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) -{ - stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - stbi_uc *p, *temp_out, *orig = a->out; - - p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); - if (p == NULL) return stbi__err("outofmem", "Out of memory"); - - // between here and free(out) below, exitting would leak - temp_out = p; - - if (pal_img_n == 3) { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p += 3; - } - } else { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p[3] = palette[n+3]; - p += 4; - } - } - STBI_FREE(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -static int stbi__unpremultiply_on_load = 0; -static int stbi__de_iphone_flag = 0; - -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; -} - -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag = flag_true_if_should_convert; -} - -static void stbi__de_iphone(stbi__png *z) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - if (s->img_out_n == 3) { // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 3; - } - } else { - STBI_ASSERT(s->img_out_n == 4); - if (stbi__unpremultiply_on_load) { - // convert bgr to rgb and unpremultiply - for (i=0; i < pixel_count; ++i) { - stbi_uc a = p[3]; - stbi_uc t = p[0]; - if (a) { - stbi_uc half = a / 2; - p[0] = (p[2] * 255 + half) / a; - p[1] = (p[1] * 255 + half) / a; - p[2] = ( t * 255 + half) / a; - } else { - p[0] = p[2]; - p[2] = t; - } - p += 4; - } - } else { - // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 4; - } - } - } -} - -#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) - -static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) -{ - stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]; - stbi__uint16 tc16[3]; - stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, color=0, is_iphone=0; - stbi__context *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!stbi__check_png_header(s)) return 0; - - if (scan == STBI__SCAN_type) return 1; - - for (;;) { - stbi__pngchunk c = stbi__get_chunk_header(s); - switch (c.type) { - case STBI__PNG_TYPE('C','g','B','I'): - is_iphone = 1; - stbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I','H','D','R'): { - int comp,filter; - if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); - first = 0; - if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); - color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); - comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); - filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); - interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); - if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; - } else { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS - } - break; - } - - case STBI__PNG_TYPE('P','L','T','E'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); - for (i=0; i < pal_len; ++i) { - palette[i*4+0] = stbi__get8(s); - palette[i*4+1] = stbi__get8(s); - palette[i*4+2] = stbi__get8(s); - palette[i*4+3] = 255; - } - break; - } - - case STBI__PNG_TYPE('t','R','N','S'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); - if (pal_img_n) { - if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } - if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); - if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); - pal_img_n = 4; - for (i=0; i < c.length; ++i) - palette[i*4+3] = stbi__get8(s); - } else { - if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); - if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); - has_trans = 1; - if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is - } else { - for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger - } - } - break; - } - - case STBI__PNG_TYPE('I','D','A','T'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } - if ((int)(ioff + c.length) < (int)ioff) return 0; - if (ioff + c.length > idata_limit) { - stbi__uint32 idata_limit_old = idata_limit; - stbi_uc *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - STBI_NOTUSED(idata_limit_old); - p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); - ioff += c.length; - break; - } - - case STBI__PNG_TYPE('I','E','N','D'): { - stbi__uint32 raw_len, bpl; - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) return 1; - if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); - // initial guess for decoded data size to avoid unnecessary reallocs - bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); - if (z->expanded == NULL) return 0; // zlib should set error - STBI_FREE(z->idata); z->idata = NULL; - if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n+1; - else - s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; - if (has_trans) { - if (z->depth == 16) { - if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; - } else { - if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; - } - } - if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) - stbi__de_iphone(z); - if (pal_img_n) { - // pal_img_n == 3 or 4 - s->img_n = pal_img_n; // record the actual colors we had - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } else if (has_trans) { - // non-paletted image with tRNS -> source image has (constant) alpha - ++s->img_n; - } - STBI_FREE(z->expanded); z->expanded = NULL; - return 1; - } - - default: - // if critical, fail - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - #ifndef STBI_NO_FAILURE_STRINGS - // not threadsafe - static char invalid_chunk[] = "XXXX PNG chunk not known"; - invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); - invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); - invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); - invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); - #endif - return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); - } - stbi__skip(s, c.length); - break; - } - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - } -} - -static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) -{ - void *result=NULL; - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth < 8) - ri->bits_per_channel = 8; - else - ri->bits_per_channel = p->depth; - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - if (ri->bits_per_channel == 8) - result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - else - result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_n; - } - STBI_FREE(p->out); p->out = NULL; - STBI_FREE(p->expanded); p->expanded = NULL; - STBI_FREE(p->idata); p->idata = NULL; - - return result; -} - -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi__png p; - p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp, ri); -} - -static int stbi__png_test(stbi__context *s) -{ - int r; - r = stbi__check_png_header(s); - stbi__rewind(s); - return r; -} - -static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) -{ - if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { - stbi__rewind( p->s ); - return 0; - } - if (x) *x = p->s->img_x; - if (y) *y = p->s->img_y; - if (comp) *comp = p->s->img_n; - return 1; -} - -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__png p; - p.s = s; - return stbi__png_info_raw(&p, x, y, comp); -} - -static int stbi__png_is16(stbi__context *s) -{ - stbi__png p; - p.s = s; - if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) - return 0; - if (p.depth != 16) { - stbi__rewind(p.s); - return 0; - } - return 1; -} -#endif - -// Microsoft/Windows BMP image - -#ifndef STBI_NO_BMP -static int stbi__bmp_test_raw(stbi__context *s) -{ - int r; - int sz; - if (stbi__get8(s) != 'B') return 0; - if (stbi__get8(s) != 'M') return 0; - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - stbi__get32le(s); // discard data offset - sz = stbi__get32le(s); - r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); - return r; -} - -static int stbi__bmp_test(stbi__context *s) -{ - int r = stbi__bmp_test_raw(s); - stbi__rewind(s); - return r; -} - - -// returns 0..31 for the highest set bit -static int stbi__high_bit(unsigned int z) -{ - int n=0; - if (z == 0) return -1; - if (z >= 0x10000) n += 16, z >>= 16; - if (z >= 0x00100) n += 8, z >>= 8; - if (z >= 0x00010) n += 4, z >>= 4; - if (z >= 0x00004) n += 2, z >>= 2; - if (z >= 0x00002) n += 1, z >>= 1; - return n; -} - -static int stbi__bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; -} - -// extract an arbitrarily-aligned N-bit value (N=bits) -// from v, and then make it 8-bits long and fractionally -// extend it to full full range. -static int stbi__shiftsigned(int v, int shift, int bits) -{ - static unsigned int mul_table[9] = { - 0, - 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, - 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, - }; - static unsigned int shift_table[9] = { - 0, 0,0,1,0,2,4,6,0, - }; - if (shift < 0) - v <<= -shift; - else - v >>= shift; - STBI_ASSERT(v >= 0 && v < 256); - v >>= (8-bits); - STBI_ASSERT(bits >= 0 && bits <= 8); - return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; -} - -typedef struct -{ - int bpp, offset, hsz; - unsigned int mr,mg,mb,ma, all_a; -} stbi__bmp_data; - -static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) -{ - int hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - info->offset = stbi__get32le(s); - info->hsz = hsz = stbi__get32le(s); - info->mr = info->mg = info->mb = info->ma = 0; - - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); - if (hsz == 12) { - s->img_x = stbi__get16le(s); - s->img_y = stbi__get16le(s); - } else { - s->img_x = stbi__get32le(s); - s->img_y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); - info->bpp = stbi__get16le(s); - if (hsz != 12) { - int compress = stbi__get32le(s); - if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); - stbi__get32le(s); // discard sizeof - stbi__get32le(s); // discard hres - stbi__get32le(s); // discard vres - stbi__get32le(s); // discard colorsused - stbi__get32le(s); // discard max important - if (hsz == 40 || hsz == 56) { - if (hsz == 56) { - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - } - if (info->bpp == 16 || info->bpp == 32) { - if (compress == 0) { - if (info->bpp == 32) { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } else { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } - } else if (compress == 3) { - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - // not documented, but generated by photoshop and handled by mspaint - if (info->mr == info->mg && info->mg == info->mb) { - // ?!?!? - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else { - int i; - if (hsz != 108 && hsz != 124) - return stbi__errpuc("bad BMP", "bad BMP"); - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->ma = stbi__get32le(s); - stbi__get32le(s); // discard color space - for (i=0; i < 12; ++i) - stbi__get32le(s); // discard color space parameters - if (hsz == 124) { - stbi__get32le(s); // discard rendering intent - stbi__get32le(s); // discard offset of profile data - stbi__get32le(s); // discard size of profile data - stbi__get32le(s); // discard reserved - } - } - } - return (void *) 1; -} - - -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - unsigned int mr=0,mg=0,mb=0,ma=0, all_a; - stbi_uc pal[256][4]; - int psize=0,i,j,width; - int flip_vertically, pad, target; - stbi__bmp_data info; - STBI_NOTUSED(ri); - - info.all_a = 255; - if (stbi__bmp_parse_header(s, &info) == NULL) - return NULL; // error code already set - - flip_vertically = ((int) s->img_y) > 0; - s->img_y = abs((int) s->img_y); - - mr = info.mr; - mg = info.mg; - mb = info.mb; - ma = info.ma; - all_a = info.all_a; - - if (info.hsz == 12) { - if (info.bpp < 24) - psize = (info.offset - 14 - 24) / 3; - } else { - if (info.bpp < 16) - psize = (info.offset - 14 - info.hsz) >> 2; - } - - s->img_n = ma ? 4 : 3; - if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; - else - target = s->img_n; // if they want monochrome, we'll post-convert - - // sanity-check size - if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "Corrupt BMP"); - - out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - if (info.bpp < 16) { - int z=0; - if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } - for (i=0; i < psize; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - if (info.hsz != 12) stbi__get8(s); - pal[i][3] = 255; - } - stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); - if (info.bpp == 1) width = (s->img_x + 7) >> 3; - else if (info.bpp == 4) width = (s->img_x + 1) >> 1; - else if (info.bpp == 8) width = s->img_x; - else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } - pad = (-width)&3; - if (info.bpp == 1) { - for (j=0; j < (int) s->img_y; ++j) { - int bit_offset = 7, v = stbi__get8(s); - for (i=0; i < (int) s->img_x; ++i) { - int color = (v>>bit_offset)&0x1; - out[z++] = pal[color][0]; - out[z++] = pal[color][1]; - out[z++] = pal[color][2]; - if((--bit_offset) < 0) { - bit_offset = 7; - v = stbi__get8(s); - } - } - stbi__skip(s, pad); - } - } else { - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (info.bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (info.bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - } - stbi__skip(s, pad); - } - } - } else { - int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; - int z = 0; - int easy=0; - stbi__skip(s, info.offset - 14 - info.hsz); - if (info.bpp == 24) width = 3 * s->img_x; - else if (info.bpp == 16) width = 2*s->img_x; - else /* bpp = 32 and pad = 0 */ width=0; - pad = (-width) & 3; - if (info.bpp == 24) { - easy = 1; - } else if (info.bpp == 32) { - if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) - easy = 2; - } - if (!easy) { - if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - // right shift amt to put high bit in position #7 - rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); - gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); - bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); - ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); - } - for (j=0; j < (int) s->img_y; ++j) { - if (easy) { - for (i=0; i < (int) s->img_x; ++i) { - unsigned char a; - out[z+2] = stbi__get8(s); - out[z+1] = stbi__get8(s); - out[z+0] = stbi__get8(s); - z += 3; - a = (easy == 2 ? stbi__get8(s) : 255); - all_a |= a; - if (target == 4) out[z++] = a; - } - } else { - int bpp = info.bpp; - for (i=0; i < (int) s->img_x; ++i) { - stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); - unsigned int a; - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); - a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); - all_a |= a; - if (target == 4) out[z++] = STBI__BYTECAST(a); - } - } - stbi__skip(s, pad); - } - } - - // if alpha channel is all 0s, replace with all 255s - if (target == 4 && all_a == 0) - for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) - out[i] = 255; - - if (flip_vertically) { - stbi_uc t; - for (j=0; j < (int) s->img_y>>1; ++j) { - stbi_uc *p1 = out + j *s->img_x*target; - stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; - for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i], p1[i] = p2[i], p2[i] = t; - } - } - } - - if (req_comp && req_comp != target) { - out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - return out; -} -#endif - -// Targa Truevision - TGA -// by Jonathan Dummer -#ifndef STBI_NO_TGA -// returns STBI_rgb or whatever, 0 on error -static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) -{ - // only RGB or RGBA (incl. 16bit) or grey allowed - if (is_rgb16) *is_rgb16 = 0; - switch(bits_per_pixel) { - case 8: return STBI_grey; - case 16: if(is_grey) return STBI_grey_alpha; - // fallthrough - case 15: if(is_rgb16) *is_rgb16 = 1; - return STBI_rgb; - case 24: // fallthrough - case 32: return bits_per_pixel/8; - default: return 0; - } -} - -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) -{ - int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; - int sz, tga_colormap_type; - stbi__get8(s); // discard Offset - tga_colormap_type = stbi__get8(s); // colormap type - if( tga_colormap_type > 1 ) { - stbi__rewind(s); - return 0; // only RGB or indexed allowed - } - tga_image_type = stbi__get8(s); // image type - if ( tga_colormap_type == 1 ) { // colormapped (paletted) image - if (tga_image_type != 1 && tga_image_type != 9) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip image x and y origin - tga_colormap_bpp = sz; - } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE - if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { - stbi__rewind(s); - return 0; // only RGB or grey allowed, +/- RLE - } - stbi__skip(s,9); // skip colormap specification and image x/y origin - tga_colormap_bpp = 0; - } - tga_w = stbi__get16le(s); - if( tga_w < 1 ) { - stbi__rewind(s); - return 0; // test width - } - tga_h = stbi__get16le(s); - if( tga_h < 1 ) { - stbi__rewind(s); - return 0; // test height - } - tga_bits_per_pixel = stbi__get8(s); // bits per pixel - stbi__get8(s); // ignore alpha bits - if (tga_colormap_bpp != 0) { - if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { - // when using a colormap, tga_bits_per_pixel is the size of the indexes - // I don't think anything but 8 or 16bit indexes makes sense - stbi__rewind(s); - return 0; - } - tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); - } else { - tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); - } - if(!tga_comp) { - stbi__rewind(s); - return 0; - } - if (x) *x = tga_w; - if (y) *y = tga_h; - if (comp) *comp = tga_comp; - return 1; // seems to have passed everything -} - -static int stbi__tga_test(stbi__context *s) -{ - int res = 0; - int sz, tga_color_type; - stbi__get8(s); // discard Offset - tga_color_type = stbi__get8(s); // color type - if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed - sz = stbi__get8(s); // image type - if ( tga_color_type == 1 ) { // colormapped (paletted) image - if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - stbi__skip(s,4); // skip image x and y origin - } else { // "normal" image w/o colormap - if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE - stbi__skip(s,9); // skip colormap specification and image x/y origin - } - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height - sz = stbi__get8(s); // bits per pixel - if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - - res = 1; // if we got this far, everything's good and we can return 1 instead of 0 - -errorEnd: - stbi__rewind(s); - return res; -} - -// read 16bit value and convert to 24bit RGB -static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) -{ - stbi__uint16 px = (stbi__uint16)stbi__get16le(s); - stbi__uint16 fiveBitMask = 31; - // we have 3 channels with 5bits each - int r = (px >> 10) & fiveBitMask; - int g = (px >> 5) & fiveBitMask; - int b = px & fiveBitMask; - // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later - out[0] = (stbi_uc)((r * 255)/31); - out[1] = (stbi_uc)((g * 255)/31); - out[2] = (stbi_uc)((b * 255)/31); - - // some people claim that the most significant bit might be used for alpha - // (possibly if an alpha-bit is set in the "image descriptor byte") - // but that only made 16bit test images completely translucent.. - // so let's treat all 15 and 16bit TGAs as RGB with no alpha. -} - -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - // read in the TGA header stuff - int tga_offset = stbi__get8(s); - int tga_indexed = stbi__get8(s); - int tga_image_type = stbi__get8(s); - int tga_is_RLE = 0; - int tga_palette_start = stbi__get16le(s); - int tga_palette_len = stbi__get16le(s); - int tga_palette_bits = stbi__get8(s); - int tga_x_origin = stbi__get16le(s); - int tga_y_origin = stbi__get16le(s); - int tga_width = stbi__get16le(s); - int tga_height = stbi__get16le(s); - int tga_bits_per_pixel = stbi__get8(s); - int tga_comp, tga_rgb16=0; - int tga_inverted = stbi__get8(s); - // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) - // image data - unsigned char *tga_data; - unsigned char *tga_palette = NULL; - int i, j; - unsigned char raw_data[4] = {0}; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - STBI_NOTUSED(ri); - - // do a tiny bit of precessing - if ( tga_image_type >= 8 ) - { - tga_image_type -= 8; - tga_is_RLE = 1; - } - tga_inverted = 1 - ((tga_inverted >> 5) & 1); - - // If I'm paletted, then I'll use the number of bits from the palette - if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); - else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); - - if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency - return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); - - // tga info - *x = tga_width; - *y = tga_height; - if (comp) *comp = tga_comp; - - if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) - return stbi__errpuc("too large", "Corrupt TGA"); - - tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); - if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); - - // skip to the data's starting position (offset usually = 0) - stbi__skip(s, tga_offset ); - - if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { - for (i=0; i < tga_height; ++i) { - int row = tga_inverted ? tga_height -i - 1 : i; - stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; - stbi__getn(s, tga_row, tga_width * tga_comp); - } - } else { - // do I need to load a palette? - if ( tga_indexed) - { - // any data to skip? (offset usually = 0) - stbi__skip(s, tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); - if (!tga_palette) { - STBI_FREE(tga_data); - return stbi__errpuc("outofmem", "Out of memory"); - } - if (tga_rgb16) { - stbi_uc *pal_entry = tga_palette; - STBI_ASSERT(tga_comp == STBI_rgb); - for (i=0; i < tga_palette_len; ++i) { - stbi__tga_read_rgb16(s, pal_entry); - pal_entry += tga_comp; - } - } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { - STBI_FREE(tga_data); - STBI_FREE(tga_palette); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - } - // load the data - for (i=0; i < tga_width * tga_height; ++i) - { - // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? - if ( tga_is_RLE ) - { - if ( RLE_count == 0 ) - { - // yep, get the next byte as a RLE command - int RLE_cmd = stbi__get8(s); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } else if ( !RLE_repeating ) - { - read_next_pixel = 1; - } - } else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if ( read_next_pixel ) - { - // load however much data we did have - if ( tga_indexed ) - { - // read in index, then perform the lookup - int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); - if ( pal_idx >= tga_palette_len ) { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_comp; - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = tga_palette[pal_idx+j]; - } - } else if(tga_rgb16) { - STBI_ASSERT(tga_comp == STBI_rgb); - stbi__tga_read_rgb16(s, raw_data); - } else { - // read in the data raw - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = stbi__get8(s); - } - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel - - // copy data - for (j = 0; j < tga_comp; ++j) - tga_data[i*tga_comp+j] = raw_data[j]; - - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if ( tga_inverted ) - { - for (j = 0; j*2 < tga_height; ++j) - { - int index1 = j * tga_width * tga_comp; - int index2 = (tga_height - 1 - j) * tga_width * tga_comp; - for (i = tga_width * tga_comp; i > 0; --i) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if ( tga_palette != NULL ) - { - STBI_FREE( tga_palette ); - } - } - - // swap RGB - if the source data was RGB16, it already is in the right order - if (tga_comp >= 3 && !tga_rgb16) - { - unsigned char* tga_pixel = tga_data; - for (i=0; i < tga_width * tga_height; ++i) - { - unsigned char temp = tga_pixel[0]; - tga_pixel[0] = tga_pixel[2]; - tga_pixel[2] = temp; - tga_pixel += tga_comp; - } - } - - // convert to target component count - if (req_comp && req_comp != tga_comp) - tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); - - // the things I do to get rid of an error message, and yet keep - // Microsoft's C compilers happy... [8^( - tga_palette_start = tga_palette_len = tga_palette_bits = - tga_x_origin = tga_y_origin = 0; - // OK, done - return tga_data; -} -#endif - -// ************************************************************************************************* -// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s) -{ - int r = (stbi__get32be(s) == 0x38425053); - stbi__rewind(s); - return r; -} - -static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) -{ - int count, nleft, len; - - count = 0; - while ((nleft = pixelCount - count) > 0) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - if (len > nleft) return 0; // corrupt data - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len = 257 - len; - if (len > nleft) return 0; // corrupt data - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } - - return 1; -} - -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - int pixelCount; - int channelCount, compression; - int channel, i; - int bitdepth; - int w,h; - stbi_uc *out; - STBI_NOTUSED(ri); - - // Check identifier - if (stbi__get32be(s) != 0x38425053) // "8BPS" - return stbi__errpuc("not PSD", "Corrupt PSD image"); - - // Check file type version. - if (stbi__get16be(s) != 1) - return stbi__errpuc("wrong version", "Unsupported version of PSD image"); - - // Skip 6 reserved bytes. - stbi__skip(s, 6 ); - - // Read the number of channels (R, G, B, A, etc). - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) - return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); - - // Read the rows and columns of the image. - h = stbi__get32be(s); - w = stbi__get32be(s); - - // Make sure the depth is 8 bits. - bitdepth = stbi__get16be(s); - if (bitdepth != 8 && bitdepth != 16) - return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); - - // Make sure the color mode is RGB. - // Valid options are: - // 0: Bitmap - // 1: Grayscale - // 2: Indexed color - // 3: RGB color - // 4: CMYK color - // 7: Multichannel - // 8: Duotone - // 9: Lab color - if (stbi__get16be(s) != 3) - return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); - - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) - stbi__skip(s,stbi__get32be(s) ); - - // Skip the image resources. (resolution, pen tool paths, etc) - stbi__skip(s, stbi__get32be(s) ); - - // Skip the reserved data. - stbi__skip(s, stbi__get32be(s) ); - - // Find out if the data is compressed. - // Known values: - // 0: no compression - // 1: RLE compressed - compression = stbi__get16be(s); - if (compression > 1) - return stbi__errpuc("bad compression", "PSD has an unknown compression format"); - - // Check size - if (!stbi__mad3sizes_valid(4, w, h, 0)) - return stbi__errpuc("too large", "Corrupt PSD"); - - // Create the destination image. - - if (!compression && bitdepth == 16 && bpc == 16) { - out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); - ri->bits_per_channel = 16; - } else - out = (stbi_uc *) stbi__malloc(4 * w*h); - - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - pixelCount = w*h; - - // Initialize the data to zero. - //memset( out, 0, pixelCount * 4 ); - - // Finally, the image data. - if (compression) { - // RLE as used by .PSD and .TIFF - // Loop until you get the number of unpacked bytes you are expecting: - // Read the next source byte into n. - // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. - // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. - // Else if n is 128, noop. - // Endloop - - // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, - // which we're going to just skip. - stbi__skip(s, h * channelCount * 2 ); - - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out+channel; - if (channel >= channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++, p += 4) - *p = (channel == 3 ? 255 : 0); - } else { - // Read the RLE data. - if (!stbi__psd_decode_rle(s, p, pixelCount)) { - STBI_FREE(out); - return stbi__errpuc("corrupt", "bad RLE data"); - } - } - } - - } else { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. - - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - if (channel >= channelCount) { - // Fill this channel with default data. - if (bitdepth == 16 && bpc == 16) { - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - stbi__uint16 val = channel == 3 ? 65535 : 0; - for (i = 0; i < pixelCount; i++, q += 4) - *q = val; - } else { - stbi_uc *p = out+channel; - stbi_uc val = channel == 3 ? 255 : 0; - for (i = 0; i < pixelCount; i++, p += 4) - *p = val; - } - } else { - if (ri->bits_per_channel == 16) { // output bpc - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - for (i = 0; i < pixelCount; i++, q += 4) - *q = (stbi__uint16) stbi__get16be(s); - } else { - stbi_uc *p = out+channel; - if (bitdepth == 16) { // input bpc - for (i = 0; i < pixelCount; i++, p += 4) - *p = (stbi_uc) (stbi__get16be(s) >> 8); - } else { - for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); - } - } - } - } - } - - // remove weird white matte from PSD - if (channelCount >= 4) { - if (ri->bits_per_channel == 16) { - for (i=0; i < w*h; ++i) { - stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; - if (pixel[3] != 0 && pixel[3] != 65535) { - float a = pixel[3] / 65535.0f; - float ra = 1.0f / a; - float inv_a = 65535.0f * (1 - ra); - pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); - pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); - pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); - } - } - } else { - for (i=0; i < w*h; ++i) { - unsigned char *pixel = out + 4*i; - if (pixel[3] != 0 && pixel[3] != 255) { - float a = pixel[3] / 255.0f; - float ra = 1.0f / a; - float inv_a = 255.0f * (1 - ra); - pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); - pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); - pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); - } - } - } - } - - // convert to desired output format - if (req_comp && req_comp != 4) { - if (ri->bits_per_channel == 16) - out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); - else - out = stbi__convert_format(out, 4, req_comp, w, h); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - if (comp) *comp = 4; - *y = h; - *x = w; - - return out; -} -#endif - -// ************************************************************************************************* -// Softimage PIC loader -// by Tom Seddon -// -// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format -// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ - -#ifndef STBI_NO_PIC -static int stbi__pic_is4(stbi__context *s,const char *str) -{ - int i; - for (i=0; i<4; ++i) - if (stbi__get8(s) != (stbi_uc)str[i]) - return 0; - - return 1; -} - -static int stbi__pic_test_core(stbi__context *s) -{ - int i; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) - return 0; - - for(i=0;i<84;++i) - stbi__get8(s); - - if (!stbi__pic_is4(s,"PICT")) - return 0; - - return 1; -} - -typedef struct -{ - stbi_uc size,type,channel; -} stbi__pic_packet; - -static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) -{ - int mask=0x80, i; - - for (i=0; i<4; ++i, mask>>=1) { - if (channel & mask) { - if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); - dest[i]=stbi__get8(s); - } - } - - return dest; -} - -static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) -{ - int mask=0x80,i; - - for (i=0;i<4; ++i, mask>>=1) - if (channel&mask) - dest[i]=src[i]; -} - -static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) -{ - int act_comp=0,num_packets=0,y,chained; - stbi__pic_packet packets[10]; - - // this will (should...) cater for even some bizarre stuff like having data - // for the same channel in multiple packets. - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return stbi__errpuc("bad format","too many packets"); - - packet = &packets[num_packets++]; - - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - - act_comp |= packet->channel; - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); - if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? - - for(y=0; ytype) { - default: - return stbi__errpuc("bad format","packet has bad compression type"); - - case 0: {//uncompressed - int x; - - for(x=0;xchannel,dest)) - return 0; - break; - } - - case 1://Pure RLE - { - int left=width, i; - - while (left>0) { - stbi_uc count,value[4]; - - count=stbi__get8(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); - - if (count > left) - count = (stbi_uc) left; - - if (!stbi__readval(s,packet->channel,value)) return 0; - - for(i=0; ichannel,dest,value); - left -= count; - } - } - break; - - case 2: {//Mixed RLE - int left=width; - while (left>0) { - int count = stbi__get8(s), i; - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); - - if (count >= 128) { // Repeated - stbi_uc value[4]; - - if (count==128) - count = stbi__get16be(s); - else - count -= 127; - if (count > left) - return stbi__errpuc("bad file","scanline overrun"); - - if (!stbi__readval(s,packet->channel,value)) - return 0; - - for(i=0;ichannel,dest,value); - } else { // Raw - ++count; - if (count>left) return stbi__errpuc("bad file","scanline overrun"); - - for(i=0;ichannel,dest)) - return 0; - } - left-=count; - } - break; - } - } - } - } - - return result; -} - -static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) -{ - stbi_uc *result; - int i, x,y, internal_comp; - STBI_NOTUSED(ri); - - if (!comp) comp = &internal_comp; - - for (i=0; i<92; ++i) - stbi__get8(s); - - x = stbi__get16be(s); - y = stbi__get16be(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); - - stbi__get32be(s); //skip `ratio' - stbi__get16be(s); //skip `fields' - stbi__get16be(s); //skip `pad' - - // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); - memset(result, 0xff, x*y*4); - - if (!stbi__pic_load_core(s,x,y,comp, result)) { - STBI_FREE(result); - result=0; - } - *px = x; - *py = y; - if (req_comp == 0) req_comp = *comp; - result=stbi__convert_format(result,4,req_comp,x,y); - - return result; -} - -static int stbi__pic_test(stbi__context *s) -{ - int r = stbi__pic_test_core(s); - stbi__rewind(s); - return r; -} -#endif - -// ************************************************************************************************* -// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb - -#ifndef STBI_NO_GIF -typedef struct -{ - stbi__int16 prefix; - stbi_uc first; - stbi_uc suffix; -} stbi__gif_lzw; - -typedef struct -{ - int w,h; - stbi_uc *out; // output buffer (always 4 components) - stbi_uc *background; // The current "background" as far as a gif is concerned - stbi_uc *history; - int flags, bgindex, ratio, transparent, eflags; - stbi_uc pal[256][4]; - stbi_uc lpal[256][4]; - stbi__gif_lzw codes[8192]; - stbi_uc *color_table; - int parse, step; - int lflags; - int start_x, start_y; - int max_x, max_y; - int cur_x, cur_y; - int line_size; - int delay; -} stbi__gif; - -static int stbi__gif_test_raw(stbi__context *s) -{ - int sz; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; - sz = stbi__get8(s); - if (sz != '9' && sz != '7') return 0; - if (stbi__get8(s) != 'a') return 0; - return 1; -} - -static int stbi__gif_test(stbi__context *s) -{ - int r = stbi__gif_test_raw(s); - stbi__rewind(s); - return r; -} - -static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) -{ - int i; - for (i=0; i < num_entries; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - pal[i][3] = transp == i ? 0 : 255; - } -} - -static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) -{ - stbi_uc version; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') - return stbi__err("not GIF", "Corrupt GIF"); - - version = stbi__get8(s); - if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); - if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); - - stbi__g_failure_reason = ""; - g->w = stbi__get16le(s); - g->h = stbi__get16le(s); - g->flags = stbi__get8(s); - g->bgindex = stbi__get8(s); - g->ratio = stbi__get8(s); - g->transparent = -1; - - if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments - - if (is_info) return 1; - - if (g->flags & 0x80) - stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); - - return 1; -} - -static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); - if (!stbi__gif_header(s, g, comp, 1)) { - STBI_FREE(g); - stbi__rewind( s ); - return 0; - } - if (x) *x = g->w; - if (y) *y = g->h; - STBI_FREE(g); - return 1; -} - -static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) -{ - stbi_uc *p, *c; - int idx; - - // recurse to decode the prefixes, since the linked-list is backwards, - // and working backwards through an interleaved image would be nasty - if (g->codes[code].prefix >= 0) - stbi__out_gif_code(g, g->codes[code].prefix); - - if (g->cur_y >= g->max_y) return; - - idx = g->cur_x + g->cur_y; - p = &g->out[idx]; - g->history[idx / 4] = 1; - - c = &g->color_table[g->codes[code].suffix * 4]; - if (c[3] > 128) { // don't render transparent pixels; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } - g->cur_x += 4; - - if (g->cur_x >= g->max_x) { - g->cur_x = g->start_x; - g->cur_y += g->step; - - while (g->cur_y >= g->max_y && g->parse > 0) { - g->step = (1 << g->parse) * g->line_size; - g->cur_y = g->start_y + (g->step >> 1); - --g->parse; - } - } -} - -static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) -{ - stbi_uc lzw_cs; - stbi__int32 len, init_code; - stbi__uint32 first; - stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; - stbi__gif_lzw *p; - - lzw_cs = stbi__get8(s); - if (lzw_cs > 12) return NULL; - clear = 1 << lzw_cs; - first = 1; - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - bits = 0; - valid_bits = 0; - for (init_code = 0; init_code < clear; init_code++) { - g->codes[init_code].prefix = -1; - g->codes[init_code].first = (stbi_uc) init_code; - g->codes[init_code].suffix = (stbi_uc) init_code; - } - - // support no starting clear code - avail = clear+2; - oldcode = -1; - - len = 0; - for(;;) { - if (valid_bits < codesize) { - if (len == 0) { - len = stbi__get8(s); // start new block - if (len == 0) - return g->out; - } - --len; - bits |= (stbi__int32) stbi__get8(s) << valid_bits; - valid_bits += 8; - } else { - stbi__int32 code = bits & codemask; - bits >>= codesize; - valid_bits -= codesize; - // @OPTIMIZE: is there some way we can accelerate the non-clear path? - if (code == clear) { // clear code - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - avail = clear + 2; - oldcode = -1; - first = 0; - } else if (code == clear + 1) { // end of stream code - stbi__skip(s, len); - while ((len = stbi__get8(s)) > 0) - stbi__skip(s,len); - return g->out; - } else if (code <= avail) { - if (first) { - return stbi__errpuc("no clear code", "Corrupt GIF"); - } - - if (oldcode >= 0) { - p = &g->codes[avail++]; - if (avail > 8192) { - return stbi__errpuc("too many codes", "Corrupt GIF"); - } - - p->prefix = (stbi__int16) oldcode; - p->first = g->codes[oldcode].first; - p->suffix = (code == avail) ? p->first : g->codes[code].first; - } else if (code == avail) - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - - stbi__out_gif_code(g, (stbi__uint16) code); - - if ((avail & codemask) == 0 && avail <= 0x0FFF) { - codesize++; - codemask = (1 << codesize) - 1; - } - - oldcode = code; - } else { - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - } - } - } -} - -// this function is designed to support animated gifs, although stb_image doesn't support it -// two back is the image from two frames ago, used for a very specific disposal format -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) -{ - int dispose; - int first_frame; - int pi; - int pcount; - - // on first frame, any non-written pixels get the background colour (non-transparent) - first_frame = 0; - if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - g->background = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - g->history = (stbi_uc *) stbi__malloc(g->w * g->h); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); - - // image is treated as "tranparent" at the start - ie, nothing overwrites the current background; - // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to teh color that was there the previous frame. - memset( g->out, 0x00, 4 * g->w * g->h ); - memset( g->background, 0x00, 4 * g->w * g->h ); // state of the background (starts transparent) - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - first_frame = 1; - } else { - // second frame - how do we dispoase of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; - - if ((dispose == 3) && (two_back == 0)) { - dispose = 2; // if I don't have an image to revert back to, default to the old background - } - - if (dispose == 3) { // use previous graphic - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); - } - } - } else if (dispose == 2) { - // restore what was changed last frame to background before that frame; - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); - } - } - } else { - // This is a non-disposal case eithe way, so just - // leave the pixels as is, and they will become the new background - // 1: do not dispose - // 0: not specified. - } - - // background is what out is after the undoing of the previou frame; - memcpy( g->background, g->out, 4 * g->w * g->h ); - } - - // clear my history; - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - - for (;;) { - int tag = stbi__get8(s); - switch (tag) { - case 0x2C: /* Image Descriptor */ - { - stbi__int32 x, y, w, h; - stbi_uc *o; - - x = stbi__get16le(s); - y = stbi__get16le(s); - w = stbi__get16le(s); - h = stbi__get16le(s); - if (((x + w) > (g->w)) || ((y + h) > (g->h))) - return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); - - g->line_size = g->w * 4; - g->start_x = x * 4; - g->start_y = y * g->line_size; - g->max_x = g->start_x + w * 4; - g->max_y = g->start_y + h * g->line_size; - g->cur_x = g->start_x; - g->cur_y = g->start_y; - - g->lflags = stbi__get8(s); - - if (g->lflags & 0x40) { - g->step = 8 * g->line_size; // first interlaced spacing - g->parse = 3; - } else { - g->step = g->line_size; - g->parse = 0; - } - - if (g->lflags & 0x80) { - stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); - g->color_table = (stbi_uc *) g->lpal; - } else if (g->flags & 0x80) { - g->color_table = (stbi_uc *) g->pal; - } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - - o = stbi__process_gif_raster(s, g); - if (o == NULL) return NULL; - - // if this was the first frame, - pcount = g->w * g->h; - if (first_frame && (g->bgindex > 0)) { - // if first frame, any pixel not drawn to gets the background color - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi] == 0) { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); - } - } - } - - return o; - } - - case 0x21: // Comment Extension. - { - int len; - int ext = stbi__get8(s); - if (ext == 0xF9) { // Graphic Control Extension. - len = stbi__get8(s); - if (len == 4) { - g->eflags = stbi__get8(s); - g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. - - // unset old transparent - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 255; - } - if (g->eflags & 0x01) { - g->transparent = stbi__get8(s); - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 0; - } - } else { - // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; - } - } else { - stbi__skip(s, len); - break; - } - } - while ((len = stbi__get8(s)) != 0) { - stbi__skip(s, len); - } - break; - } - - case 0x3B: // gif stream termination code - return (stbi_uc *) s; // using '1' causes warning on some compilers - - default: - return stbi__errpuc("unknown code", "Corrupt GIF"); - } - } -} - -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - if (stbi__gif_test(s)) { - int layers = 0; - stbi_uc *u = 0; - stbi_uc *out = 0; - stbi_uc *two_back = 0; - stbi__gif g; - int stride; - memset(&g, 0, sizeof(g)); - if (delays) { - *delays = 0; - } - - do { - u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - - if (u) { - *x = g.w; - *y = g.h; - ++layers; - stride = g.w * g.h * 4; - - if (out) { - out = (stbi_uc*) STBI_REALLOC( out, layers * stride ); - if (delays) { - *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); - } - } else { - out = (stbi_uc*)stbi__malloc( layers * stride ); - if (delays) { - *delays = (int*) stbi__malloc( layers * sizeof(int) ); - } - } - memcpy( out + ((layers - 1) * stride), u, stride ); - if (layers >= 2) { - two_back = out - 2 * stride; - } - - if (delays) { - (*delays)[layers - 1U] = g.delay; - } - } - } while (u != 0); - - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - - // do the final conversion after loading everything; - if (req_comp && req_comp != 4) - out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - - *z = layers; - return out; - } else { - return stbi__errpuc("not GIF", "Image was not as a gif type."); - } -} - -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *u = 0; - stbi__gif g; - memset(&g, 0, sizeof(g)); - - u = stbi__gif_load_next(s, &g, comp, req_comp, 0); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - if (u) { - *x = g.w; - *y = g.h; - - // moved conversion to after successful load so that the same - // can be done for multiple frames. - if (req_comp && req_comp != 4) - u = stbi__convert_format(u, 4, req_comp, g.w, g.h); - } - - // free buffers needed for multiple frame loading; - STBI_FREE(g.history); - STBI_FREE(g.background); - - return u; -} - -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) -{ - return stbi__gif_info_raw(s,x,y,comp); -} -#endif - -// ************************************************************************************************* -// Radiance RGBE HDR loader -// originally by Nicolas Schulz -#ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s, const char *signature) -{ - int i; - for (i=0; signature[i]; ++i) - if (stbi__get8(s) != signature[i]) - return 0; - stbi__rewind(s); - return 1; -} - -static int stbi__hdr_test(stbi__context* s) -{ - int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); - stbi__rewind(s); - if(!r) { - r = stbi__hdr_test_core(s, "#?RGBE\n"); - stbi__rewind(s); - } - return r; -} - -#define STBI__HDR_BUFLEN 1024 -static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) -{ - int len=0; - char c = '\0'; - - c = (char) stbi__get8(z); - - while (!stbi__at_eof(z) && c != '\n') { - buffer[len++] = c; - if (len == STBI__HDR_BUFLEN-1) { - // flush to end of line - while (!stbi__at_eof(z) && stbi__get8(z) != '\n') - ; - break; - } - c = (char) stbi__get8(z); - } - - buffer[len] = 0; - return buffer; -} - -static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) -{ - if ( input[3] != 0 ) { - float f1; - // Exponent - f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); - if (req_comp <= 2) - output[0] = (input[0] + input[1] + input[2]) * f1 / 3; - else { - output[0] = input[0] * f1; - output[1] = input[1] * f1; - output[2] = input[2] * f1; - } - if (req_comp == 2) output[1] = 1; - if (req_comp == 4) output[3] = 1; - } else { - switch (req_comp) { - case 4: output[3] = 1; /* fallthrough */ - case 3: output[0] = output[1] = output[2] = 0; - break; - case 2: output[1] = 1; /* fallthrough */ - case 1: output[0] = 0; - break; - } - } -} - -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int width, height; - stbi_uc *scanline; - float *hdr_data; - int len; - unsigned char count, value; - int i, j, k, c1,c2, z; - const char *headerToken; - STBI_NOTUSED(ri); - - // Check identifier - headerToken = stbi__hdr_gettoken(s,buffer); - if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) - return stbi__errpf("not HDR", "Corrupt HDR image"); - - // Parse header - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); - - // Parse width and height - // can't use sscanf() if we're not using stdio! - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - height = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - width = (int) strtol(token, NULL, 10); - - *x = width; - *y = height; - - if (comp) *comp = 3; - if (req_comp == 0) req_comp = 3; - - if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) - return stbi__errpf("too large", "HDR image is too large"); - - // Read data - hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); - if (!hdr_data) - return stbi__errpf("outofmem", "Out of memory"); - - // Load image data - // image data is stored as some number of sca - if ( width < 8 || width >= 32768) { - // Read flat data - for (j=0; j < height; ++j) { - for (i=0; i < width; ++i) { - stbi_uc rgbe[4]; - main_decode_loop: - stbi__getn(s, rgbe, 4); - stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } - } else { - // Read RLE-encoded data - scanline = NULL; - - for (j = 0; j < height; ++j) { - c1 = stbi__get8(s); - c2 = stbi__get8(s); - len = stbi__get8(s); - if (c1 != 2 || c2 != 2 || (len & 0x80)) { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - stbi_uc rgbe[4]; - rgbe[0] = (stbi_uc) c1; - rgbe[1] = (stbi_uc) c2; - rgbe[2] = (stbi_uc) len; - rgbe[3] = (stbi_uc) stbi__get8(s); - stbi__hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - STBI_FREE(scanline); - goto main_decode_loop; // yes, this makes no sense - } - len <<= 8; - len |= stbi__get8(s); - if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) { - scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); - if (!scanline) { - STBI_FREE(hdr_data); - return stbi__errpf("outofmem", "Out of memory"); - } - } - - for (k = 0; k < 4; ++k) { - int nleft; - i = 0; - while ((nleft = width - i) > 0) { - count = stbi__get8(s); - if (count > 128) { - // Run - value = stbi__get8(s); - count -= 128; - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = stbi__get8(s); - } - } - } - for (i=0; i < width; ++i) - stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); - } - if (scanline) - STBI_FREE(scanline); - } - - return hdr_data; -} - -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int dummy; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (stbi__hdr_test(s) == 0) { - stbi__rewind( s ); - return 0; - } - - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) { - stbi__rewind( s ); - return 0; - } - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *y = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *x = (int) strtol(token, NULL, 10); - *comp = 3; - return 1; -} -#endif // STBI_NO_HDR - -#ifndef STBI_NO_BMP -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) -{ - void *p; - stbi__bmp_data info; - - info.all_a = 255; - p = stbi__bmp_parse_header(s, &info); - stbi__rewind( s ); - if (p == NULL) - return 0; - if (x) *x = s->img_x; - if (y) *y = s->img_y; - if (comp) *comp = info.ma ? 4 : 3; - return 1; -} -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) -{ - int channelCount, dummy, depth; - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - *y = stbi__get32be(s); - *x = stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 8 && depth != 16) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 3) { - stbi__rewind( s ); - return 0; - } - *comp = 4; - return 1; -} - -static int stbi__psd_is16(stbi__context *s) -{ - int channelCount, depth; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - (void) stbi__get32be(s); - (void) stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 16) { - stbi__rewind( s ); - return 0; - } - return 1; -} -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) -{ - int act_comp=0,num_packets=0,chained,dummy; - stbi__pic_packet packets[10]; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { - stbi__rewind(s); - return 0; - } - - stbi__skip(s, 88); - - *x = stbi__get16be(s); - *y = stbi__get16be(s); - if (stbi__at_eof(s)) { - stbi__rewind( s); - return 0; - } - if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi__rewind( s ); - return 0; - } - - stbi__skip(s, 8); - - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return 0; - - packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - act_comp |= packet->channel; - - if (stbi__at_eof(s)) { - stbi__rewind( s ); - return 0; - } - if (packet->size != 8) { - stbi__rewind( s ); - return 0; - } - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); - - return 1; -} -#endif - -// ************************************************************************************************* -// Portable Gray Map and Portable Pixel Map loader -// by Ken Miller -// -// PGM: http://netpbm.sourceforge.net/doc/pgm.html -// PPM: http://netpbm.sourceforge.net/doc/ppm.html -// -// Known limitations: -// Does not support comments in the header section -// Does not support ASCII image data (formats P2 and P3) -// Does not support 16-bit-per-channel - -#ifndef STBI_NO_PNM - -static int stbi__pnm_test(stbi__context *s) -{ - char p, t; - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); - return 0; - } - return 1; -} - -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - STBI_NOTUSED(ri); - - if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) - return 0; - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - - if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "PNM too large"); - - out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y); - - if (req_comp && req_comp != s->img_n) { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - return out; -} - -static int stbi__pnm_isspace(char c) -{ - return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; -} - -static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) -{ - for (;;) { - while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) - *c = (char) stbi__get8(s); - - if (stbi__at_eof(s) || *c != '#') - break; - - while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) - *c = (char) stbi__get8(s); - } -} - -static int stbi__pnm_isdigit(char c) -{ - return c >= '0' && c <= '9'; -} - -static int stbi__pnm_getinteger(stbi__context *s, char *c) -{ - int value = 0; - - while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { - value = value*10 + (*c - '0'); - *c = (char) stbi__get8(s); - } - - return value; -} - -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) -{ - int maxv, dummy; - char c, p, t; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - stbi__rewind(s); - - // Get identifier - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind(s); - return 0; - } - - *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm - - c = (char) stbi__get8(s); - stbi__pnm_skip_whitespace(s, &c); - - *x = stbi__pnm_getinteger(s, &c); // read width - stbi__pnm_skip_whitespace(s, &c); - - *y = stbi__pnm_getinteger(s, &c); // read height - stbi__pnm_skip_whitespace(s, &c); - - maxv = stbi__pnm_getinteger(s, &c); // read max value - - if (maxv > 255) - return stbi__err("max value > 255", "PPM image not 8-bit"); - else - return 1; -} -#endif - -static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) -{ - #ifndef STBI_NO_JPEG - if (stbi__jpeg_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNG - if (stbi__png_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_GIF - if (stbi__gif_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_BMP - if (stbi__bmp_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PIC - if (stbi__pic_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNM - if (stbi__pnm_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_info(s, x, y, comp)) return 1; - #endif - - // test tga last because it's a crappy test! - #ifndef STBI_NO_TGA - if (stbi__tga_info(s, x, y, comp)) - return 1; - #endif - return stbi__err("unknown image type", "Image not of any known type, or corrupt"); -} - -static int stbi__is_16_main(stbi__context *s) -{ - #ifndef STBI_NO_PNG - if (stbi__png_is16(s)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_is16(s)) return 1; - #endif - - return 0; -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_info_from_file(f, x, y, comp); - fclose(f); - return result; -} - -STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__info_main(&s,x,y,comp); - fseek(f,pos,SEEK_SET); - return r; -} - -STBIDEF int stbi_is_16_bit(char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_is_16_bit_from_file(f); - fclose(f); - return result; -} - -STBIDEF int stbi_is_16_bit_from_file(FILE *f) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__is_16_main(&s); - fseek(f,pos,SEEK_SET); - return r; -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__is_16_main(&s); -} - -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__is_16_main(&s); -} - -#endif // STB_IMAGE_IMPLEMENTATION - -/* - revision history: - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug - 1-bit BMP - *_is_16_bit api - avoid warnings - 2.16 (2017-07-23) all functions have 16-bit variants; - STBI_NO_STDIO works again; - compilation fixes; - fix rounding in unpremultiply; - optimize vertical flip; - disable raw_len validation; - documentation fixes - 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; - warning fixes; disable run-time SSE detection on gcc; - uniform handling of optional "return" values; - thread-safe initialization of zlib tables - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) allocate large structures on the stack - remove white matting for transparent PSD - fix reported channel count for PNG & BMP - re-enable SSE2 in non-gcc 64-bit - support RGB-formatted JPEG - read 16-bit PNGs (only as 8-bit) - 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED - 2.09 (2016-01-16) allow comments in PNM files - 16-bit-per-pixel TGA (not bit-per-component) - info() for TGA could break due to .hdr handling - info() for BMP to shares code instead of sloppy parse - can use STBI_REALLOC_SIZED if allocator doesn't support realloc - code cleanup - 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA - 2.07 (2015-09-13) fix compiler warnings - partial animated GIF support - limited 16-bpc PSD support - #ifdef unused functions - bug with < 92 byte PIC,PNM,HDR,TGA - 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value - 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning - 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit - 2.03 (2015-04-12) extra corruption checking (mmozeiko) - stbi_set_flip_vertically_on_load (nguillemot) - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) - progressive JPEG (stb) - PGM/PPM support (Ken Miller) - STBI_MALLOC,STBI_REALLOC,STBI_FREE - GIF bugfix -- seemingly never worked - STBI_NO_*, STBI_ONLY_* - 1.48 (2014-12-14) fix incorrectly-named assert() - 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) - optimize PNG (ryg) - fix bug in interlaced PNG with user-specified channel count (stb) - 1.46 (2014-08-26) - fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG - 1.45 (2014-08-16) - fix MSVC-ARM internal compiler error by wrapping malloc - 1.44 (2014-08-07) - various warning fixes from Ronny Chevalier - 1.43 (2014-07-15) - fix MSVC-only compiler problem in code changed in 1.42 - 1.42 (2014-07-09) - don't define _CRT_SECURE_NO_WARNINGS (affects user code) - fixes to stbi__cleanup_jpeg path - added STBI_ASSERT to avoid requiring assert.h - 1.41 (2014-06-25) - fix search&replace from 1.36 that messed up comments/error messages - 1.40 (2014-06-22) - fix gcc struct-initialization warning - 1.39 (2014-06-15) - fix to TGA optimization when req_comp != number of components in TGA; - fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) - add support for BMP version 5 (more ignored fields) - 1.38 (2014-06-06) - suppress MSVC warnings on integer casts truncating values - fix accidental rename of 'skip' field of I/O - 1.37 (2014-06-04) - remove duplicate typedef - 1.36 (2014-06-03) - convert to header file single-file library - if de-iphone isn't set, load iphone images color-swapped instead of returning NULL - 1.35 (2014-05-27) - various warnings - fix broken STBI_SIMD path - fix bug where stbi_load_from_file no longer left file pointer in correct place - fix broken non-easy path for 32-bit BMP (possibly never used) - TGA optimization by Arseny Kapoulkine - 1.34 (unknown) - use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case - 1.33 (2011-07-14) - make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements - 1.32 (2011-07-13) - support for "info" function for all supported filetypes (SpartanJ) - 1.31 (2011-06-20) - a few more leak fixes, bug in PNG handling (SpartanJ) - 1.30 (2011-06-11) - added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) - removed deprecated format-specific test/load functions - removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway - error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) - fix inefficiency in decoding 32-bit BMP (David Woo) - 1.29 (2010-08-16) - various warning fixes from Aurelien Pocheville - 1.28 (2010-08-01) - fix bug in GIF palette transparency (SpartanJ) - 1.27 (2010-08-01) - cast-to-stbi_uc to fix warnings - 1.26 (2010-07-24) - fix bug in file buffering for PNG reported by SpartanJ - 1.25 (2010-07-17) - refix trans_data warning (Won Chun) - 1.24 (2010-07-12) - perf improvements reading from files on platforms with lock-heavy fgetc() - minor perf improvements for jpeg - deprecated type-specific functions so we'll get feedback if they're needed - attempt to fix trans_data warning (Won Chun) - 1.23 fixed bug in iPhone support - 1.22 (2010-07-10) - removed image *writing* support - stbi_info support from Jetro Lauha - GIF support from Jean-Marc Lienher - iPhone PNG-extensions from James Brown - warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) - 1.21 fix use of 'stbi_uc' in header (reported by jon blow) - 1.20 added support for Softimage PIC, by Tom Seddon - 1.19 bug in interlaced PNG corruption check (found by ryg) - 1.18 (2008-08-02) - fix a threading bug (local mutable static) - 1.17 support interlaced PNG - 1.16 major bugfix - stbi__convert_format converted one too many pixels - 1.15 initialize some fields for thread safety - 1.14 fix threadsafe conversion bug - header-file-only version (#define STBI_HEADER_FILE_ONLY before including) - 1.13 threadsafe - 1.12 const qualifiers in the API - 1.11 Support installable IDCT, colorspace conversion routines - 1.10 Fixes for 64-bit (don't use "unsigned long") - optimized upsampling by Fabian "ryg" Giesen - 1.09 Fix format-conversion for PSD code (bad global variables!) - 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz - 1.07 attempt to fix C++ warning/errors again - 1.06 attempt to fix C++ warning/errors again - 1.05 fix TGA loading to return correct *comp and use good luminance calc - 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free - 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR - 1.02 support for (subset of) HDR files, float interface for preferred access to them - 1.01 fix bug: possible bug in handling right-side up bmps... not sure - fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all - 1.00 interface to zlib that skips zlib header - 0.99 correct handling of alpha in palette - 0.98 TGA loader by lonesock; dynamically add loaders (untested) - 0.97 jpeg errors on too large a file; also catch another malloc failure - 0.96 fix detection of invalid v value - particleman@mollyrocket forum - 0.95 during header scan, seek to markers in case of padding - 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same - 0.93 handle jpegtran output; verbose errors - 0.92 read 4,8,16,24,32-bit BMP files of several formats - 0.91 output 24-bit Windows 3.0 BMP files - 0.90 fix a few more warnings; bump version number to approach 1.0 - 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd - 0.60 fix compiling as c++ - 0.59 fix warnings: merge Dave Moore's -Wall fixes - 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian - 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available - 0.56 fix bug: zlib uncompressed mode len vs. nlen - 0.55 fix bug: restart_interval not initialized to 0 - 0.54 allow NULL for 'int *comp' - 0.53 fix bug in png 3->4; speedup png decoding - 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments - 0.51 obey req_comp requests, 1-component jpegs return as 1-component, - on 'test' only check type, not whether we support this variant - 0.50 (2006-11-19) - first released version -*/ - - -/* ------------------------------------------------------------------------------- -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. ------------------------------------------------------------------------------- -*/ diff --git a/include/progress_bar.h b/include/progress_bar.h index 04547c0..5c28245 100644 --- a/include/progress_bar.h +++ b/include/progress_bar.h @@ -1,6 +1,6 @@ -#ifndef PROGRESS_BAR_H -#define PROGRESS_BAR_H +#ifndef _3D_SHELL_PROGRESS_BAR_H +#define _3D_SHELL_PROGRESS_BAR_H -void ProgressBar_DisplayProgress(char * msg, char * src, u32 offset, u32 size); +void ProgressBar_DisplayProgress(char *msg, char *src, u32 offset, u32 size); #endif \ No newline at end of file diff --git a/include/misc/screenshot.h b/include/screenshot.h similarity index 100% rename from include/misc/screenshot.h rename to include/screenshot.h diff --git a/include/status_bar.h b/include/status_bar.h new file mode 100644 index 0000000..ef37eb1 --- /dev/null +++ b/include/status_bar.h @@ -0,0 +1,6 @@ +#ifndef _3D_SHELL_STATUS_BAR_H +#define _3D_SHELL_STATUS_BAR_H + +void StatusBar_DisplayTime(void); + +#endif \ No newline at end of file diff --git a/include/textures.h b/include/textures.h index 4315b91..6926792 100644 --- a/include/textures.h +++ b/include/textures.h @@ -1,80 +1,19 @@ -#ifndef TEXTURES_H -#define TEXTURES_H - -#define TEXTURE_BACKGROUND 0 -#define TEXTURE_SELECTOR 1 - -#define TEXTURE_ICON_APP 2 -#define TEXTURE_ICON_ARCHIVE 3 -#define TEXTURE_ICON_AUDIO 4 -#define TEXTURE_ICON_FILE 5 -#define TEXTURE_ICON_FOLDER 6 -#define TEXTURE_ICON_IMG 7 -#define TEXTURE_ICON_SYSTEM 8 -#define TEXTURE_ICON_TEXT 9 - -#define TEXTURE_ICON_CHECK 10 -#define TEXTURE_ICON_UNCHECK 11 - -#define TEXTURE_DIALOG 12 -#define TEXTURE_OPTIONS 13 -#define TEXTURE_PROPERTIES 14 - -#define TEXTURE_HOME_ICON 15 -#define TEXTURE_OPTIONS_ICON 16 -#define TEXTURE_SETTINGS_ICON 17 -#define TEXTURE_FTP_ICON 18 -#define TEXTURE_UPDATE_ICON 19 - -#define TEXTURE_HOME_ICON_SELECTED 20 -#define TEXTURE_OPTIONS_ICON_SELECTED 21 -#define TEXTURE_SETTINGS_ICON_SELECTED 22 -#define TEXTURE_FTP_ICON_SELECTED 23 -#define TEXTURE_UPDATE_ICON_SELECTED 24 - -#define TEXTURE_SD_ICON 25 -#define TEXTURE_NAND_ICON 26 - -#define TEXTURE_SD_ICON_SELECTED 27 -#define TEXTURE_NAND_ICON_SELECTED 28 - -#define TEXTURE_TOGGLE_ON 29 -#define TEXTURE_TOGGLE_OFF 30 -#define TEXTURE_RADIO_ON 31 -#define TEXTURE_RADIO_OFF 32 - -#define TEXTURE_THEMES_LAUNCHER 33 - -#define TEXTURE_SEARCH_ICON 34 - -#define TEXTURE_BATTERY_0 35 -#define TEXTURE_BATTERY_15 36 -#define TEXTURE_BATTERY_28 37 -#define TEXTURE_BATTERY_43 38 -#define TEXTURE_BATTERY_57 39 -#define TEXTURE_BATTERY_71 40 -#define TEXTURE_BATTERY_85 41 -#define TEXTURE_BATTERY_100 42 -#define TEXTURE_BATTERY_CHARGE 43 - -#define TEXTURE_WIFI_NULL 44 -#define TEXTURE_WIFI_0 45 -#define TEXTURE_WIFI_1 46 -#define TEXTURE_WIFI_2 47 -#define TEXTURE_WIFI_3 48 - -#define TEXTURE_GALLERY_BAR 49 -#define TEXTURE_GALLERY_IMAGE 50 - -#define TEXTURE_MUSIC_TOP_BG 51 -#define TEXTURE_MUSIC_BOTTOM_BG 52 -#define TEXTURE_MUSIC_PLAY 53 -#define TEXTURE_MUSIC_PAUSE 54 -#define TEXTURE_MUSIC_COVER 55 -#define TEXTURE_MUSIC_STATUS 56 - -#define TEXTURE_TEXTVIEWER_BG 57 - -void Load_Textures(void); +#ifndef _3D_SHELL_TEXTURES_H +#define _3D_SHELL_TEXTURES_H + +#include + +C2D_Image icon_app, icon_archive, icon_audio, icon_dir, icon_file, icon_image, icon_text, icon_dir_dark, \ + icon_doc, icon_check, icon_uncheck, icon_check_dark, icon_uncheck_dark, icon_radio_off, icon_radio_on, \ + icon_radio_dark_off, icon_radio_dark_on, icon_toggle_on, icon_toggle_dark_on, icon_toggle_off, \ + dialog, options_dialog, properties_dialog, dialog_dark, options_dialog_dark, properties_dialog_dark, \ + icon_home, icon_home_dark, icon_options, icon_options_dark, icon_settings, icon_settings_dark, \ + icon_ftp, icon_ftp_dark, icon_sd, icon_sd_dark, icon_secure, icon_secure_dark, icon_search, \ + default_artwork, default_artwork_blur, btn_play, btn_pause, btn_rewind, btn_forward, \ + btn_repeat, btn_shuffle, btn_repeat_overlay, btn_shuffle_overlay, \ + icon_nav_drawer, icon_actions, icon_back; + +void Textures_Load(void); +void Textures_Free(void); #endif \ No newline at end of file diff --git a/include/theme.h b/include/theme.h deleted file mode 100644 index db7da6b..0000000 --- a/include/theme.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef THEME_H -#define THEME_H - -#include <3ds.h> - -char temp_arr[250], theme_dir[250], colour_dir[250]; - -char background_path[250], dialog_path[250], options_path[250], properties_path[250], selector_path[250]; -char app_path[250], archive_path[250], audio_path[250], img_path[250], file_path[250], folder_path[250], system_path[250], text_path[250]; -char check_path[250], uncheck_path[250]; - -char bottomScreen_path[250], bottomScreen_bar_path[250], bottomScreen_text_path[250], options_select_path[250], options_text_path[250], - options_title_text_path[250], settings_colour_path[250], settings_text_path[250], settings_text_min_path[250], settings_title_text_path[250], - storage_path[250], topScreen_path[250], topScreen_bar_path[250], topScreen_min_path[250]; - -struct colour -{ - int r; - int g; - int b; -}; - -Result Theme_SaveConfig(char * themePath, char * coloursPath); -void Theme_Load(void); -void Theme_Reload(void); - -#endif \ No newline at end of file diff --git a/include/touch.h b/include/touch.h new file mode 100644 index 0000000..eca3239 --- /dev/null +++ b/include/touch.h @@ -0,0 +1,9 @@ +#ifndef _3D_SHELL_TOUCH_H +#define _3D_SHELL_TOUCH_H + +#define TouchInRect(x1, y1, x2, y2) ((Touch_GetX() >= (x1) && Touch_GetX() <= (x2)) && (Touch_GetY() >= (y1) && Touch_GetY() <= (y2))) + +u16 Touch_GetX(void); +u16 Touch_GetY(void); + +#endif \ No newline at end of file diff --git a/include/utils.h b/include/utils.h index c313cb1..32a0ac3 100644 --- a/include/utils.h +++ b/include/utils.h @@ -1,18 +1,13 @@ -#ifndef UTILS_H -#define UTILS_H +#ifndef _3D_SHELL_UTILS_H +#define _3D_SHELL_UTILS_H -#include <3ds.h> - -void Utils_MakeDirectories(void); -Result Utils_SaveConfig(int sortBy, bool recycleBin, bool galleryDisplay, bool hidden); -Result Utils_LoadConfig(void); -Result Utils_GetLastDirectory(void); -char * Utils_Basename(const char * filename); -void Utils_GetSizeString(char * string, uint64_t size); -CFG_Region Utils_GetRegion(void); -CFG_Language Utils_GetLanguage(void); -const char * Utils_GetUsername(void); bool Utils_IsN3DS(void); -void u16_to_u8(char * buf, const u16 * input, size_t bufsize); +void Utils_U16_To_U8(char *buf, const u16 *input, size_t bufsize); +char *Utils_Basename(const char *filename); +void Utils_GetSizeString(char *string, u64 size); +void Utils_SetMax(int *set, int value, int max); +void Utils_SetMin(int *set, int value, int min); +int Utils_Alphasort(const void *p1, const void *p2); +void Utils_AppendArr(char subject[], const char insert[], int pos); #endif \ No newline at end of file diff --git a/res/drawable/btn_material_light_check_off_normal.png b/res/drawable/btn_material_light_check_off_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..a7161a0e6a4ec103235a63e22368cac4ac283809 GIT binary patch literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAd3?%E9GuQzsp#Yx{S0HU*U|<9TKz3((zamhS ztt7}Vm_Z;swo$O;!en8fh^nWHV@SoEzv05vdpy85}Sb4q9e0J_>DqyPW_ literal 0 HcmV?d00001 diff --git a/res/drawable/btn_material_light_check_off_normal_dark.png b/res/drawable/btn_material_light_check_off_normal_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..aa346fe076807dc568fe9ea5a9dedd239cbeb28c GIT binary patch literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAd3?%E9GuQzsp#Yx{S0McX6`Yy&@HbGDtt7}V zm_Z;swo$O;!en8fh^nWHV@SoEzv05vdpy85}Sb4q9e05@1KZ~y=R literal 0 HcmV?d00001 diff --git a/res/drawable/btn_material_light_check_on_normal.png b/res/drawable/btn_material_light_check_on_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..b44bf841d498eb74427d4b99a8134a5a29ca46f1 GIT binary patch literal 301 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQaQ2{<7u0Yzrz`zI<=;`S}1oZXw zQDuw_!2nqht{5l-6o-&NGsX0}X8}!@D+%%oW)KLEoi($Sr!I;ksmr=IjxDrLO`)58 zg0e`D+El@f!8_D|DpNdN978JRBquBoOh`#;V^VY5p&-mHV{njl=0c@4Icv_mXj*gT zjSP^S6LaT;R%OM{h=?DoERm5DSXx_LU49;QkeaZy+uMVyf!EP6*ehxRtMdfcHc?-J k)sIsJ?@dwFp3}j=u(y|a!itoX0-${ip00i_>zopr0EPNh+5i9m literal 0 HcmV?d00001 diff --git a/res/drawable/btn_material_light_check_on_normal_dark.png b/res/drawable/btn_material_light_check_on_normal_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..478b01dbac9d20ea08f187a990ebdb0310116cb1 GIT binary patch literal 297 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQa;Q>A&u0Z+&4S-Ja`e2~ZvL!)& z!3+Z7v9o5j^3+9fBz0N$#<7L=sVQ``Pf!-=nJQSscES&+FvZiwF{ENna>4?^gp{N< zCN;Mm3c}nn1_xPZE>v2Rv*yf;rZs2Y$Nyfc zsR>)Vy*;=ZcpVLcy&@*CI!|z&CF(1%`f;kjy(x;?b2=Cpw)-)+p4NJ^185(Er>mdK II;Vst089>(^#A|> literal 0 HcmV?d00001 diff --git a/res/drawable/btn_material_light_radio_off_normal.png b/res/drawable/btn_material_light_radio_off_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..97ad84ba56b1cab12d205c654b94fe939fd63b34 GIT binary patch literal 824 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=3?wxlRx|^tKLI`=u0UE_Tiejk(Ae162nNu& zFkvHoeSJ6^$S^Q40E$4g1GOMaA+wQ91G0g-AS6%?#8M!MtOlq6XePu2p!3w#)qx6t zg2*m3F)@K~_4M>0=IH9`0u2GOfg}(B#erHNhUn<%Km;Kc0og!tpaL!5c|Jf3LP~=C zf*F|kG#mq?WrGV_`=-upVwVq_HcO6~&$DL6q=xKBzv(qr%n1{-%%%9*7<($Um@;HF zm@2CnnX~yUCf8>PkunVtXc?%mz$JjK^I zPba13mbD+wOI|zezTlk8M|ZRKyBAc)`n`MpzV=^3N4^mALGENFkv^Bk2%CvhmZUWG zGp;OAOL0kgDx>g(`)E*P;3?I06T}<3=9sQfd_6bh>aBY#SBCEOpBZ&r{G4J~zRaxN zT^%tu+#UZ2c6?n{mXURKT1Ux*CYD7j%(f(l2jBK!J;9>x6u?>ME~+5(e)(&bPo|f= z*0NjdZfV`ey^(3>zo!iA><*?^#)zKuFyx!3`oqk8op;KUXJUEM2e)%e+?zPDNT_M< z18bWJB1VfmK3M!?H@rBb^X=xi4^zxfeEcwp@8*%vSrQV$$tMkzYk#~ye*9g9(NaEp zz9cizCv+L{h}X-yYs^A={yDkzoA7FEKG|MN{`a zimR&9(Cu~dT>p7j_r9|MliH-~J_y#bnQp6^-P^}JZCC%^e^QU5OMlO|@3#AR?!1-g ZUgmkqMvp&PX@T+xgQu&X%Q~loCIEvU7vKN@ literal 0 HcmV?d00001 diff --git a/res/drawable/btn_material_light_radio_off_normal_dark.png b/res/drawable/btn_material_light_radio_off_normal_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..72ab5b1e93676c4d4848806d5169673e46f01b1a GIT binary patch literal 683 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=3?wxlRx|^tbpbvhu0Z<#&;hz#U$lTOwkiqo z3ua*E({K!omJKd!?VCEYiCsQy+AKNGni-QCvLpSbTO~})GMD0KW9+HaV#=tpm|QQA z8xgKut=?_s_1Z9?pMim~$kW9!q+-t7sgcD^4gxJZrzZz46cr3~VPWmBnEmI!z1W3S zDzjLXr%qoo=eXXks`T<_&5!4O?e7+M@eC7*6k`3C&bKIQm!t1wDPCXI@B8z&=}5<) z+IFjaQcK@u);sgAfB(C0`R)S;B)O)>vM4vSJcZqHhtl@(uV;F=y{EX3eZ`t+VUr!nW&es;drLDH^-b@{?X0@s+NLMTp`LTPirG2o zh0#&7wCG26E6!XH6|Ot9O~uaVX}OLmU)bW>jf{I!*gN~!UQf!lN$qW1X*f-0dout3 ze*sp1ri)tNTX*B5fkwPxorP}L|5vt?|1Rvlyg@`FJw|6;;l%Wi2alM&)?eJ!aA}s# zUKy|DS{hF+^Tog9Zriv2?u*sGUuZMzt`^wfYTNa(*q#5s&b+VYuVs7psj=-(Ruuxq OA%mx@pUXO@geCx&*@K?| literal 0 HcmV?d00001 diff --git a/res/drawable/btn_material_light_radio_on_normal.png b/res/drawable/btn_material_light_radio_on_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..c98957d001a946d412cad0d65f491a0233cf2594 GIT binary patch literal 1007 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=3?wxlRx~p(Fd7B;gt!8^+S=NNhK9z*#zruJ z#)SzR>Fevm*+7PYfdNniq8+FOSqhnrY#NXa)CD1dY9N*ZNn|xZ1wb=_vZkh{5Ob`p zt$_@n8X$n^0ynGivs zN}xudG9b{=(SZm;oCi?`R8Y?Py%lJET1k*!FatB6hGSr~Y;a*~-_)5+?DApLX2~)0 zdDhIB)Q}zNH@(J+IbmX!xfDMeV^5_PQ--VtQ>B1L5LXqKeOD_Z^Mq_Zi^=tZU0GTJ zxe;u^+-=+r+~KlJ+ST&v%vM_6d}ab{7GAmRC$>83F)%Q(dAc};RLprhH9Pp0gGk$A zMkYnZldF{^nxxiEWQbcJ#Oe}Vy)axPFgkzj-~aUsci(WhbaP3`viUOSr{CQ(IsM!` z+k8KZcb4-C&Yb<+n4lrT^Wk&xW`MEv;u`(tYhleRmfQ`%jNH8 zs?R&47`uA+(JEg@J<0D5VqpueY}`nf(>Gx;st+8X9`0dAIb@ieT zGd-F*BwkNaU;D?XSX;eK(t_9cK@E@b)~5%oHs*SA=0_LZ(KP&jbHcm2+4HX*OwT|aG4JmufN7Ay)78&qol`;+0P(D9*#H0l literal 0 HcmV?d00001 diff --git a/res/drawable/btn_material_light_radio_on_normal_dark.png b/res/drawable/btn_material_light_radio_on_normal_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..d025a387caf74864173a1e784aff0af68b047f5c GIT binary patch literal 809 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=3?wxlRx|^tlL0;MektaV`n+ z3ua*E({K!omJKd!?VCEYiCsQy+AKNGni-QCvLpSbTO~})GMD0KW9+HaV#*K*;;ORm znqV=xUNAR;yUih7yIQ^5%!}3ZRYH|>831bbv;BlP$W0+Eh zTW{yA6_)@0FW$6xX#=ZVXr-stXQL}u%FbTB>~U%T-$d^dIy1eN2YafiuXEPOYD+hb zy}oH@&#IWZ`TMmcIA#Ry*gwHjZ`vt`rCHZ>R;PX93eY!wvN_fyy5ZWplIq&q@4ip* zi)NOamHz33Z{W!Tg?~F<^SI1Cd&A~u68F@Ho`+9ea9BN~zwyr01fH4Fe7k&}Xx=^U zz?I4~MfOm#@@1KgANOr@zruZy<7mUP3&$&ru4W#vnlEso%tT`1Z_gDDvx|Dx-b~as z?lxG*xgs!lvhj+N7s*DS%`ImKJjv|yxpG0`cyaiT>6rz;gZ*1}g%*f0dZt}`$NBz+ zV~3yrUe-+~IXt`$Z_?k>HKEsKMSSzLu7)M05!*hbNhDo5qx0hB{6-lr>Dc%i3v0`2 zf9tUOS;y^VQ>;;S;&Ru^I&nO7U+msf%86l)hXN)=9Qv^(=I0g_7aLvG%v!HHCXaV% z(%%36cJ7@&@5b+Ka`j1_$C&5tlkK@VJ6BS^H{#%plSh*y^j!LkiwZ@So?qv_=!vHP zGJ&Q2#fwxv)x2Grzj4c&r%mVez2!OI{Bmnl-5q+YT=wpt%|Eu^&%B>&ViP}o&i`0o PGGXv^^>bP0l+XkKnbFi3 literal 0 HcmV?d00001 diff --git a/res/drawable/btn_material_light_toggle_off_normal.png b/res/drawable/btn_material_light_toggle_off_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..a3c43d513b6a0d60238ec8955b7428f3258edbdd GIT binary patch literal 1568 zcmV+*2H*LKP)RJ*&gSn+W zobQ}_hY$)13JMAe3JMAe3JMAe3JQvsiZ^szw~YF8YZ^Gg^}r240dNb@9_XMuZU(-k zyS|>RF+uci{7u45ROI91v%9&u`OxXprxUxoyAwM*JLR>twK8XC=Us4pE6`T=|1}{7 zPJR<`OMHC%$CZ_p5jSq!Xw_&mBU-I?abaQMzK(zV4#4wxy|lD6?AWnm62e6Y-me$K zRK7Iez%lH1G&D3Ex^w4F-{RupgM0VxF)t$g@2~0U>5-b68adqm0q`B54dK!Sx#vA^!I-eU^!=JXEm2DU8;|Yijw*J`}+k2 z1xXJcJm?u79_|qq7A9VS9(WBXf&WS1C=Lk;*&7fL;07t^B9%&aiA17pg!3aIdFDU7 z163A8L`1l%)#^r=%u{10Z%BngF%MBOR$gANs;{sArLnP5+0@jeFeIGEqf*Ba-MJx% z#4D#xo$3eQ`LVFD@I+c#+82?Lk-pZ}*6$K7fGGi<8g6u!S?p{+jQgRMFSLg3B9^z`)ockkXE<5K`Vp@6W1 zhtOs^Iy!g}0tw~%W>Qj8yg(qp^e`(T*eHNd`x>I)PfLh^yng*UD*|89pi7TO;5ip$ zGT8wuD=SBshn1-!pn~r~C786707!T~8wFr`W_yDntE#Gcf`fy-2v<4Q87lyr3RziM z2VkI2EGYtd!BE298@&03=i#roX?R0rO?r z+uL~+0n?(mxcGbMNA?n~;WEk_Py|OPi^Y}}0eSiIWv05i8r_{&5r9uBDk{38qoel| zE+vc-0gdGC(b3WB)evEDaL{5RfG?br%jE|M*QT2!Le;At39!gJ3k}q#rig&)V1M=M z)x6aZ0oD@?lr-K&08e;aR#sMz>kwjsCtx|Tf$l2`dW46T6acHk`Sa)5&XI3pgx+H| zBO~J*CnqOtqc<2MfF9U#I*pHyU$LYJQ&UrH1Yn=QbBSbA!JIw;!TJJ(DyD?lE60B$ zt{-5qympKz0EntSa^%S8LZMJZxE{fz)){ZI&CAOR)M~X0mJk4YgucE$rmn7z*C+r% zXL53KzO}Qnb0XX(j9H5)%%iQ)%w)iTdF>p*YnWTNZZQoF4cOW7GI{WnMG*Ao)YR12 z9RR}9a+)>B#Kc68$;rtIbOX!QS59{(gRbp8&#5w029NG!C+ zxLgR^%*;$Ls2Eu@Gcz}h=pmls7JPGa^Ork!?sO*H+rqM?-#K2sdu$9h=*G1wyUlv6 z*A3n=J3HGOnw(THx3lx}^S|Nu1{4Li;c$x`cpuKsOiWB%?CI%ADJUrTjBsD4R{HMn z$^li!y32|Y7#QdZu% literal 0 HcmV?d00001 diff --git a/res/drawable/btn_material_light_toggle_on_normal.png b/res/drawable/btn_material_light_toggle_on_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..f7b01b10445f50b4efd537ab3cd99911ce170bb4 GIT binary patch literal 1694 zcmV;P24VS$P) zK@2I{I4hvYkS&l{M;uuIbSB|BonVRN<-d{y?HL+`BH@CC&NeQ=!tGlQ&IQ>2v z2A_+P+qwiXSBqi%WQz76V|@ytSuN~-Tr;+%cuwyYD)UE5E#^lm?O>pndD zx1YS#Pd{7wh9Zpl7{-6Pes|?V_ifdWCMtS#7C_Ab2$Rn^fw=)NR}MvcDkH#Zns5cS zwqC)}^J5Fo?8_`|?B034^J)Hv-AGsNJl}<~%Wk8DLp5klcI!wCe~F@$j!)QLu^tqi zRScOB0jp^hureudOHt*q9cS9U1|$9)i2#^+XFE_vNj-|#Q->yP>tj>|aeDuA{<_Qg zFmEwM?^ikldw{W#u?b)17w(l=+R({_@ft6HkU_D<*O4@PH0d24O+y7VN#3vc+56SxC!k`}s%U}$M=!}W{W9GH2$ zv6rbZphKZk6$)$iRqN3U{>~Qs)fX};I%R5|DU2Q6eC^ifmSpFh>v+PL7vNAh*NGsN zqR8E~$Tq!S-y$GkLgQtyM@Z2jp%qMByh9u}?>Tpv$-RIUp#y<;p|HX_y$hfcA?bD_ z*fW9BGE3mtVL`R$3>#uOi11t=A}lQ!9T7xZ?{tGK)VQRf6;NpVg8MM*|iliQx z`~YLw0X9ZBQ=k=lZ=lI~&kj&IuDO^+(FK!E=Lj>wgsGCa!t#T;9>+^-|c5_IJlQ+xwCXSFfA7o>DN-~{COQ*^71k$!>( zFFH&1&hFCixZH@CyyF?EhpNgmO6qHY^}t5RCQTV74UMm4X+v{nX=4jK@1!g1+EWf) z>xnL?cp#HkJrv7(9*f(m&kD%zLtzMh08Zk88X8g}I9cOJgxI2wg7bSf|UOd4Bd+xae zfq{X6fq{X6fq{X6fq{X6f$`kJLeB|*qs6~DsIdYHlmWB`+5y?XDSD!37H|^Kig*qM zu_UPA9DvRYI~La?YiR~Yk)!4nudn2lY^>ra)~TJML&M?wX+S67WZ*<%oQPpD3Zj`u zfv{~n-96mW6XJQLx%&ha#qWr!%Dxm;Z~akRt$HBRs2-wc(Vo)J_~nZ8Ub0Muv<_41>1P4;;-LQ%`F>ZH7EQi#B>9Ya^R`I44A<%uwf_gg_sNYt-Zj zI3TPjzR6SO*1-EO5tM@%<`lv(TgZNFrk$;e$1Hy?FEm&nijDU1OI+wLktRz5(ozEh z(o%GkNEZc4Qj-JVQ}Tr9@6_bLfW@h^XQm_t!Ml;1@Zgl`*~>Iyjq33TgMW%3#(VC| zlapX_=@7XwXMRp=Q$x`k7rufZejkkhh{acWNm+M0DQ!ML`t93Bs|YAhaEsQL!?;}t zDqool3(6dw$;e}j(T4K9FXLasg0Oa|5Mrdt3VA+dhvm7ggmsG4GesUjd7 zIl%gttUJ-t0t5ez4gqv7=-s(y5R@c#kwRp*%T2hZJnIh;W?S}<=T$hbS(qj zQn-2%mclu5W$R({xnRg{!70ctC8#aa=$(S$9TwuTqP|8|eC6Up+FYO)q2NL%nFa~b z$GigQ$lqCT7UFr4pq7MDm?Vyi;b%5A)YF*@s9oVADL>yqE;>?Yb^#CtySH41c-#mh zcL}t1m{9HcKtgPVH9TMr5t5JW9}^LT6(u(y9(RH&>5T%@FJYk|>)7Fj|GX0_im#h0 z0)rnJ;kl}zz8osUU+EX2lT0~UKPDo;NwXW`K_^V3a{?+Z?4eAKkZap-!Qcn!-5t8f zCC3_Ici3)j5eB%0*<}P(Fqw3Yu%6=S!CiCYU~N%<&-b*sL0>0iwH+hp?B8Y99YVCX z^qzBKR0LsSoq&KUMtlCt5wo&ek2XSXcue1t=q2;J&X9?()|%4|gMwX(UOQhtsu&rj z2teqSo5-+cPK}XoN>ub|Kc{P1ptA!UyUFyHL!_j3hj~QMsDAM-%|neAM}mfvBvO$b zS5%OcbG*5+=<P z^1wdj#?!C0=AS#;E9*XcN!HzdS*C68*L^;Vs0*d-xC*<@+xcCmuWvYc{MOROx;vqJ zx7`(0ZM{G0WLQ=9wfl-isKv%6Mv)8t2^RVvM$J(>CX3}X&EMC9Gk3NZCuj}_j@?}T zyl@^bEQE{bJq+P{Z(&sA3@FP5ehCZ2e(?)t`ozTsaU;W`JvXe_0<(A**7GA{?T_9; z4cC`er%4H_X=~PIt3eDyw;BC2L&w<|4((2(Eeu)L#cf$~Du0)vM;F)#1rY8j{Q`2p zDYs=w^YsFZn>&b+VAy*H$A~#%&kZZr@ps5iqX0$Ks{1J0>l}i*(eMmQ=Uc%m#n*Vs z+-m2<=rCKcw->x?ON@a7#sWicpj=#1Sb3C;X-e?dFkjr9NOW*P$n0|NsC0|NsC g0|NsC17qCb4+r(P2-oxk6aWAK07*qoM6N<$g83OK`~Uy| literal 0 HcmV?d00001 diff --git a/res/drawable/btn_playback_forward.png b/res/drawable/btn_playback_forward.png new file mode 100644 index 0000000000000000000000000000000000000000..ae67862e4ff6ee016109e41c6e0d97e0ae029872 GIT binary patch literal 1392 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw3=&b&bYNg$Vhr#JaRqXJje-#z0!t@E-vcIs z)g?iG!3@e#365-x#?jWovG2caS7YyZz1EZY#qYy8eC=<)U(Ztbe=|w0b$q zN?m=He~wp4{<^1HIZ@_}ck97|;#>nkp+oKuu1z!Jc*xx?@w9rE-I{<8Mq4^h`SL!y zT$Jd4PVBzQ4XN{y&l_|!muoEf*kiT&$;_ZoQ{}B@i-2jk*we)^q+(8Tf&}a01d$62 z21bUPH*89rocR2~6Qu<+XSYx9k7tKd6X9W5eht=Q4Bw|TL!-t4&xXNji9#M}*-!+Z73yN55I z_Qo1dmhjoUXi@)q`Jg49rw<(YTgk-K478qyk2BTv$4yB^!Dj+;jvkvA?GH^6i8fia zSy4gF?A)}5v;~))bryC#Iy~X%-NQmchEB4vo0EcyytJg3o$cz%d^t(7#YFp6cj~9+ zls8*0sQkISc~dTD2Af*XoEY1)FKiTfI+y%m3XV4WGfQ-G372TjL}%%DCG!_O5@MRw zc+zy=6~6AY1ScJB^(B#e7I|p#uYPuFRo0(bf7iZQm3?D_#qGVJ5rDt~qZjxJ;w(yx4H)}!WZf~~8 zrtQ0zi`bSf$}B7Mc++}vfy(&@Vfs3%Ou_Q{hi=s=+qUpbI{WCDaFyX!ColgUlht>* zCSR7hW9!L3J1${iK3}L&WG$DMBtQ3tB`eh~7lPlkx(jQQzl(;jKn zq?U_brZApj1_ss#yHX03-}VD@4TGnvpUXO@geCw2J9do-bV*by{B!TkK&iIF-fmp-Eol({{Zdn>CsD zr5uQVQ7WDGcAfH4)zDLO&Den^J@IsL45^s&_L@82p#UD104GU~z2EO~?*0BdzI(rG z2ZOOw&WC@V6Za?gAOF1m{?wBmHqH{bnuTBjW%oKG={gstE3rL}mmjMc*}%o@F;v*rxoyUMax6;@|yzPWD-6{+`X@Wl#+A_VM&{n}199;`=wR z-@dz)CUy7xOzk_L7T^9>qIK=Yqwj0HICP#WsjF!yt4{yCiuI-K?5|6l>=&$O*%YYx zbCK&_XLsq++zb^{J!Y;i9dnz+GUqglJpR~Pb^4iki_qogR*k~NFRC2`MJ2=~KWy2) zbN{a0TTflJ{`2E^jop)^^yDn2c{Yv~MY=783=B-Xo-U3d6?2jk7N{kpB&8|FnQBX0 zvuj^mWw~=lWyQ~q(6jmnbTw3ToR(w-8s_CW7V;@gEoKTW_V)1dRPH=^;?zlPjZ@kS zm{naZdT>;`HtvyLRqWX5{w| z*PoD-FYg-9W^%ReUqq5RgNC4l$eB(ZjD?QP5a#52dP+jN zj}{lKEDP!E>l95@2r}L86;oreOTq8zoUFhXYQ`rR4!Z<688Zf(nkh_PFiWk>ewf|cvs3(K7sZ&ffa7Z$s^$Wmg}&sn9c zj+SeeZe6=qcRe$s)#t7q7w+EG%3(gYD)z4J-rBu?g#{L@uYaUcz@Zf4Zt5qd67WUg zXHZ;FlS5*Y%)O1@X0?PU&pc~>^n|pIg{iib_6)OHwxAg;=XDsg=TEh-J{QBMaB24G zMxJNqE>CylI{0%Vw@JgQjYs*+1m5Y3arDe>J+dFVdQ&MBb@0PZt3tN;K2 literal 0 HcmV?d00001 diff --git a/res/drawable/btn_playback_repeat_overlay.png b/res/drawable/btn_playback_repeat_overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..ff598e0cdb127108e7ea3eca801e11dd3a697d32 GIT binary patch literal 1262 zcmeAS@N?(olHy`uVBq!ia0vp^x**KK3=%maw1RE|~8mhQ#(Z(hHB zcPUNk?)jP8cRnq?{jEgn+Korw*LZR0JXKOx(@<8O{&^MaOWWCBmpIukSkJO4Q1j;^ z*S*f}(xtf>DyDkOTwgloHi>10>v9;>-GxHXq%g?PEg^OQQI|zzOh)aIhvVG_N zUAwoQx@!IB$L|`uCrRnaSxob694(4;TM8K%n0P&1978JRBquCTOGrsdQ;aj!mbPZs zzPQSA=Z?yXpBg%X2K`Q<_@L6k6=<;pM5^dGf@mliC`mv==a| zx~7UQ$jWmJ)vQywn)OO*R@AIpFC5&xz1yXEO1^#(Jav_4$46#PXXTcxWMQUNJAKbi z2$?M$kdv6!8IZe_OK4+S`u2^1u1q#IZ?+{IE&ImT$8pt9VYgmfP* zE?8L>(%IK3nyL_Fy5B3N#$uO(-_PfR7?i^9*K zxS%G7#3q@08^6tJ2~nPT*8J!RX&nnwZ7J;;X0>cVGg{8;Flf)8YF~XWhEL(r?9+`r z&(2+*?#OlU=SFUmhE*Gn@|g*|(--6DncI3~W!edjZy(j-Or4J{VpYB2#SzmNC}t4p evn6X014C(2-(_pA*$03LnZeW5&t;ucLK6Tl`Y3?_ literal 0 HcmV?d00001 diff --git a/res/drawable/btn_playback_rewind.png b/res/drawable/btn_playback_rewind.png new file mode 100644 index 0000000000000000000000000000000000000000..fdd4dc8a404894e022accf34df7cdbd0d963a609 GIT binary patch literal 1327 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw3=&b&bYNg$d=TIh;tJ&c8U@2Q1URPee*%o- z1tmd#!3@H-QRYkxoDK;#%$}RxPGn=%+x2~hT-=SHANsg6PW<^lQ?~hZw|wMPzJtHI zL<2rDD!%K@I{w>wi~Fpv9HHwz|Km9@`TgI1cB4fP_|3MvF8QK)PbcU3)Cnwi<3-BO zs24sH>=8TnL-&!));5XD9eG^ZD?4B5w?3A-?lXVQw2-qamK7hp^3Ct$MpdDfO6iD8 zDi7PM6&Al<^yc)h}Nk}fvO1o-eXc~NAb>y)-P5f(^ z?%upJE$o^A=bTx&Id9*{ZpbV%DK`o_uzKd~!#9tLE^|3`R#xxfyN55AGO$N1K6U%} zb&iEcPDreayl~$B0Hb2_(X>W~qYA7l4BWY9?;cDDU{8_MnRWJXPt3%rFWQ}R`Z&Jy zxLoXBdDB4a5|1mZ$$=Y-l9QeWs$MX4m+%=X)cl!q#ij^;JGt2hOlx$N9;Of?xzD{9@7Ol985`EjyYPP1>}nYn9ZhtWsWIU5g8D9j{-!%FKKzt+8n5kqajzmfbia zqL`P)C$uqO;})mDytA2$*@JVt1-5N2XW+E7+Nil;*U_8zSc6TwSI;)`ICFIFdFIHi zQod6fwOh+^@!6ha=xa;VP$+b z40kSQ*m&wRtM)m44V_0?eg>L}4(v-)6OPYNXY}RMVb4Che#M%kghxGBdJY8ee$fi3 z+`3z|IW&Ox#@U~@wk~**9DDq9|H@ehuiibVx_zGGT1BQcJ2tM26jjbtYMgfFn%S{$ zeSRAr^{mSawaHRkuvb~|&B1v)qIyDl7}tC{bg)ahdUiL5R@m!imXzfGEPL|Wtdf;1 zax28I_$lRldcv{c(I3sk$un$gZhh41EA%U2(Vl0OE9Bhc^{b?&OEhgt!)DjT-_Gc? zm5KVkkL1eiv6y=?fT`5jz+_Dtx5W*G^FZk(E~u43yS%XTj3Or%tT z{SFaz-S~y6n}keF(vCTb99KQqvMQ``b6MZZtS@_#LpAsdHZvz zF{_Yr{lwk`*USyJ$ie(JmGWdMVaO~$4X4hjaR&H(Y|JM zvo(k3{W}JebcWY$TZ^>Ax;xj#ZA?0PX{pxk^z#0N$d}wy bWM!DS?E;@?(gqh`&Smg)^>bP0l+XkKq#@Zp literal 0 HcmV?d00001 diff --git a/res/drawable/btn_playback_shuffle_overlay.png b/res/drawable/btn_playback_shuffle_overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..7ba2512dc00d5a6de16915a41ce16c5e2c4d3c6d GIT binary patch literal 1581 zcmeAS@N?(olHy`uVBq!ia0vp^x**KK3=%maw1RKMF=efb17hC`?IZKV(=OZWT# zn+&^NznkMc@y)+gzPG;{aj{NVZ9MJ6kC}eoB&PK7gsk`_TKDPmEbrj2{U5^@t?!w^ z&l|Ym(2IrAE%V*jmj6u=nf_&}*YC{<0__XL8|Q6_5`MWjSy|rm&#nl?B027WCHvxj zZuHrfG+Quw>-Kc*pu`3Zhu8}$&v%BJ);?0Ixi@*z{ibD-VQqUg)vBJX(|cIHvR>Wt z?Y!>+!2IyU)5S5QVoq{`#HEIbot=`gJ*`uF=QdBC+&z1C`}BVK$c+sPCK&9>5)3pf zOw5)8|(X?pNrcGU|q*i5>^7`spTyX1n{o++-=1XafMLUmNI3cm@#t{+4 zyfi+cjR70CI0fdN&0NeLoZBt1ZF4yTr=`_K%>}!T-n_>eY}&ngwvoq~qjS$QM{b?E ze7gCvEH*azc>4(p>KztvI0fjQFp=o?Zg4PF5y{!bsBV*T<R(A&*^LEJks(r&`fk-Uz(b5e1bcT$Ab|IaRzT&} z-J;E*0lYWP{=Bty!IR|J!jGlhGkhz zOx<^>siY*mM54jicN&(VU3&1`d(&z*^?Zq!C$z!Ow%bKfp^gtPKUDMH&Gr3 z7v61BITbVOEmQE^CV{x^a#GF9m{!bNd5ghR_NJEMzpP(k@|VsT7&5+$}K+~A5Pq#D?97a+tcL`mntH@E&CSEi?z_b)`g38QX9||v63La zU{F0v6Dd|5l%yqfVcR5bi<;@z+>5PeirL3O=$9A^IE8qxkHy-e;vT6f15!S!`5*d3GMPfDQ2w+5 zl|reK$(2H>oHsC}H$1RIh|Pi6AjA%#4glB*0U#KRVi1g4tdBKQTJiT+c>Q9d$!vpA zJB-38j$(upCo!C)-5wZ0Rg+VL!_gi)?1Y^-;v^8j7=hs~oTMmKl~c1m&XK6z!os zUXPan_Xf*?W=f|^!VL1r?q0-Inc1S8HB%c>WQ&iF&&FpL6e4Tsmj=@8IsYY6HD zo;{Za^!JxY7nqHvqT{*U(RsjxAeevd87}e!4UHd&rI%HjUsReY_2d*t1cQ>xr?pb> z!}zV$RmWKIN$jWNw#1gVpS|!|b$pIB@+90{i3P4r<-ZWS_emlAaZEn>pR#6dCV1Y) z2JynCj(`nU1ka7&kthJUfQ~YtjUGHN58XHeXdyMRzOAjTwTL6$69R8aTv7UZ&8O12 zzmejYqiG4QzxEaDuHWAAV&!}lb5%Z6QrDL&pTADN%dA*1DR-J2UMSA)x_jPXQgFj% z+qZWqYM#p-xl!9(*YVP`L;1q|0*OOvnhAH$=CqyG?0T)T;=|f=@pHnrzcResT#28} zs2)+ZH1Ra*(Ilban)HhkVIL{lHkww){DKe}1FHT^(RVQp*9d50riImHq7==#4@ ztZ^)lVhoHt`G-5%^>U-OHb#_o3Odf-doT019RyE1nU?NnK2u^g|Jd1?zg~R&EyLHk@=?hQcs{I(vZ_*b HNkjL)2;H`? literal 0 HcmV?d00001 diff --git a/res/drawable/ic_fso_folder.png b/res/drawable/ic_fso_folder.png new file mode 100644 index 0000000000000000000000000000000000000000..ae7554ef30c59607093c6522cf15080b880ad171 GIT binary patch literal 496 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX3?zBp#Z3TG#{zspT!AzYm=&)zZ$4|*dfBY) zifPk1{e;P?LA4r{CuJ)xNtRubsko$9ebOLtno`mNsgkomLH(MO78TpA>-JmK?zbx6 zBCO>m5;b3<_#9A5qYi|0>o1s9?GcWgCslSyx#EmQShHlwIf?KoKvFPhqF~?ziIQ`| z(Tjjw;i!eek@JCEAQBAh7xZb9C^#-raDv~Zf=k6#D0GH+-VvZO$Gy;^|?;&>qWes(nrka8*s@(X5Q6v&%3x157>`QQKF8##?X{hlBrEyc}X`|aP$ z=X=d;|DWS~tMy(_|C`-+d;9P2^~^uN(d1kA@Bja`+;WX8SF>~T6kgTe1T<~Br;B4q z#hkO3Zi+bth%_Y5dTqi#>6xWe#v-RL|MZV)ChFeacPi@d_WN(o8+abCYMkMfzG;5Z z&lO@_s*gg~g(%0Y7T)i+`Rc!G4|h%Z8F#?t#c$p=X!dUvgie9H8x$ujfL>BX;GRmObj%J1g9RnpIdPndl9zH3eD{)QZGx!>iM X2SW@cs!mqS0r}q3)z4*}Q$iB}9Usi_ literal 0 HcmV?d00001 diff --git a/res/drawable/ic_fso_folder_dark.png b/res/drawable/ic_fso_folder_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..b9c6c496e72c3788eaf62cbe73f3088449eb66a9 GIT binary patch literal 477 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX3?zBp#Z3TGdjotzT!AzYSXbrQ_chp0ZgH5> zYS-Ujlo78P;cwbit>0It(^sq4S8LH#Yn+v+otLE3U9HzwXW3nA+feGzR%PE-Wm{h? zYN)G}lb{WjGM&(1Hlfk7zrm)tTrnr!u)A6(CPcfZS~)ceNXjLJ1Ce5G0+0u3XK|%`>Ywt`TU|1Wy;okcv6U2@6aUTA0>M@;RfkM&Q7)KF!ky zPafrEwb@f6ap@1!odN|76&)t#MM;koGC4h4Otw!kkvVbVRO6DT6PE@yG%3Yps;2I_ zq_yhD(yMF{QL)VHw>*0=_1dLls%sZ>Pq}vaxN2?!#}qRoGsB}?K4;E2p1gAC(kUtK r4V$(wsK>fVM_cP0xW-m}Xc;id76|KK4*xT+9^_|FS3j3^P6(&#>Q%DY7r3;0000F4-aN$X2itA z)YR0Yqob;-s>Q{{T3T9GR#wBq!>+Eb5)u+!U0`QwZCzhu9v>ha935U>V`pk@U0q=q z7#JEF8&XkKR)v{STVy{#LqI@7Qd(kHhnrGSRURH59UUKIV`jI<&O>pBP)}1978ec< z4^K`~LvMw($IoSDXd)sb8X6mOb9U6#)p4S?PgPq#Kteq}K~Pj$ZJ)Gtb$A{g9}El( zWo2p1%*?gN&qQZCe$NK7p)FI81nrKO~UqOUl01jWd<+@=H z04=rVuYiB9=D08b7Q)v?2>}U2)W#TY$0j4hbpfYYG64ZHRjdLGx|ih~7~`3_4iwGv z|9b!ccK`Lv77^+G|I!u{&j0HZ1<(Kg`CC@^|Nqnneg6Odya@-l|Nes^mb2f(85Y3b zw3s6X3?dr>0vaL>)$}ZP0002MNkl!#& bQGf&h4@n~HUQk8w00000NkvXXu0mjfNjgjv literal 0 HcmV?d00001 diff --git a/res/drawable/ic_fso_type_compress.png b/res/drawable/ic_fso_type_compress.png new file mode 100644 index 0000000000000000000000000000000000000000..547013bfbf4b2bb3975df475074e2357dd60227f GIT binary patch literal 426 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX3?zBp#Z3TGivoN?T!D0dU*BLx;g2&e16^rW z666=mz~$<1$!n}MV`isSN~7|`aGPch9_ALU!jQ=^W{DjZ!Bb5ors(ta31*d+6z4Fo zvU`-t_(;v#_NEzVLZhdPV@SoEr)NEdnj8dN0v8l`Y+;>xFs$&%#z2RU|H3!lonFfL z@0+WemEZMZTc!Q(SDsfIxEM$=cRlA6a0^Z_>ldDPIVUkvd;asMS-F<(M?R;`_U73- zxuV{}D`w8QN4HKNdH>wux8cjHA|PS04hdP z36Wa81_;&?RsghP)oO?qpgGG{tOQxQ5@ZobeDxZzIUu9ftX;Qu-Fl!b(0ZUO$oMsD z*MbFs;vjdzoQmuKpcF`UkA?#ih?FcBw;mMm!i>_td+vcO(E2T&E{-7; zbCMGzUN%hZoY^|HcW(1!?$svd{QC0y8x~C1z!8=iXjsVUshKGmX<77UT6jQ6P*`AS z@bwE<4k@i&%Q7i6{Q8B9N0&wWORB71xH3PX!}r#S8#j(zIdkXGrBk=qy0@&IJ9lsM z;>nx4SI@Q<%jDm%+Gg*bjl1t0zkc3c!9c@8#Y9J@yToNl@RZG~qf2~pVr;}-wj5dV zWXhE-U%vGC_m? BVU+*? literal 0 HcmV?d00001 diff --git a/res/drawable/ic_fso_type_executable.png b/res/drawable/ic_fso_type_executable.png new file mode 100644 index 0000000000000000000000000000000000000000..44d61c9b7c5680bdc6e34be9b266127f8d79e07f GIT binary patch literal 1119 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 DD**t; diff --git a/res/drawable/ic_material_dialog_dark.png b/res/drawable/ic_material_dialog_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..c592fc4f21757dd06eb52878a0ece24b6a3b43c9 GIT binary patch literal 5594 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 B_$vSa literal 0 HcmV?d00001 diff --git a/res/drawable/ic_material_light_filesystem.png b/res/drawable/ic_material_light_filesystem.png new file mode 100644 index 0000000000000000000000000000000000000000..1040449a0a10b8678debf6b931bd1ae25d61d554 GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAd3?%E9GuQzs#Q>iWS0HVK7o7Bc+6+|0T@vIM z%pgB2rmvC5j;%xJm_nsDP}tnl#WAE}PI7{Ruz{AfCSxN*$4Uv7iWS0Mc#FF3FG`X^8kcS(?6 zFoXQ8n7&3HJGKs?V+xhtKw)!F7srr_ImrnM!UkH_nv9JM9V;bRk~d9lc4V4xQjvp= wSAq4_o@PhU2Q>yKqDADUHx3vIVCg!0KpqY_5c6? literal 0 HcmV?d00001 diff --git a/res/drawable/ic_material_light_home.png b/res/drawable/ic_material_light_home.png new file mode 100644 index 0000000000000000000000000000000000000000..d0e06a74017b70aa9bf574b1ebf982b14620efe8 GIT binary patch literal 268 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQa-T^)#u0Yz@*x1O($iTn=NFp;J zTwJ7~p&^>ov^L)tK;tA!g8YIR8048udl|WWyJy*RL`<8)7Cea~WTstX_0!3>{s9$* zd%8G=RLt3X=C+W7f=KJb9MxRc=T-mJuSy8MJM$sFZgZO8r$kwY^6m4C+}c-v3{>dq z+^%-DXQ}JS%Ud??NbOzstmeSp2X_s7MUy{A8Za(lPjmVd{b!-*i6WL8Pa0I~fmSeh My85}Sb4q9e07mmxSpWb4 literal 0 HcmV?d00001 diff --git a/res/drawable/ic_material_light_home_dark.png b/res/drawable/ic_material_light_home_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..05b8dfdc605f7fa39aede7b58104e7c5d7d92af0 GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQa9sxcfu0Z-fHNf+ViVZ-sBuaw( zf*Ba(nM`{bxqQ24*>glpo5B`6DP*QyWA&vIrGh{;VV*9IAr*7>p7G>sP!MprxQun8 zn26;2`ryt$$GbCM#P18?dT>}mX?OJT)(3yDK3ULlMI-uh+M-K2C8oPAZWVRgmfKy3 r`%o&)-*YzoV9N}-8H}Hz|11<;XT-8aTSa3N&;|xiS3j3^P6$IYmq5W}b=z{lV$z@5-oz`DV~g5eHR1Os=A zb_44n#s^{tn1Z=$&d+)#am;?ztGj0n)fj+U?l8r^zpI*O!~CvjLY^$gh0u5Lj}#Ja zdER5(!+hvI<43U>=O(TPs6NXX#b( zqaW3G{L<8V^(boAN1lk)FjHd2T!FNSiHR{77#SG>X&@H@ zfNY=$jE6!uG&I0fnwpxz*+4~ldU_Bpkc2Q$biz0gDP#peHktz@2m?C01IrApzFgn+$GB{3Xt#6#%A;&x;GdiKu zj4@=IrhFy8beKEu6wR1pKvNn#T^vIy<|HR9kW5HPN^=xXPEAfvfBwKl@bM$Iu8p5R zeEP`E!R2_g#r4KRhY%hu4ZTKzDN`o3X-s+O#_p8Y4(1FD+qk(7 U_AlJQ2y_dBr>mdKI;Vst0HBY2CjbBd literal 0 HcmV?d00001 diff --git a/res/drawable/ic_material_light_remote_dark.png b/res/drawable/ic_material_light_remote_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..e89f6b6939ef1a76a1c8b57fd26a31bbb42e609f GIT binary patch literal 429 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQaO9OmDT!HleK@WEPeH;pOsCh|{ zUoZo!qL!FR%gjVUnZD^+-r+V}3``NRoOzRU7@ccW9VfKbH%{)5W1f*2ozQ6(GEGyy zl3zN^op-K;vl-Bc22U5qkcv6U2@50>Qj*df#gkK$)6<_na1ng`h^=em=MSGga&vGw z9&K^G@z5cJM@vJkPhiS~DNPzvp1E;sI(>qR`Dn|NCyp|#uCA)70?w|Y8Y_-5CaucO z$jZuWzWR!Rb!o}h8xtGn*`4rVb}mgZ OVeoYIb6Mw<&;$Ss(fYao literal 0 HcmV?d00001 diff --git a/res/drawable/ic_material_light_sdcard.png b/res/drawable/ic_material_light_sdcard.png new file mode 100644 index 0000000000000000000000000000000000000000..6e308d85159f5a9502f0f03f4cad5d987af060bb GIT binary patch literal 240 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQa_5nU2u0Yz*(9j4BfDAYrE&^d- zC4q{wXNJ558X;N|)0*SW0`dqw4?>gcK&GG%*pwFtaj+ z2M=7t9zS}P_#pLBa+15)bP0l+XkKbD(l? literal 0 HcmV?d00001 diff --git a/res/drawable/ic_material_light_search.png b/res/drawable/ic_material_light_search.png new file mode 100644 index 0000000000000000000000000000000000000000..c47f1353fc5f64b6987bdd90043fdf34d4bbb5e6 GIT binary patch literal 351 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQa)d4;su0Z<#0D-ejZXbZwYnBB0 z1v6a!_x~!>`$Ih^zOyE)x+!!YT=jUK&4lL;XHLqSJT=u=`fZs?|KEyV*Prn{lLBhW z@^oRiR0EfOmyPmi_tXkqZ#^4LUQ;{206d0dmcr{{)0 f^L_8PvEP~@D1nVl+D9N3Xe)!KtDnm{r-UW|z2eZ? literal 0 HcmV?d00001 diff --git a/res/drawable/ic_material_light_secure.png b/res/drawable/ic_material_light_secure.png new file mode 100644 index 0000000000000000000000000000000000000000..c11e3e1c3833c954366a491dc5f88d562a489556 GIT binary patch literal 527 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQaHv)V@T!FMP7?^+okZERSW@Kap z;lfFvIGhb(V2Z$XL8OqmaK%6dG8=9tP;o;;16&FsZfIx-6h{`cv9U2QFo5gA>Jo_h z|3^hH0R84s666=mz;70AE5ImJF@0v00#8x9bDf~()Bq+!D<&6a%P7z?fU}H$sp`K}H1!XUMVr}Hv;^9@ZAVx4_L6q@p8EM}}UEcE%Z~G49y>ET7`Ji-e zy2U1)>91$_x|CbZmj2bWdiRtoacfVe?PmVgzCL-bvi^JScbn~>Gp=jr Vox10xHzUxc44$rjF6*2UngDI|o2mc+ literal 0 HcmV?d00001 diff --git a/res/drawable/ic_material_light_secure_dark.png b/res/drawable/ic_material_light_secure_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..6c949e806f98cc822a3db0d3c6c93fa6a566d73a GIT binary patch literal 491 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQa2LgOTT!Hlep$wQ8Kl1_l!J#C` zFPMSfEZkOrQK(}2%qj(*qITyxLCvWFOomo2%$8vs4qe`E^}+0_GhE{)G4Q7MP3~t^ zv#*h|?rl@|jSP$uagx8)^#^3uEKe85kcv5LFGLC-G!S8ZFoDbO?W(EMg0wPL*53d3 zfB&S1iVOYI<{Zy|H^=(B(5Lp4O;e;Sza{>35^RXPv^HktH6Gy=<|a8&y0a}Bu5J@8 z3YcE8X~E^t7mKg+vkA2?JT$R2{K$<^3BP+Cn&DDMH}4eayux}tOgB9H{!I^G8(-yP zC(5?7^BrHG{CKjA-SWBu#&w!|?l!w~)t?jI`jY$o;faUYfUaclboFyt=akR{0IqB< ACjbBd literal 0 HcmV?d00001 diff --git a/res/drawable/ic_material_light_settings.png b/res/drawable/ic_material_light_settings.png new file mode 100644 index 0000000000000000000000000000000000000000..14b73484d4b0d5cb2cd688bb5470825e77d08be1 GIT binary patch literal 745 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqMyzV6+bK32_B-jlsYK2QV}=#FQ~J zGs6^whyV@4B?T0BaBzSq0Fp*v09Op;VKu?PzyK%?l!a)7SPeJC)YKHN3nBOde!&b}7LKOOdi^uo#h6{E_35%O zF$X3a7&T8XS2s_0W>lNlBb^h+AlPKf%I;Fi!C2wI>NBH2hCz!Ym& zy49M0YPg(2Y&S!Mc-N%f35-_0GE(_vl3sSkQ|uWUTBPJz_+;gu11EaktF(*0U z0JFr*88agzF0$ObVQFEpa{|kbMy-kpON$>rBqdl_6qB3|DJ@ERBqJjuG%4tk(WasV z$4XX(`OG{GQx*w%^n^-kF-#2zWaV15vUx_A)U2pmTnf|r^a29>uQ112QPAJXuLUd;K3uVlpQ;{8CY%a zI2^04s{H+<>FsyspQr5`m|0ged}U`VWNCL`x#4Unl6z3Bwbk9l%~hKDXwsUb$Bz{6 zr3A44Qe=Gb;fM-b+mn&*uOD literal 0 HcmV?d00001 diff --git a/res/drawable/ic_material_light_settings_dark.png b/res/drawable/ic_material_light_settings_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..d71b1f9a11d77db7c6f0f34ef2a7867ee4ee2c9c GIT binary patch literal 669 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqMyzVB`q!32_B-|Br(I|0g`ZejgYX z5hX!>!3cE8yGcDFIP8DcV<+Z*dv`2#~|2b%gXLj%2DCq zGowI8OO{W?q>Ym&y49M0YPg(2Y)Z}zXX_1VKOwuEv zNkNy45*#Bf73MSZG)&pU6tPe#gh6ZJ5+0F|Kvu)2SFf0wJay2~&0VmdXAR$kmE2o= zV*8t&?;i+|%vm8AFhTIh112sh=bY(+Y+SP%BDiE^Y?dY5W<7JJv8hRnhi6)tL)%tf z`+y7Aq*C_G-P^oa_(om&g#A-uEdm;ZBKEJAZr{!B(r{P!RRx=*e!jDOeC>i6^EcKz zC>)v1bIM^lE2FHO3{&Fa#zYP4>KQCi6*|Q%%e*F3K=kMRYzkmP!^5x6fvuC5DqhG#!3FIC)aA5ZA z*+8jx@7@8m07W1gU%h(u{P}aB#+^HNe*5+fXeLm>=g*(tzkk1R<3^xqKx1FLcmY(u zdGqGCZ{P0Tz5Dz3@BjY&d;R+L{{8#^|Njp(`QX8WKoOvUJv}`@D}ZJK*|%@s1_}aA zUb%8*YHI4GOP4-<`UKPq0YEit*RJ*S^aQHhGUKuw(A$9}L4Lsu42-OtTpY}7lCD_= z`SE)EOxD}p|NZ~-L8J7MA4P8eE=&AURt4Hx0E8eCiJ71NTi*27KA~xUp8LpDoqPP` z;U-UB!CMAmpBWgKv^-rLLn`LHy%ioNk|^T(aQ~{^jx1Bn(p??SL>eZYEBv>3Vw%A3 zb*2CQ`aZa`ca_|$g;CjuEoPqk19aE?K+t;7uOC^a88q z%aU`>Jt$#YZfr64;7dG0Jva3tb$Ry1ZOSq@p3rtsqFItHnI}!6$8biX!7-cyWnN)_ zpDNx{`f==RpH5T$-FF_-*Kav^lIPeR&B(8pCnXykPUtzv(`?9Q%yUfQjDZAhfxE2t zbtAt{Z?A}DyMG&KjQ@=XDQwB_rmqoWv)73H8a)fQRt&@Wy~DVh<)^Os)$hK?^}}Oz z*+>@qyDz~`>qu8!U6)yi@R{5EgS$oNO<(g)uPJ`dn-08TgcOohJoEOgvTNERzvs)Y zUmO=^@7<`ozc##6XF&UvGc+&zJr2_4oPv3*SmFm!2Ih qd*ipc<%_%ZCmvh8KTt8@w>(Fa#*yca(_RCUB!j1`pUXO@geCw75B5)%_4Qb|cko}QkTmX_w`=5}^=0RaI(l|c1CSs)420u(ebFwlbmV`F14 zFE5}PpaKsM4}E=oph#$FD9{`$D=VNXplJyS3GVLhKvG>@T}@3*M@Prh)Ko)518B8{ zg#}O-PzuNfS_(7@=n|l-fDE9ZuC6Z789)&;Gc%w$KsL}EZEbBJ1Ly``Wp%`sZ=f7FgeRbh*3Id+S5P(zn!eG;;wpsI8k-k_e%3R ztN;K1Z}=a$qSV-FVYZTemi+&P$}39O*Z$xBzbSOZry9$`2fsEchEypLcYuAHf)zxBb^YtUICiETTX*Ogt<~b&D#y}$3;BZ0@PJtMW$gA4? zAFnm*$Gtic_2JLcSF!4`ks0CZL-`$d)h)jJM(fA5+~&M{_K7D{XS*eh>T$V)#Y=ySmZf3PX;>g?=+zE_~ejXb1doR;vfB9VSoQ? z{Clh}8);-Qhu9#k;;H+!ri$aj=cj?yA3ueDm6&lozfJq?+uh&g?do0~dafV8JpA^X y?eFdD^hy&ef4nZw&MkX(eh2^JsaL;!mw&SB#-FH)EPh~mW$<+Mb6Mw<&;$U;3Z`uU literal 0 HcmV?d00001 diff --git a/res/drawable/ic_material_properties_dialog.png b/res/drawable/ic_material_properties_dialog.png new file mode 100644 index 0000000000000000000000000000000000000000..57d3c8c4266cb7c836a110e71da44eeb1afbfb98 GIT binary patch literal 769 zcmeAS@N?(olHy`uVBq!ia0vp^AAtA?2Q!eIcqUR8NX-fG32_C|?l{2GrAyDAJ$vB5 zfqh_bi+_u=>`Auv*bSs z?N6Bb@X3?M*JA|*ujUnS0QEiaba4!+nDh2dc95{6gxkY;^E`KGwRB%p;W`z{D0=7r zJ)ejpw?mho-`x1#_GW5_C@u3L|r$s&a+j@+-xx8Y=TwuOP*!MGtLoaSUk&~ zyqfK~#GJ%}o`aHX$BAK-h~McrSi*K$(kIF@LrFZol=(<7m^Y_3MCXg~8L+&t;ucLK6U&cz`JY literal 0 HcmV?d00001 diff --git a/res/drawable/ic_material_properties_dialog_dark.png b/res/drawable/ic_material_properties_dialog_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..c01894028af41e0717907ce214a2b4c7f2e5d16f GIT binary patch literal 884 zcmeAS@N?(olHy`uVBq!ia0vp^AAtA?2Q!eIcqUSpfq{`Dz$e5t1q_mtljGy#qoSfB zA|fIqBV%G>5)%_4Qb|cko}QkTmX_w`=5}^=0RaI(l|c1CSs)420u(ebFwlbmV`F14 zFE5}PpaKsM4}E=oph#$FD9{`$D=VNXplJyS3GVLhKoV$|g@pxB8H5eA1*jKjHP9JA z22c>l2C4^I4O9;#ffl*Cx<*Gw1C0W*Ax8Q7`WhM<0=Ym1KqmnK(EUzMP9`QMKye5F z+5%JtadTW;Twq|Jo0}WNk3fe3!=#sS|4$$lQ4-`A%)lz^5E>cjZz#Z|QnhTuAE6wYy0#}q8J1xvsvd@xNE?r-{{eM&Ficd9`g%5sh zQV{j*KJ)3z%k@PZVy;<@CS1Aa^MOZGfzftJky;}!} zcYFVv=^$eK{K|T1$8aXuz~JccQ$YduFWd;}Tal@^b;~x@g?kroUTs}f_4^k)+hhY1 z8zU>R(#OKC{3p*pxl%GEtt)Qg;+s3YQgU6_pSWCeHNmX;CC@VB8RrtLsLAj)tVrDW zLa;I_s&DGmMea{Bb8XEgaoxLPG~+CFErPrJH^1K-!(8i){(I&=U2$Kx%4OcSRTCwB z(hTMvEMdDW>62~{JL%xc+9T<|**PsuB#86xE|KQ`zHYgRizi?9nlm?E#8%mSdic%K zJ9qBgwYO1w`10q{kEW;c-^Z=dp_J70o*ouB?Pt^3cQVkYoO%VBmdFv&1@ My85}Sb4q9e0DI15MgRZ+ literal 0 HcmV?d00001 diff --git a/res/drawable/ic_music_bg_bottom.png b/res/drawable/ic_music_bg_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..8da78949d42a629c2e4e90f576232b41a4fb6ce2 GIT binary patch literal 34667 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%0DcTgLlxLMSYPtGd{p)(qX3yq@&?HXUj@X2xAktmN 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

lq@au|9 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: - SystemCallAccess: - ControlMemory: 1 - QueryMemory: 2 - ExitProcess: 3 - GetProcessAffinityMask: 4 - SetProcessAffinityMask: 5 - GetProcessIdealProcessor: 6 - SetProcessIdealProcessor: 7 - CreateThread: 8 - ExitThread: 9 - SleepThread: 10 - GetThreadPriority: 11 - SetThreadPriority: 12 - GetThreadAffinityMask: 13 - SetThreadAffinityMask: 14 - GetThreadIdealProcessor: 15 - SetThreadIdealProcessor: 16 - GetCurrentProcessorNumber: 17 - Run: 18 - CreateMutex: 19 - ReleaseMutex: 20 - CreateSemaphore: 21 - ReleaseSemaphore: 22 - CreateEvent: 23 - SignalEvent: 24 - ClearEvent: 25 - CreateTimer: 26 - SetTimer: 27 - CancelTimer: 28 - ClearTimer: 29 - CreateMemoryBlock: 30 - MapMemoryBlock: 31 - UnmapMemoryBlock: 32 - CreateAddressArbiter: 33 - ArbitrateAddress: 34 - CloseHandle: 35 - WaitSynchronization1: 36 - WaitSynchronizationN: 37 - SignalAndWait: 38 - DuplicateHandle: 39 - GetSystemTick: 40 - GetHandleInfo: 41 - GetSystemInfo: 42 - GetProcessInfo: 43 - GetThreadInfo: 44 - ConnectToPort: 45 - SendSyncRequest1: 46 - SendSyncRequest2: 47 - SendSyncRequest3: 48 - SendSyncRequest4: 49 - SendSyncRequest: 50 - OpenProcess: 51 - OpenThread: 52 - GetProcessId: 53 - GetProcessIdOfThread: 54 - GetThreadId: 55 - GetResourceLimit: 56 - GetResourceLimitLimitValues: 57 - GetResourceLimitCurrentValues: 58 - GetThreadContext: 59 - Break: 60 - OutputDebugString: 61 - ControlPerformanceCounter: 62 - CreatePort: 71 - CreateSessionToPort: 72 - CreateSession: 73 - AcceptSession: 74 - ReplyAndReceive1: 75 - ReplyAndReceive2: 76 - ReplyAndReceive3: 77 - ReplyAndReceive4: 78 - ReplyAndReceive: 79 - BindInterrupt: 80 - UnbindInterrupt: 81 - InvalidateProcessDataCache: 82 - StoreProcessDataCache: 83 - FlushProcessDataCache: 84 - StartInterProcessDma: 85 - StopDma: 86 - GetDmaState: 87 - RestartDma: 88 - DebugActiveProcess: 96 - BreakDebugProcess: 97 - TerminateDebugProcess: 98 - GetProcessDebugEvent: 99 - ContinueDebugEvent: 100 - GetProcessList: 101 - GetThreadList: 102 - GetDebugThreadContext: 103 - SetDebugThreadContext: 104 - QueryDebugProcessMemory: 105 - ReadProcessMemory: 106 - WriteProcessMemory: 107 - SetHardwareBreakPoint: 108 - GetDebugThreadParam: 109 - ControlProcessMemory: 112 - MapProcessMemory: 113 - UnmapProcessMemory: 114 - CreateCodeSet: 115 - CreateProcess: 117 - TerminateProcess: 118 - SetProcessResourceLimits: 119 - CreateResourceLimit: 120 - SetResourceLimitValues: 121 - AddCodeSegment: 122 - Backdoor: 123 - KernelSetState: 124 - QueryProcessMemory: 125 - - # Service List - # Maximum 34 services (32 if firmware is prior to 9.6.0) - ServiceAccessControl: - - APT:U - - ac:u - - am:net - - boss:U - - cam:u - - cecd:u - - cfg:nor - - cfg:u - - csnd:SND - - dsp::DSP - - frd:u - - fs:USER - - gsp::Gpu - - gsp::Lcd - - hid:USER - - http:C - - ir:rst - - ir:u - - ir:USER - - mic:u - - mcu::HWC - - ndm:u - - news:s - - nwm::EXT - - nwm::UDS - - ptm:sysm - - ptm:u - - pxi:dev - - soc:U - - ssl:C - - y2r:u - - -SystemControlInfo: - SaveDataSize: 0KB # Change if the app uses savedata - RemasterVersion: $(APP_VERSION_MAJOR) - StackSize: 0x40000 - - # Modules that run services listed above should be included below - # Maximum 48 dependencies - # : - Dependency: - ac: 0x0004013000002402 - #act: 0x0004013000003802 - am: 0x0004013000001502 - boss: 0x0004013000003402 - camera: 0x0004013000001602 - cecd: 0x0004013000002602 - cfg: 0x0004013000001702 - codec: 0x0004013000001802 - csnd: 0x0004013000002702 - dlp: 0x0004013000002802 - dsp: 0x0004013000001a02 - friends: 0x0004013000003202 - gpio: 0x0004013000001b02 - gsp: 0x0004013000001c02 - hid: 0x0004013000001d02 - http: 0x0004013000002902 - i2c: 0x0004013000001e02 - ir: 0x0004013000003302 - mcu: 0x0004013000001f02 - mic: 0x0004013000002002 - ndm: 0x0004013000002b02 - news: 0x0004013000003502 - #nfc: 0x0004013000004002 - nim: 0x0004013000002c02 - nwm: 0x0004013000002d02 - pdn: 0x0004013000002102 - ps: 0x0004013000003102 - ptm: 0x0004013000002202 - #qtm: 0x0004013020004202 - ro: 0x0004013000003702 - socket: 0x0004013000002e02 - spi: 0x0004013000002302 - ssl: 0x0004013000002f02 \ No newline at end of file diff --git a/resources/ic_launcher_filemanager.png b/resources/ic_launcher_filemanager.png deleted file mode 100644 index baff444bfe2ad90a914b4ddad09ec37b29c91dd0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18807 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 diff --git a/resources/logo.lz11 b/resources/logo.lz11 deleted file mode 100644 index 5764b298a807007a1740fe82101964482d8533fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8192 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 F?mxuM;XMEV diff --git a/romfs/res/drawable/battery/57.png b/romfs/res/drawable/battery/57.png deleted file mode 100644 index 7aad741542b3a59d1cc9f43198a5c71d51e8f288..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14632 zcmeI3O>7%Q6vw9$6(!JIibUYFb&4n->s_zyINsW>jGf>VoJ5IB6HwKxcgM*Rdw09L zshvoCh>A)GAysgI@*z%KfgU&&Xpuq>s1h6~2P9BIDpF7dNO0jp!0fJLKRb4&MLmus zdtYY$@4eaIdo$U~zBoPk$nJrA1`tBKCkpu)avk)(yKW)BA7-yUK`yshg?R^|+xK|i z9q5g>h7cOvr*~Ym+5)ZCOofp@W%$R6`~KYS>k{My+Z%aw8q`BkagrE`3u07=io6t+W1<|BxaKF686Z~5 z)++K$e!Q8E{FM$ZxvnJ(LcLxO*P~(6UKAq9WKs|%L6Uf);GI>&RT{kE3+W5>(k@O%ByXng+HphgGWirargH#D>E+@BJBmR7O`L%iz%T!2$Zd^l5=L& zw8^q?CXy0*4FfN~)8q-maTP6Gye-Ov4xOK(G*+tS5*R$7ysGV-z;ZuTl`d9VN zLIk6o_1Wy$t@TWKhnh@!fvijMYd|*7ma%r`KtJczw7TN!6Ou?iLd5%Ry29yh*ZJ*T z;dHla+w-uYyBVpOt!o%~c_(jhEXps6ruH?(pluzM!3mF`;KmMU>J@ifi$Q#!$m~| z1q>IYLA4n!Dk3OgxF8Lx&2Ui>K>@=BX;5v3i;4&e7%oVIYBO9^L{PwRK^jz>;i4jf z0)`9HpxO)<6%iCLT#yFUX1J(`pn&0mG^jSiMMVS!3>Tz9wHYodA}C8Ps5ZkzMFa&57oQ4v7_!v$$j?QL-lv|s(h26^AFPG0Pr zdi3%K6GMD4{_^Qv-{X%?W$!pTls!brM<5`sQtNja9{cC`t*J4CtsS` zxVCin54oqO{(APptLF|~{qd>q{;mzbzXNR?|7})0Fn%jp37Qz2%)j^W!t?(DevjWi diff --git a/romfs/res/drawable/battery/71.png b/romfs/res/drawable/battery/71.png deleted file mode 100644 index 8313cb01fe9369ad0ac8aa3d950d4a115ad822bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14623 zcmeI3&u`;I6vqd#T0vns01}8Eu)Mnn1di?eXd08GDoJ-6M7pI7+7=0Q9edJRO>AR3 zo3xiMRAR*eA#p;8e*tcs&}toFTtoP5<^bxjbsmF1rXl_EhkF2z~bX zvj|OIFe*#lQuz%)SkM=sEsssw|c5 z0$wv_HyymVIakp(*R_n!Pvyt5ZG{AAU{B@Rjk@V7?HnJ*Rfru7i#!)fy!9ME9VFzI z%JW>oaxj;QO$eGak>=8wn4C(dlj&EuxFn}UDIq2#A)Zi@l9G&boe!TMBUaYYYsy0L zQYRhxE61;So~?*ttJR9N5;4nJ73EAOBT8{G9v6s0a5qd(Z40J*J_>Rir-)t6F>KGU zOfHD4R;{L&`q)jU9xhGSL|n%WY zzXgoq1NYH6T5H%mZ9_$&Le=OXu2u6|s)MIjNl)C%UXP;oyA6iVioxk$H~I?^jrP~) zxMNS&GaDRg3h4#1E;XzH**qu4dYL2rQc%-+Gt{T!68VU7@Y`{P(}S+dr@O-GLD!z= zTc+XVTz9wHYodA}C8Ps5ZkzMFa&57oQ4v7_!v$$jZH9}A2nrZ3NP}uKTvS9*z;HntRGZj3(}z4 z3>Os<6fj(n2GwS`sED9|;es@%Hp4|l1O*Hiq(QY$#WmJ@^$(lmeZLlYvG3=-Uk=Gj zft)tGP)2C;B0@K|5&Fj`*WVFpN(lXN4I$-Igr2cJS^4%7(KbuP=}P-fb${o^IPWg` zuO7Diotx!_KX3ow|K-2we{uBkH&1gH9zME!xOZ@Ocj{p4yDh;hZTm0yA03S!rjM-s zl_%eEcKpBnD+g1%{*V5)zxe&dpZuHeS^oIhTfZLoA0Mq8Z0-8D{Bi$Z?E~TLna9!I X^ZUCSiPvSa092ZpD}MRfwfFx6MT+VN diff --git a/romfs/res/drawable/battery/85.png b/romfs/res/drawable/battery/85.png deleted file mode 100644 index 9000c76ea05e63e21731d653637ec653e8b2cc45..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14606 zcmeI3Pi)&%9LFCL2UavLAZ;3&WO+k?z_F7wNn?_fl615hDWwfs3PQ}WUmB~4ZER;r zd)WjB#FY~QByNZce=djvhajXKIKWQRU>tgy1RU7lfC{BE&wrZyk|uo{+VMq7{Ofz) z-|zSQ`TgFjFMelv>G`8mC#DcWM;8}LE9Cl+|9#+o^84l7jaSI!h_$fhAoSqF{`Wrg z{)fjAnmMgks&2LXf~=aYn4+0A9P73$qDCk;+qD#R1G`)euj@vhzxj2K=X5R4pG%dc zvQ@+lePPSSt6NJIb!$V-YW!?rD%X`sfEIQYuG?xFj@-@jL0p;G{;SZk-zeM!*wlL6g!x6301dYTw6d+s$BUnkwQZZehc9 zh+llHpVcs3(`lF!Que1OCMGp0m;1TLuD8`1o7!>bFAxL~(y^XSWz)jq3UXC$ zvLx<)V!h*R(69*(`)zw!Dv!D;9&U z;+{CC8=Bd1p3I4(E`xdxqT!O_;sWW%j1ZRuDN&I~Gsflkj3A|CNdiGa|X>Je$C`V(E{i7+aaz|017R1q1xo+Am#VzP9WgUx_v7QqLgFxBuD!FJjO`9wW z7vec_+%WJ8I!#_Q99J<^yjUubftapqa!SgoSt*kg(slBa#M7!UBh|2wt!2`xrfBI@ zQVk%M%h3Hm=#r_n{oN9U*6uPia6b_GMO!B)ozesvfAx2pVoaC=sTOpH^yen%ThhJ3 zS+b^NZ3Pe(qZ6Js6M zCOLwM$T2Jx_;(&uG8`oj3*_4as7rDO??y)hrqk9|}hGR(}4XVv>Q4v7_!v$$j zZH9}A2nrZ3NP}uKTvS9*z;HntRGZj3(}z43>Os<6fj(n2GwS`sED9|;es@% zHp4|l1O*Hiq(QYAE-E4@V7MR+s?Bgw5kUdN1!+)ihKq^_3K%X(gK9HeR76m~a6uYW zo8h7&f&zvM(xBQ57Zni{FkFxZ)n>S;h@gPsf;6Z$!$m~|1q>IYLA4n!Dk3OgxF8Lx z&2Ui>K>@=BX;5v3i;4&e7%oVIYBO9^L{PwRK^j#1P+U`kSO2g<-uLT}7yC|J{pbtw zQXr=;tdtSjdJLgUZz1%zN3Opj)RqwX?L0#Adk7sf-+le^8KTt|OLLX(_1eyrOONtS z)q7^I<6U{Xyz+bRC-0W`lK1KDr@noJJN>=)^WL@H&v&=~>0Q0J=6-kGJL$c8`>TI% z?rh)MKJwb1dl!%I{MAz$XMga#U%boSF>iaf_eXEG{$uUgZ_vk&?|$;Z*{uZ0hZg6T LN}oM<{>}dYj{f6- diff --git a/romfs/res/drawable/battery/charge.png b/romfs/res/drawable/battery/charge.png deleted file mode 100644 index c66b3818ff533ba817417bdacb1f7966f6f30aee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15669 zcmeI3eQXp(6u`Fx3^Xa42ERb@a!7>0-tOM_Eq8~tS8UO%wXLB=5%zZHuAAQNuDjdb zwQ5ia24f0@s1W%}f`sr##b{y_p&CIFlwiDFa{BI_CB}M>!cBk(aa_7 z?VC66y`A5C^JedleWk7S;kw${wG>6wwKT`t;kR{NJN*{;f5?4oKm3}ZHFp~nHS?hL zSM&O!nR6&=$u7CG%j}9j%8P2oB}i%lxN;c{&ZemF(wruUy})!NKuT63&Y$*;I32PS zajx*i*|^pSdgSIo9drz~c8Y_&Vn}i>jn;;9Jamu&rr^kB(u%?7BF?;D9$s6+jMI^y zV)jOy4OT!$SG>*9sOrGsbNOkJ^#>e*kc;yL0^Y!4hnwYm4C`S$EbaF2UY7T|9mS6` zS_`kjx|HPGW6O%+pcHZTn5M=vOg5W!Wj!ucPcd956k=F6<95?<25k%|rjVl*V_v~Y z$xjRzqAqKutSS!6uaHpt%!t!z1uB1v>&j^5K#Eah2PrZ+L1Q=<%TzOx#IlXnr>FC` zCW#D4gA7ni1KM#_?X(`%RE-{Wf{AJ4Kj zCxw}akGLwy$$=_9JS-23+vdYI6ZHZ2k0hAFq`cVY+Wbf&pH%gXU`FMPkOGXRq{2*P z&^EJNReYnGR&{t=AnFM-)rM_W`AXwk6vGr05wygjaKI(Y5-$ZLzzQBO%?Tck_9VP4 z9hCecEe6G;&n>Ya;q&Do#^ZMLajs*k*k|n)=(^BYBvpj&E4L+bL5~Qy1TBaOj^;QJ zpo57-f(}XUpdfmHU+_w$KsION0zIb7uB<=UYIH7=7po4ya zr3GIwASM%jPap(JA*#3;7phq{V6_g|su)9+p7WME)W4U8N)?Mef|3GKlqs#OH1J=_+~oS-(~gDbYFgc$j`zL!&(1}rgk-I z+Ko50t5MU6Tc28tzQxpvgsj@v#=euWN6h5xQUkXh5r^2|#T0Vbn=0#;iH`6QPiv&S{ z;6;K9&j-;GTnG@nNO0l#AX4T@FKy5=YwboE(8c(B)IT=5G}!l z0Ktm{7oHEICAbhEc#+`3^Fg!(7Xkz?5?pvbh?d|&fZ#=f3(p795?lxnyhw22`5;C0e)iyP>qk~S*{#;k_(ESFYdCs$kgBh*KTK`a>W*l)p6E0e z+&*pow4ujmExBLcad4#Z#d8Zj{&6UJbo;>a*oGa`skdi=?x!DUxwPxGH@{xEQM__y z!}N{kHiMH>FTAz?%G~2C-#e`^ukHwTEWdtUtGVZJLvQ>IaP0K&Q}=$daPO4pwF|!t zKHo9xP*ZAc?7PvsF3)=T?7)5dPo{@2h1Y7UvcB0v2V$|BsSQ5|=Wbg4TaWnVH!Gk0 zVc^Vj{_Qmz&JVn^?$b4=yhqP|epcHyWv6^Z|8VDM{|RrMQ>QM6Qy+}J+qIb5`|W`_ zVsy_Q{tv!o%gFHiyE3C+{d)b{N5h*gUcHyPL+IUoeCyC6>&51lrq(^b diff --git a/romfs/res/drawable/btn_material_check_off.png b/romfs/res/drawable/btn_material_check_off.png deleted file mode 100644 index 9c456dcd4364190a8ed47fe4568c780834d94455..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14607 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 P00000NkvXXu0mjfR7^$v diff --git a/romfs/res/drawable/btn_material_radio_on.png b/romfs/res/drawable/btn_material_radio_on.png deleted file mode 100644 index b078cdad843199ae2388058ff85e36b1614b7872..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3213 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 diff --git a/romfs/res/drawable/btn_material_toggle_off.png b/romfs/res/drawable/btn_material_toggle_off.png deleted file mode 100644 index ceb796335f2b092819d42f00fdb78f87316245bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 658 zcmV;D0&V??P);00003b3#c}2nYz< z;ZNWI0004VQb$4nuFf3k00004XF*Lt006O%3;baP00009a7bBm000ie000ie0hKEb z8vpy9XhxFCY)i~0+GyT2g#?D8OjC@$c>>&!_z?`V%p z+W|#+tFj_8v%>Nm4u^}};Bxu*c|M6bh2d<>Yug>beRdvRbWl@h4({%n|DM`_gW=wawo_ zWpE}E38_}ASAI5VA>*fFfC?eVWHQ=XYy0h>%1|gIlgUKt^}2pL1Y(d+#0;F}Tq>1v zTlNTu_If?pY&PPMiJ9b6aE9xMMx(Boo`LocheL4){Ee7_Dsk#Npt0R126oEP);00003b3#c}2nYz< z;ZNWI0004VQb$4nuFf3k00004XF*Lt006O%3;baP00009a7bBm000ie000ie0hKEb z8vpriBDAL4Qo3*#Dg6V1th(qA zFkN+T$g+!WL!p!|LTCxiHjqV}2Q4I&QrbL>g&o_GEnD~M&di;h8970!EhGEDNSB#! zzVp4ulTsQC31hmVJ_?A?V6P={??ngyHUZ6}dSWntHpEOl=7xt{`|vz)@n;_g2<=hN zc^`_RKoBVWx0d7U+b!JvCr2E3i2UxkDmurOtE{6E!+G&yo5Psk! zyqts~iJF-b(6)$Izx7h2>T4 zj5;ntMyRoC@bp!OJmhVr1sJ)3u&LZkHFj7`!-ldK))AYcCk;h^H3Xw%%VIFdC|Py; zrF0?)>S*UI5Eh>pjfHF4!JvhZhx$rP`j{ z*yB)Uf_tN9DO%fGSb6yZi;pJJ$U=^}Vn8byTN}tZS9j^%_n$PdaH)pMAAkgMZQI92-K5F3O*(%09 zm_@la$HDWscYDYmm)VEH{9kpI7jBYXsZUJ{NZ08eAAnadoNn{1`ISV`@iy0XBj({-ZRBb+Kp#CmT7srr_xVLw6 zJ3>Nbj(@ywvv%{joGdj*FD1Qa+=4Ze1v~;4?DZ04(KgjIb#;y3@@2`i)LHsr!qe`G zuWpiH5lY#_Ia7DZ(wS?znz(l_>yTRUdfnOPb7wYfzFYil*`@9o)nyyC>+B0=R=?Y+ ze{S{v{^E0c%atytpM56|bgt{)cm@V0MF$23KMnzg24N-^h6gPSj0_gIgxYi*y!P&W zr9J(x7=ywapS$OtebTbN_D+#=LXO$;Tbz%lu3D$&!tg*aKtt^7n;q#P+lywNVHb?e z`o4g9$?oUdmif+IRBJ80wdmyRQ_nxw@iQp&oJtjz{CjKv^2qM^*~^!$ojv7b3cD4v zL`cJ(7rT~DPusI#{j7ag%-Q)~{q2none6v_FGGVtm&=l*jY}S@```T5=-h6)X4R>A z><|A~et&JAck9Wo9eHcZOx|C6mdksuBxB9}>Ua~WuWYY-#TW#%rmv3)Js&UG`#tOX ziiyg9R!=RS)mU=()wQneVz)lrxV9y@clWnfJ8yM*sZHjWIXuOWcYAjB-@kvJaxk=b znoYm_=_vP=f3FjL=b2p0Tfr_`!B+G=&vE^pKZ~~I{W!4y)BR`qu}jZ&PoLtU@<*GY z;s4~ySF6^qU$bp~RL<`j_vO!YVpgfU_ilOX#mXmOJoClAyNkbvt-mmd^>gU`D{E?h zN1LR5$+&D?F!}f#)klS~`){VQpEeR>XmLL_yHO|l%dUGftfRB-Zmsxsj=S-@W76X+ z_4-Hi!;d_1C^cI7zCI^ZN$mQq@_W~&X4kKJ8n9aFVn@+W>+gL&YU$0*9~c@63=pf*&Yu)>A*7ir}EQ>IC^>|bo;$M~sVN`Ir@JiF}g7G`^PyJhOT z^vT#S)M`I|kb!}}om1d*OU~cruXBFCH1vP@I)DG>)!vRgS`Ix|K4fhzFIn~WjOE{= zN9DWkC$h0IaHwxK3eo!dYWudv?+(}M1J_sRi0vq|-p~Fi|7g%jX@@&{(Tog-&R==! zw6G%owc-lJ)!)*b+kifEZ(n&^dESabF8<~&eue_41_p*X0tyTa>MR@#2RIp-(1kpo v{G7R-@8s+k3=PM7wzKd z0st-fifhL=Nb#mbj}R;Xs61UCph8m>9qHbKL#{6Vcocs!jEF>QGlaAK{j|`Dgk0xp zaNao&;v$r+rHrcElmmf#kPTea5R80RGSpD$Hr%_xNHy;g9)#2)8tfX*@psO%NI%Hf zycK#bhIoblvZx5x#+fV`pH8c#o?jd;zr1_0r6W8Q7k!fctly40i}-kUus<*qGZwND zAl6y0DFB902;eIxm%Rl9f*o`QJiZCtg4jN%zpiz@8q~`kzH`i{+yn*?Z)#_{wyCjb zWtCeXKoLd@vid(u%~WmnNOM#I$o8jXTgR?N4!tE{KXeHWwAC7aW^-j6a5@XGnQV1{M2jIT}T_{@#IOAj)_PK)}@x|^M7J71-|ib4{4 z{DkM)L|vZ67}EUf76X+5xJs6SmW?~zu~Shu)o^1K<yKth~)%6T@#%)3!InuEY9BjR(Rhb*}rK=yzp{*WCPS&WO@4C%(F} zxV1Qk7P=1mBYvO$ihGc21W|X6CF~CJehP2qsHRF1jGU6h%$Af$>Q%L?g~iGqEzvca zSkEm*kqzTNVNmRAf6JM!Mylx5Pcw>#NFOCQqY8(yZE{g_E!HcN484!-5n)HH>Eb6C z!4<2~$`y{XS$Ijny_h&-!jjNxnzS)`N)18SH#jk(O(<6XL)k;ylS|9QA~T0Cy|sq( z4v%GjLc#`64CIk=5%R%kJVDH@UH3JE|KD> zBA8g8rN`@1pY`r`+{RV4;kF?J`1cE&UQ5M<4>+wVL-cX3vftiK1 z(`6jtLNu-0TfZbPPB5Wa+l)Dg){jFg%UUKC*$@1K&%nTO4!9kHtf^{mdr8I)Nl49= zz-N7D6HI1HA*fH6QiOw8<7sBGSJz0C`al5V`Y!j&sN{D~Vfv|#8Md@6Ofz0Lx}JPH z;eL`IlFlXH7C1uah~%mKC+V-iQ4h4w?Gdlh__f{J%9iMBUyfb_6+*$p%2uc;b6A{- zKg=r+BQ^@SYRx6Fxjn+gq?+3(juLc;JLdGxO7xQ0BmN|Bnr!WPyq!NfDO?Sk3}!AL zuhCU{!KLN>am#i@DGQS@wem=j-Lv5}1fpe(F2mJ5l&|_+SW#*w)FND1SA);?R_pAU zvY^}OGbu-yBC@+CsBqQQGX^lzs*nO!so&JXpq>w0SG6FZHVq$Y+!)%SS-yK#vDX$_ zpzf;?#Ue@89@q7yp#JpmyBYD9#je#N7x4f2)E5yZvdlhymQUfYx zN{!NHte-z3cscjYzAb|lX3llmQ68fMt>$AWU@%~E7!)Gz>QDp&&wVA3l{ZH}kGlGc zo%R~S7(N4|5iJjg>&AmoN7Yk05Z4pUvf`>6=8N&sF2D+W$f>PPP_{g&Z zlgX5kVR-B-Cqcmgxqg1%1Rv)=nGoa*1k-YGc^-?*%X32i)*qeN$ TZpTTfwF3?v^m1+5A94OaO|Bi@ diff --git a/romfs/res/drawable/ic_dialog.png b/romfs/res/drawable/ic_dialog.png deleted file mode 100644 index 6401cff043cf1b437d176fd85ecbaab58e0df6fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15647 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 GUG!fwl-DBw diff --git a/romfs/res/drawable/ic_fso_type_archive.png b/romfs/res/drawable/ic_fso_type_archive.png deleted file mode 100644 index 489162ce24d5306655a86c97ba537df8c38e5423..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15268 zcmeI3e{2(F7{`wR3k(*12@-^)>BNE1-u3!xJ=(%phnrd$+ms?P?t1rjyU_N!yW85$ zUjzjvA|?|J@dt?_kwilj_yc1QA|fVY2qq!{0R@Z-3QR);k?4E_N7$|#B|udfR zhEXrSI+db4r={kQ77E_a33ANJi*guPQ?Ue`O;O(YsRS>q1ez%fTBW$peEEZYW|JiP z%*&iXCYY!JZBkvg0-CxPHw)b>1&?T+?=SPFI4BSU8gEL)qH&c=`OIlu4*u4MX|pLk zMO*1JSL+E)pq!~MHXDqCpb1{`uDkxGyljOKb*X6@;IT#X!+6?j>|Z}-y0h7D2aoyOJ2RgI4epg!P-16D~AIi3ehmB%Jn z989Fj;0S!oeUJ8cnrIAUilcGlsr zI34iBvk_nsS(_aQBG0(O&YZL%e1P;4MS`71+-u#I7Xk!t z5?pvbh?n3(fZ$Dn3(p7f5?lxnyh(83`5<0`3ju;R2`)Sz#7l4?K=3BPh3A8K2`&T( z-Xys2d=M|eg#f{u1Q(tU;w88cAb6AD!t+7A1Q!AXZxUR1K8Tm#LV(~+f(y?F@e*7J z5WGon;rSq5f(rqHHwi90AH+*=AwcjZ!G-68cnK~92;L;P@O%(2!G!?9n*(PGFs+ySzKh;9J}|2H z%L}C&PrkC^(!L{y{hxuZEjNzLy8ivFRU@x_we*tf^#|Ynyrs;({hJZd#+~@} z;?-_>!Q7S!yY?KK{nEQ@`#zm3vIb)eFl^#1nkNRR*M$E?ft z+Cu(V`;op0dk>c!|H$hyC%>L^W!|;X!)LrdF~0Tu^Rr`p>-l#^Y`=OS{L5`Kf8J2m z**<0O+?(6_PTswvedD^&f``tn>s2;goUHOMOgw$=@huZKk8CMf7OH*n{H`mJnfvQk zojiU(d~DCOwep&si=uCi8@r8io#g`ME8e?#W;nO;Xw_M3)8#Mjn11k@<@eN-$|aL- nJba_+kHF;pCD(i(mQwAP?~NuaPyL|3M_XUJIIv@3%j&-X1BbK% diff --git a/romfs/res/drawable/ic_fso_type_audio.png b/romfs/res/drawable/ic_fso_type_audio.png deleted file mode 100644 index 17da186f0778563d3516bad64ee487305bd287e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15599 zcmeI3e^3alyjxVz19iW~|Vju60#)kc@yy<^MW?y>z0{5u?@mqbV(oefRFTJ6?`= zXq!&|*_*lBectDNzVGhmeV*s`pWR+ox-2DmPBMmJDaA$ha&Vk0fA35JzhcVy7I2vE zEn4Zruo-vB-)Y#hzsSTe%K%qdB~>{}NLujZP>fK`<^()mFdM_HivnJXu4N@vHS6N| zJoVMX$J8p0$y2W|ItYiifUV(*nnbpuskD-As--QAdXX*J8X$pyhm|N*z~kn9WFSu+ z)Fr{WJdCSV!6{O0o;qJ%P*vq9Qxymzt1{-~YG@+Yq%v7@v__N3U|Ois5n3Zo=y5%v z(dkJ8K^k$F&#VWqeaduaRRTBb*XBhUdmIe<%J@j@O^o_k%f3)m>r;q2PiMD z%^~o3K@1&<@z#s(;H5D%&bnC-%S%2Gr;Quutq~-_S0hZaWW+t`!eCDvj>y_^uh-*= zyR=U#Yyb$tNaIHODjU5lUe5Z2dXZ)e8^Ep1i+A@D=ZtR7I3?LL>9w-~ZjvzB6ABBa zLTHDq;9kiBk)4!xQbUMl%Ge^Gq ze|L#EG82|6;(Q=K8)NboWN)ySRj_sAN5>p)F4&X2l;~sSipx`vm0YBWMS7GR6Rcd4 zqUCO8qve8N87n?+GDR7ff~YjP2v;FSE7$mY91 zPE4iNL#pG$V0kMpcYincKiCK#)$reK!_?~NKi!5nJ?a0q4bd#7Ybf5uGB$iHfyV}> zwsWEV9A8;u4Pjy>TIIhZ5>zqBC@NSzS}Q(bEV^=NewAE}?)u<-la2r%Tr2;ELu1`= z=t^{GtQ!uEMlR<$$)*dh1`WqV$^V(5hF$?4?^s)|(Wp0Cad;SxDB}XGyUNai&g+x= ziOw93fgN!JS56if;|K?awkqa9ELek~QSg`to-pyqBj$wijC8TD^CWslU#BXhr7Qvg zL7NILG#|*T;DUglO$8U459C#FK|s)^f(y+D@+!C>AZSy;h2{f!6ZTo4enso+BMfxHSX2ngC#aH07?UIiBf1Z^s~ z(0m}Tf(rtIHWgfGK9E`Rd7K-(58Y5%?I)-xF8^C z6NxK1`qC!LgEuw(;FZkQwI{p4>zpdOsN8{JP4h8q<7NySxeboP7*FI{)Gb=BK@ ze{$%eW8lw2k6oSr`5%vOyK}cC$$fqA}Ms6NSd3MLqZI`a$om z;Vb1=+b``NIc&{HSTB^m{h9NH)ckpi(|0a-K)P?0j&9m8ZTY~ncfUOR(!FbrUg8|t zFMj-VxnbY_-lpEw-~Na*{==+InBA3e?#A40UDHRKpIp&rFzozl@YKPRGtMWM@9#;T zKDv38>5kLaFQv70_w{CFX3}LX=5#i7;mHPSw`Nh0LCkYnxL4T9RK%KGb_9J9|^_b3*!F_qsp4eqzYpdo7UIz3xU?S;M<0 z>i*Qqh#85CPrtfaY+I*)__BZSgJ%}pzi8Lde$P?9dD@8d)`X{F} zJpI*o{3%}^{^i!ce{<{kA9vJgFZ4MHFV?>->4`UAPW<%qmM!Lf!><#XTP_9;y!4>+ yZ-jaDTd!M>4?CA0+d6zTJEbJy_Q>cx*!F@~T%#xNJqS`5D=sXx?_aWZ)4u>r(P3u* diff --git a/romfs/res/drawable/ic_fso_type_file.png b/romfs/res/drawable/ic_fso_type_file.png deleted file mode 100644 index 2d52878418845305caea39ace841484b13008ef8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16369 zcmeI3dsGu=7QhEYl!ror^|5LVQCmfanLH;MLIgnt6tp3w(QTES_UxX? zIeFage)rz_e)oQNCVzaT^D|}-3;ua91VO`6Q<7E8_gL3^Xdv@x8+LgU^A%)GSzw2t zkVx0t58CqXCXJrbTr+XL>Y`JMK&t4C__ya<&tt8f4VYQ;lvpSX39bEoMw~7jyvP|ZofG5 z+ck{vdG0BW+<1PXD==rOldvL! zSQP2cNJn~Yta&z*`_gnILYXKtWpUUUJ3+s8Rs-#z?FM>)kY4qGftj47r+Z`fd%b3J zzoG4pnfVNYZlwKM+SP?t3QL044&s@-_|En5qa$%WZ8H-NrO`~}P>9u%qdJLIkeQ zZc|@1Xu5#i)K?9f_FBGdF*=mO?r6ZUkCp4$$8%%OWFFHgrBosjNfZb;3|h=LIw_MT z*~r{M?XEk4P}Xe&D*82CFp!~-qTA4uDxU{xclY#+GLP%bKLy15SD{a1_1=Ge${)Qe z`YF8vzPKO&5O$Hn#m)!Na<~8>>>`JYoe!YpZ~;KrMGhA`A3)3D0)ViK94>Y~fR@7r z0AUw7TS*!cij4i^A~UF2}F^8vIRE&vF-$l+q=186y101$SO!^O@A z&~mr{AnYQCi=7XkGcxLC~P1smY0IXUlhQoubFg9M!nB>-VE)$E5pz5EC6x z9B?%Zx+>ehV<#`pQZ61ZjtE+^+AwBP#K_^V%A)qEH?05ijs3Fdn%ub3IhDMLvWAK= zs<78f??J0WR2BH5KZmZ_{94=;=Z5vNGaItL-IxFP;`PFhXlp2#{_SVymu#Ll{GQUEe`KnN z{N|0^sy2Oe*O9epsR8gHehCy_6Bk6k2sIvA7&0P2Q?mJX$KT>U$*Py{f?r!+TR+mC zdF<0cQ!kD;hbg<-n|>KL3g~#7w@>`3peCzM868>L z`Sqedv<;tTZk~GMyN(BqmdIvJ_=f2ae??7m{i*}Qj|N9Ko%P#tVA$PpijwimKVA9G z;^mJ|p50}?>=^u3Zl?{G%r}VrHMuRQ{`z)7Qz3J}dW3&+16@!&J6wF{eEGcWj>epj z%wzVES4%2RUP<_@DKvYX-@%ILFOE!Y-#t|>i>$1!9#Xo>|L}Ir2XMx3TQC3JV%)Rn z_Z14P1Eh-nvI;)1d62i3cl`dy@mIq5>kbPW&R|99ieg&YvuAF$JDc zR*gN3Z)?8z)$*;XKW^d0-)<{vy>vYCSZt%;i3^pw%F(siW%&u$hBmH3r5m;%YV(^C z^wPNfhP+uBrPKNP;3ac$fgJg+>7BScCE7UYGSN{9*4tPN}46(WihJhJZi=;h}}XN=rE zxVD+M(=ei@qdD>LBh{Oe>(Yv@%rX;2isI<~=gM@MgUM;N`Q_1L6N&bqOIhcQb+_tN zL!pK>6*uQZ?wD}>fImjR{_sxI$?UBAYo44c>e$m!ec8F}&Nq7-E<89_UjE_>8J%67 zPnrzvOB{2Z?eE+z{`ldehY#xy<=uSL+S;1Ac-fr?r<0ywcZ!GnV25hUKKyIugRcqK Pq5RaD8OeKRWUu%Kv8`q0 diff --git a/romfs/res/drawable/ic_fso_type_folder.png b/romfs/res/drawable/ic_fso_type_folder.png deleted file mode 100644 index b61da277f817ef5a6371fcb0024e9d4ff6d5acd5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16003 zcmeI3e{dA#8ON8VK#0&JCV^BD&P(Y)U~hNtZg20F%K^5zmrVG5-!G}>yZHI9gsj>@353K1AV6i0}3phIECqS)QL+>gAu zyhB?%op$zS?#J_f-{*Pu{k-q{ynBD_<27qm&dRwX2SJcoRh7Ka`&x^gkh>qvtIWpN9oqihC(q$o4BL}$ba62ncH z2}g}4+Kkg?qi*b_cjPF)>|xGJ*E;VROQ%?6`W9IZ(HItqL<|v=K@2xzgw1Bda3f|k zqDl``>IlkA6b(ux@gRvfPF`ZeLP!?GpiYg;c*HijOs`iHC0}FX3WSo0g3=f}g(4PZ zLKtDdu}n@JoAe2_h5fNh<5-OM^8r36ONt+n(J#~@%A(XFPLeVyPnuYn6PGKQJLB~R z0vS_Faz(p>U<_$SPs!a8;;~v@65GNoU(v1%r6kknWx<=8oNm2P2!5ah&&w8)NuE~)uWMdO`}%sZ6z zNTIkHHQ{c;NE@v*VMcM1#__Zu@t!~om≫jFa2x}a%N4VjFQ))S1O>>rMi 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 diff --git a/romfs/res/drawable/ic_fso_type_system.png b/romfs/res/drawable/ic_fso_type_system.png deleted file mode 100644 index 4dbe7c357c5abbb968f850ffbcf50faae3cc15aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15828 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 diff --git a/romfs/res/drawable/ic_launcher_themes.png b/romfs/res/drawable/ic_launcher_themes.png deleted file mode 100644 index e622520da9ca0740ab636fc1904923dfd4aaf4da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15494 zcmeI3YgiLk8pj8$Hxvbmmt_}?5x1ZwlM9zH4UtQ%#DZc#1aw`8WCA0}Ovp@3K-%J6 ztm0+C+VxVcwc3ibmP@f!(aJ-y-nyk;>RNGeUGP$CpFY}c71^02B!`>R?$eKxha@lO z{QvJc^E>CfnJ-h4GHZsfPmm7;LB5(qwU+q~WZy#uF`pYsf_<58M5)uSv3u(ZO7zEj`9x6RN^HfS;?1k6gZcUl;T)x@< zx6shqJBu3M5l#22xH#eTrFZts!~+&Cy!h(Mz4Hf;vDKeAJ;$8##Je*#|IQ7+2`g3% z*>(Hcsauwl2d-H6qh+XH-bDiW>S0mz(3eB2z9wo$K&>IA|2^3@#rsAO|2N&W-Ln_d|xvC{8(E!H5~oMWk?gIh7pr`w;~tQ?0SL3+!y5zm+Z`Tf0n#hW?@ zpU6Ibg+MHa7H2NK`$S&ZcIMjkjef-$g;TTQ6lrZOlh^NTX=`n?|6v4)7HX@%ol^Hr z@v6ITA4=bIbl^`n;S-IsJKD2mXG4%84^PXWGm?`K9cdDydNLam+f8PMHv}oC+0Cdf z52FRySPo9a2*3ZdUMRrzF~WHf$&zGq0+x#>7Fw{>!dYp$!aQBHUN}wVqqHMT0ux4~ z0=vmbP>4N7=*WvOG209a1r7g2NYx^rw9^A3nqvVM~ZZk$S6Tnv{)Ju6%`&e zRUnf{BVdUfmPaOl1S4eQbZof{D~yth(rP;aVmS{NGA2Tp`foE3WUw)$pI*P z=3vf`9;MM=l?BuSdGtEOKw3;Ft-?)c4hEZv93|{&1eESwg(Q$h(!xv&Q^}QZU(J2G zm~x!rtj4Y-(9Vb#gfb^l!4UUk0c1cL$A(2T1Vy8S4%4VrOoJH5^@vm_l}1GB!bOHC zX|^aF4cChl(F&OeH7L;RNKBC}i(n?%%ib%sn$%g@L+47Z|Ba~ud#}{9EjUv!Q6r#X zXa8G==p#+9p%QV5+2#en_Gb=(qa>zc`Q2N)I*hoZw3$&0g|P=WM%Z<7-L=qN>R6AX z03fK2Es81~d#*6O67JUOnU>q>vf=;l0y^^p_WsAPIgtN8#`gHBs-s`DkYa_z!1!i8TV-2KAxL2EJWT!rZ zT~njgp^uVDm=`Q%KV4R^yQekt*;cT-r`6+}OW?Fh<{GU>GvHzixU;&XGWQyoDl#HM z9-)MLHurR-;C9TIp~ji>OR>9B#$1=f)3c*bV`hIwpu=T!E){TB;8;m#D|64m-0r~c z`^=y;bKtq6-#IaujvoZ1!5QV6!5q}8k{kYixUwj;BkR8IAb0cCn8Y5;{s`L#yl=gM4*7j1=8S*d0d=` zKmm^nq`?{UxHu7k0v;DggEQuFaUuc*JT8z1XUyZ`L<9fdU>E zNP{!xad9F71w1a024~FU;zR@rcw8V2&iI+Qd^}GOVg&QpppAJxP+aBj&pcZw&?Rb< zA*gT?1QoAw-LDTB zG^r#z>Fg7mZSIlw+viuV*Q{KS+g^Fxdgt2G`(Zy^r*@$29b<8;T_4;myz=xD?w4>(+92RHH;Qfc+Sf4&tQWEy7m zSv|BkJ${EG(x%OdnNhV}bNzmFQ1HtK@2>sgaaipqr6&SAX06*o%_&fuArtTc{^VSUw68&$$m~UXPD2Jq_3Cn z=@@XJw7vDg(KRC$g`^cdy@WoBZ|K;bv+nr5(VxX;WDvuHx0gQ@*Ce*RRqKaU2R*>{ zp1Il95KhGpWtt@n29|5Iqik8~l?(pY$L_{Y1NivLoz zWkpQX<}ICDFO4Jy2Ys%HoIl7u@~qb{koThxU*6w2wIdj+Zv5`U(XWgxWuJ4^OrNE$ Ij9;|;e;cxG{{R30 diff --git a/romfs/res/drawable/ic_material_light_ftp.png b/romfs/res/drawable/ic_material_light_ftp.png deleted file mode 100644 index ba043793128a3f445875fc7a4dec0e4bde7a63fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15493 zcmeI3e{2)y8OP6*A886&vP2D~spMSBfEN4i{3DKUj;kaN#9JMHIS`mMIN!ZaZn5u- z?}8m?Y^+5sWk9vAn#L$=wW>cz+cZi=MaNRYB*@lLx~*!El67UHM6{%e#>OhNt=)Te zVmoh~SCQIj|Gbv`<9WaD^E~%{-sgFJ|J-96Th=ctUQtX@)G~jwZ!`QZH{L~y;pgGU z?pX%EZjUu@*C=Y??~S+V;L65tQIvZ^4z}rSfd_d}jamgs4FPL18iTVb%JV=nCWxIt zH-|vGtkltyr_a!4S*oMAIRk7U)(AS}=2RSPNwoyURHx{c=m)&To+J+gM1d}tlhKHx z@yR+m9hZl{jbVm1r>E$hb#%Rv(A*Z-Xl_*F!0fcvSVXqQWp=r(oYUoUxYn9&Eazlc zJ7Z@pHaqWNd56tB^`gDS@Ruhph560CbyMk}RY!N|dW>h7L?U5L*sW^3o#EVWH^bT( zo6Q1eShQ|M7m^l5tIhx#y*i4<*$9i+%4 zg&4zGS*DPaBxZeLUGYfz(j<`q5fBB6u0cPppkJ&*)m5!Sog-ydo-;ArlRzMwyWsUk zqXkoIdea_=U`m zQpoI(2dGJ0v3lubXhnKxGW*A7Up%<0=9;=IYcqDTQE^xuvv*V#ASHy36Z=x z##$tpOt95RZmCwV)P^``EYj<81!&*+C zY+JzYnWNJ}L{6VjF(Ix2qyOsY=?2UmY1tFY@JaVEFNnt3<`s?h0FsB9F`B!qtml-Q zJbOFmhaeL_&wcJ3i4LIP+A9+(J!|eDni|#M7!j(5$MQP-Sjt0L=O+f+7W zIM2)Y2hAMsTj0$e@YXn;cBh9yhf$A>auP(^d@?+THRC+7!F8p5P@$mX_PHGMgek|& zR^{E6rF$?l3UB)0{U(#W;hfc;*;DMx{E=JHml+j_MUo&u@Fu~9=YyyO7Xk!t5?pvb zh)QrFK=3BPh3A8)1Q!AXZxUR1K8Q+iAwcjZ!G-68s00@R1aA^tcs__qa3MhOCc%a0 zgQx@-0t9aoTzEc+N^l`S@Fu~9=YyyO7Xk!t5?pvbh)QrFK=3BPh3A8)1Q!AXZxUR1 zK8Q+iAwcjZ!G-68s00@R1aA^tcs__qa3MhOCc%a0gQx@-0t9aoTzEc+N^l`S@Fu~9 z=YyyO7Xk!t5?pvbh)QrFK=3BPh3A8)1Q!AXZ(?y3=N{n%3Vd!e0UyvjdSLW8e7Msr zHg674RB9zf?R%J_Zha2FKcc8EmZCo1MN#}uDXKz!V&}Pa6t$qt@2d|cM<<@-I?L*b{@(t`twGo1+lOAf@6i+c8%O@IWb{X+rPRRM zXU9JIYQweX<&?kmSk?Y_=o{s$oAxiS?*92}{ry8XZrFNydZ?HC`aWB9-<34{s=d|bJv*e0;p9>cS73H=RAdvhVzY)j_9Y#o^SZa@VDik#Dc^-%y^sdg^y$V`B$? zbMU}ZOWxe}Zn0zEnr9X{*7r0NJ;#lAo__yLdtKe$zW0?43o0rqnB=57yuA8yuk+d7 z$sbe<-QT+RM6WwmSohR-cersUq z#W&QlyHb-+uk^>8eAX3W{P?dT1Mgh=XZMxCr{5Zw*!|LbDX*z=>Ed$}6BC2WuiX5_ z>d}!MJDpdbX+JjJ{h1)kANgOs^slm8pMTo*k9}vBQ1>6ce8(q$xmspCDC}=)@%1(A Gdg#B-WFH6s diff --git a/romfs/res/drawable/ic_material_light_home.png b/romfs/res/drawable/ic_material_light_home.png deleted file mode 100644 index 71cbfa986f585dc4264f0372b1d2f267f1c6d361..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15458 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 E7bIJ4Z2$lO diff --git a/romfs/res/drawable/ic_material_light_sd.png b/romfs/res/drawable/ic_material_light_sd.png deleted file mode 100644 index 935ec6b07b4191d8f3b0326c70ec8f1875972e40..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15301 zcmeI3e{2(F7{_l81`Y@UA_P=wK}FOvwhK#WA z&ipT=B4i7UYG<}hQo zWu_R*0`_V%p)C?_vejrQVB;MgT4Ft3o7d-X@?NjoJJaT3ojk*G49C(gPH?k=+hxnW z?7>R-&7YEELUX7tmkwG1d%IyI1%~PB>T+~(4lUKjIDI}J!@3xki-t33eT8a>XBL!I9$kG@_G}AlBsp0wi6=AAuZ{t zx2#A4rWxp3M@j;W#tXrlOvODC5YZYozKs2;e zi>Aec*(z(wBed1jWU9(GeUYNdT9^KepFv%U`(2C%LZShJupYfM%hOI@i_0Z&PWVT& zZh>VjL9#QE7-3CTVk=6;s1kUeHHKv-8Uyx^EE?jVqFCoz;>eN^(^3i12r3D&4KPWy z&Ce7EEi(&MCDdqfEd?(N1UWxbYS`kHsWhQM)eTXVKtm`92ONqb3qC0pA#(WLy(PcaUVEGS36K~?UfCc znKf__U5goAVhU8Z!3r5n=Vzn#bwk7Z8S}RwuKWiZp^KgOwhe>ps(ai9>k0M$Z9}n& zrFKzm19Fhb7jS-Ha66aX&%T|N@7DgE=r@0y1lYx}qQp%1IQ>k&v0~=z{D|3&@s7-V zuZx8*#%aFiT&*(dx)|?jl~LEC=R#F6g05UPWZ2?m{(@$Y_XY4~4+K3t&+&c+9Y#Hx zlr)G(LJB;Gb@M!N!F6SPP@$yb;(;73Le4R}RhHYbOb=#9;Y}aB-((6moc-FfaEjf} zFU1wz&!|W&k^}*QHwi90A4Da%5FmJy;KK7kRDufuf;R~+JRd|QxDX(CliDK6krXkfEd%RPmaYJg)y=tG{$dw;m^8LuE z-1$Jw+vB$V-87+cZMWl%GrwKyZhLO*#5p~aM^?P@e(TB0JCIv<)_ILiI>+*8B$Sw(*5y8 z=etiQ%A2>SU$JL54sD&W{e^~(Us5CeJ!K!ou6sAUb0@O&vnBhU+IpPleh&_tGw0f^ nJLfwOUODvbte57V9Zii{^G$Ev`aRps2W=Z_8$(O>>>)0CU*h__upzL;c-Hx@r+ud#L zmWaHDmyiGwBQY=ukVr^0WI}|Xn}}1gn4m_D@ec{a1W=?MhTqN&8%U9N z8&=!>v|p|fI>q{aMQG`72yp#foRc?HdWv0f1|*0G8cW6_;i$^Q-G)S72E=+ZWgrt0 zt;=nw(hHJ7eD#Mf4L2vnkF+86^q48F{?>ZIw-T#>7-~2Ww986!l?E|H8yUHstc1z z#`Aaul~Y7n6QwAr=VjZa9?fkq=!Mds)O|(dbfKu4Vh1QvaaN|xCYs7;;<>a(?oq;t zq469ggoTI@)l}d!=k>{*k|wF0(j+C*-IEpubK>`>Yv&zrB$BtZs@3)a1SzC>oob*@ z7N}-Hm3kCTsO<%}vM}G>YhoxfIa!(Xo=L|p#KlQ$dQZ}pSPID*a)EngY7~LhBqbn8 zVNY_EHI5@BYibg!iY!|#MtLcwE^$%0mdt!-vI$;R6Fgu&I*hd4Xm$oH7RG93Xs403 zF*FS&Nh))4_$6Kp^=0L-&;i;4tc=+V=YW*CIl%tmS&eXnKJPC7OW zY4p)QYKc5)CX}idRj@w$pmhsYZ{jR#5xTQm$2-De;!KiRMHTcN=QfOQx%45HKBII` zV&^g}r=MmXPTvp$@1n9=Gs8+dQ#SGcPGB-WWS_i7tW$_SXhS7aB~+6qQKgU;V-=yQ z1FVUM+3U%y*|yyDE=oWB!(#SCBwMrRC$a6pik�F8xqrz*+#~ViR*%S>0+?{J}eXteo&b4NoFGoeqV@XxZX@*?-$(uYDTEIhs;3>D; zt#%ic+nnoY6yriT=oP{KRrOVA8TaIN|dI zg+v4kFfNz|GRC-&h+qN61=B#r7#9)|EWo&68ps&qLL!0%7#Bj0>iLj4>`GB3OWN!8DLD#)U)#3otI21~SIDkceOb#s$+r z#uyh85iG#CU>e96<3b{W1sE4h0~upnNJOvzgbN)Zu0r-yPJNWKh(Z- z=Fdw9_g$;~^60ZICC6{y|788zi=Ltjug#r$>Pqp4!B4O47~W{V`T3^OBb9;5o$l$| zPVf2QymQU|WyRNPXRjYyI@r6VKYX?2srtqHP4}i7PaNMGE%gre%|13*HOpO6^W40G zSNFW`v+i7Vw)ts=JFwj~v^945u-*nFz0Qd=CZG-py>bA}Q0V9k!DgXcg diff --git a/romfs/res/drawable/ic_material_light_settings.png b/romfs/res/drawable/ic_material_light_settings.png deleted file mode 100644 index 0103127242d8c67673e505f2d11925fc7d5361f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16258 zcmeI3e^gWV6~JFsK}0>p&Y@OWv*4=R2=7;tmpnrlB~<9d3M7I%UFDJY0#8C-nmi=n zvDJ1}^l;!*huYR(WA#U|+DV%u%KWiY#coWr;{b{ZwRW-uwN2?)~2P^2b}VVE){R;ZKJ{5Hv9**_bYTKIMIfg$n-{HkM}!ALAU! zi(C*CzQ_BPEw4_-A!yD{)|AC(rOqQQTtO^Fb2&_`yTBpLh9G^s+d*0K8D5^l5fzQ z{F1_V?=Y;8`=;>uaf$?QKzUZ`0(m0mWaR2tjnaZ>2sxpPMb!kMB4Xqif~sKzhjB!S z;iL*7RhYd0r7(mG-}Fw}N~Rm1?++(Paf&>icaSh#R8$mOgvWBuTo~2qbTER!7^W0v zC|$*No^mVgu4uoL0Y65@WpT0&p5^RvuU{&ME9B!83U8pnSO2~W9D{-Eu6}j`Mc7R_ zU^EtiM>3)_@tiA<8ztnRdep!|PEu0`V~@Dr zf`So4yZEG~0)l>|BU-vl#SR8eXIxyN)50Vz6}A#R((ZZI8c5C%PhQQaYiHc-C}v)b z-^^DE{tW4by^@JehT=J=iQ{Yrf0Zp5Kqya4^i`F7b|!15xgyuk^e|`=$alaSW2AV- zAk-tFMAS-DXTmTNM@f}hiKs{f33B3}3HV6mXx3Uh#0QR;FqA}uKfz_dN6<{rhj4yq zil-jSOYqzvKeUCka?S#ZH?Rd%E(1I4xq3J-7&LRRs>npn#yN#$F$P=@k2D-)<*PI@ z#qQ!MyM;+H8iavZmZixYHKEhuRzituF|CqDRJ4-9t*DYvTdV|)Gn5+D_#mdH2AdCa zZR9M4-qRwu_BR%qvk2}3%TlSW8eElQRpK;4DN&RmlsYZJC>fO&LvY%XgKJa+fr6Y3 z3-qFs75W}!3!3BI3oQiGSWzv7)0i>`!!adBsWeJTt+gsOIhd8!<)|3KLJx!(!OgHx z$*fDL*5aTl7HZko*)o_{hK>#>Y^<+CIVh)#@t(gp#lQg^Y-xiX%WLC1$0TL(_BMmX zdpsCni9<#M%Nn%oH)DsV^YIY){Rf$k9-}Cav5OrqzboIY(Ve(BD_=x8nS@-SLLTSj z`@Ie|1KsOk@3p|j{s$7_qL%)fX~-cEMop-cn3g7#4C<{!3bP9RiNXks($H$HZXnIy zEe$w{;v}XT=urQ?Gz6;Hl1JHd8QK63>}+7*e<^d1*AKtz0UF`j@_&#y|4AI$69@Y2 z@E)r7-d2!8{}p-x9v(Ips2TtKEN>sR75e6Dbhw5tS#hv{=Bp8C#Z-6|SHz@09@6_S=Ap z5e*lOW*DUCH}t0}=!V+YJ^iD??Yi(w0UrFRFs!i-UVpyNFM%C>pI!l9QV;-$xG3Qg z=L2XZTmTSpQNkt82hd8m03hO`giD+cpp|d|K*U7}mpC6lE8zlwh>H>~aXx@n!UX^k z7bRTcd;qP43jiW6O1Q-N09pwb07P7raEbE)v=S}=h`1=>66XVGC0qayaZ$o0&Iiy+ zxBwvHqJ&GF51^HB0YJn>370q@Kr7(_fQX9{E^$79R>B1U5f>#~;(P$DgbM&7E=st> z`2bo87XU0EoCK;S%QqXeC?#5OGn$CC&%XO1Jyi%LH8dCpI<>xAp${HvmuDw z1VK}{b&F3t4?#~XOfe>y+-+rlKb`*)Gr7K}ySSsse&yN5!v}ZFY&t$QCGV~MrDGDp zPn^o0^4XiQhrS-0?nv8RJG*LO_^*w}-;6z;{+rqeepc=N+O!B$#M9sJ z_rbM=dv4$FI#Y#SiR!#y$o?qiSL3tG?*FJ%7BA~G-QJ1TS6`KN_cd2mR%V>Cx-wf@ zTi17Wb!8N9jsJY}x6{H_KD7y}KVE(4(%}P&nlTergh$AySDgGsLqo%q%*@P=N{`3$ z?FSyXG&$XD&NP;l&U?bDdV2YpJk^m~?LS;~DLN@lpSFSdxq^^+T7LXO)7K|s8rpy7 z@u=5UB^$pySaSH)Gt1&b?~F~j*`AHutNJVtS$VdttwdjbdDr2)%eSZPX^x#3`o;mX zr#9p#ioM8$(+kb!_pg?_ZX{)go$0>I^|fs2?d|RS)7{3Y(bK0-&;3GsWM6f1-9=#$ zvI~pN%?<0@-mg9~#q{u5L8lUg6sJVfD0|+3)OZt(sjDdA2Tu+Pi#r(^oUnn=8K1 zHkP5MmvvV_M^m@YY^ZrAGBUCqs(s~h{rZ%3w$e{GLI)CgGRjn`EtlO0X_Q4D+jDiZ z=Z@#xxpUmCh?I45#e-;};*PyKsd)UR$VpHBGaJ!Pd{A@f&^H}t-kZ3hr~R3p_71A7 sr={(|t$#Fh$OzenjT;~Rpw9p;l^BY z;yh)d(*Ajt?DO}1-}lG+eBb-t{d3zJRy;bpXh9J{5VPy+JdNOb9em#{EC9dfd=Jh8 zm%E~MEfPW8eV_8pBX;k3fFPKCLQ}Kc?EN0YiD47#7k#`b5sre{1mSu(5oNg`FOxpL zRftp?#t#n~NWoueSZ(*x-e?WqCe&?;@hdm2XyP^nIj7(7u)D~WV1R=#FSBGK9EwOx zqSB!HWx#)Bm@<&+6ggOFs8#}!&E5vGMvU>K-Q+NGw4DjI2ZQJF#A%=E5eYQ&Hj18MPYtBcAtoYTr9avjRHu8wfL1d1W) ziUdlf9^B<@YxZbL5fa>>C?>z)XUZLBv(4gXjjWYrjRBjj!sxSBSd2Vx3DB&A_gS3f zE=oDBz$lZ6;z=FNj-g)wDnq64UN2K0kz^Jupx&eABY=FIJ~KxL{B~o&V)q#p1|Q0-{3cEHYS^>|ZK_wprWwms z5kYoalF^`Hot1L)*X~R!!E-9_cG&G!yNiN{VT%SK!H1eX0=P3tNN`!P0~^;mhPESZHL+@cwz+)%T)Slc}k~D_pz_?Pi9G9rB}!o4gvu|ix?N0 z52VGoARuTF<3jU+v=|oz1TA7*Xg-h@J8aiRG@T8s+G#ke3KXc6N=^MSM&7X$DfS^SruA+=LLU);6+oks{F ze`CF;x+(F|*l2a+bk*GMzjyV{+xARzdvWj3uQy+RaPRPj6Kr$m`-_`@S?1X|mIySj z`c2!ij_TUmpFeT3zwzbP+r3x!UH!w+qYK}7-Myu3ylDOvx#Z2w=ML9j{`}IFE33!m z3=Y=*`PH%|J1+mtv$Xbu56`bSeWU-t!Lz&j7oYr;**ohvF;Q63GrO>)*g=La?%Qwd z*t$McHPXF5kIf${P`;W{o1iJ4)Rc-MWmz@yrHFu}XWuFA@j9-# zO1AAkQEHe~o$}f*o0JqmwP;w}0`(2i%sy&fw#(CdQ9= z1`ZrJ`}Q}!b??<3d$^kALq|z1zd+0VHN}o^zJDYWSGUY!hqC@R_6*Ot8(f1I4|;WE z&K0H?09IDIC?3D7ujk;ofyIS=q2ukgud(^Bjc#9b&r?$HlzG8rr)BNOkGp@r=C$vB zws`xgz{=xn)OODco*pUmZr#Xy|L#(d#)rQC!Glkk5|lnbmZ3j!=vv3l!ta!j+`GV_+jw;_zh{`i=98H+SNbY-n%9k9BBFa z)^8hzepOL1T+nmslNj?NNW_0>QIk^W!4Yj2{QTmNL)!Y^?tgRcN%Fb9HzI4-cRyYB z*EPW-@ztwLRl!ro?l&#@^up$4uN0Q-;d1OE-Dc45qEyXQi_9)5c05x$c5UL;=?BQ4 zp8d#trR&RtYvJImW1oF#x_;s2dx_Xjo?iFZ(jzzMooB?8ul&sNT;pQ{9qXR`NAN!v jyal@^@(E&cGM_kc>&%3><4`v^p+tS{3eW4yH*EVa6phRf diff --git a/romfs/res/drawable/ic_overlay_ftp.png b/romfs/res/drawable/ic_overlay_ftp.png deleted file mode 100644 index 1aa7560a34d475baa18d5a58a9c96d1dce0de8a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15421 zcmeI3e{37&8OJXRwA7^(sim;3WuBXg6neh%{jlSc?Go%{F~TKHLP!QHx!k>uZ?%0_ z-=%RPm^z^R7^|2@*AB`UqS6?OM9UAAq5=(+(siXjgbI{SWiYmN1=d5wDVYGCc6m`)d`&09?MbX_MA@Sxe5-S#x12$XJ{hFqNEUSel-7+i~Syz26FZJa(e;H9D`1QBIUKN7+G&OkOq^ zpOA4PFPe)3-taS{bI~pzSDtG;@T2|Eu%*7!Fb*Zd(B^rpy7Kp(87@%2^ z=7Jr*pu`0wUZ7b)Vp&I!QcomCLRYoqV3io6DDj*#hNCAM1J;i!Tk@2mIQu%{sEU-- za~au+Xc@T=Fh;g7#8es`Jh?G%21nEjg=zcq{@Nyqcy; z0Wm2Df_#AP<&z?f8^ zC8(m1RD~oh2#QSmd;q5OH2_)>8vyHPIYCiMs|6D|f=x(tb54Wjo}6~{vFD<~WI$va z8dy=F)u7DMykGFsLB9g%KtNR*{6QrE03=mHQ6Oy%c>p!UjBBk7tL!=IrcGSVsUaxE zceqcUV;}{xxbiB6D)yQ@h^Z&70XYX6```|lO68ZLj(0=DhZy_6Ag%om7Q&QQf4yax zT2+0$Wf*rt{r{GsvWu0Job3Z@gc;kwV+~VVxzc)$udK0Zomhz>`_D;&RSb8OT&x~n zh?&q<$z1B+WmjXmzu2GmaY3GCd@Q_1U7a%O+KqR0%BX9_^Ome;Mc{c_94ON4@Us6w zOUHXVeAojKzbNuzh(ViCk5(-Y(p@nPp2Mbno^WtpMIThC>exM*qeB>VEG?Dev8-5w zrB?XR2cI{Y@&o6D@+_ZX-{ehYM&D#qBo;}60KuCC7oHEI5?lxnyh(83`5-F6g#f{u z1Q(tUq7qyP5WGon;rSpc!G!?9n*np==22t8$MEG&x6;y`54ffJ!I<7tZ*%uFuEbN(iq}JuW=w$7*SEg_K>9MEkKiq%(bo=$Uz2FbW z!yhfYJpX4dHK$?vy~)47Bit3)wEu~VUtiTVf6sMmAK80v=kVHt5&HGQ9}DLfx3;)8 z*MHiy{LH*B3x8VwNL|hL+S@OF?N{rYMy458r#76NAHT6Kyxs!cC*pgx$5zbTRhY4L z_WTX!{`2jYnx{YcD8sL;dHK|-MedrbH+^t2zIXZDV`mD{EpL2vdPW2Ft@zTq-Hn&0 z2YaYHzc}>W5m)^Q>h~MYZ7UrA(U#5w!P7IIjy$vGu}7~M`N!d-Ekpl&e8;TYH=cP_ z_~2iyh4vLcq|$#KJnz}}d|+L(@@_P)cE4vGI5ylld}ZC=c3%JfUE6NqKYO@&^Tu5t p|04U+j@7d-TzKs~S2#{R`~J4CUeb34>=%6FP0M1>-q3URe*vgA1zP|B diff --git a/romfs/res/drawable/ic_overlay_home.png b/romfs/res/drawable/ic_overlay_home.png deleted file mode 100644 index 898c82ff9558be1a38d50645ac5740632dd3e9ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15403 zcmeI3e~c968OMh-3LIc;Ps2(pEQ?gZo1I_#Ylgk0$L_%$Wseh<<1n-`JM-S{c)K&( zojGogG@&imH2e?@f+Ymv5A?KJJ(?P9)2g6y8kHD8Xj7@dNNN!Y{UJeumAHLbNJwgUd%W`8E#2)|!H|5FA2 zOwP8fF)3>RA}63+E*mAOkGfozJ9oQ_44b z3UMWPZVxjacVUXv)97ik6S_O&?e3_N18%=JKr3t@=njfr&L0f=f(>q-<@^jQFak^S zg5+Z*AMY+5o=6=$3+L1>X+>;tDIK&LJt@n|N(|H2*XQjMyhg5@;Y3knSf1f|8qT22 ze%+Guv~JER1}Vph0aM9oSxYl?w;fkb8ogGd$73g|9HslpWGjhuv&0TkWb$&B;k+zU z%Slx#KH1(}x^QW#!hkf$0Npa7A6L^an=&lJOc`UOtjJ>~hHDa!S8~_9-b|)uYSU_7 z4-u4**7P(J{aL`Q0H)EKQ$X{2cq?;i-MyuCRTpPOBwHEt+Cg3$!_8KTZiTH-T%j<$ zS1FnUvSs8FhLMgGcUgNGp*tEa>?-$Nt2JFU`pkJ@26d^fcQqP_$rgyf{TQTKKh22= zo|goHW_=RNIwBOOApzoss&(~`2*9%mmX(B%Byi3cj+tl-SUjq1$>WORoa>0ADpHq` z%g9zl%gEh;$?DxiLW{gFK!KzOE1t0=&NfF&=lij|v?#lwP}3VCZ63Q4Ix#uU-}} zCKV83MOsj~0L^hANQ_E;Fkj- zj^+YEh30slKqmp0q`Nr3Ut#@%$R~s46g9$(O4Xv7aI5w^wlLhtg|@a5{9xp0Sx9Sz z_LP-#Ca{aI(NnIzNKnm$A_R8-{C%X zj=mJo$E>YloWhi`BbY{))hFjbQ#afmm$D(l4lnz^v)J2L!pA%i z3Hbd2Y{BR->d~&{LAo=h!6t0l&BSio1s_zX>9}SrM~6^yEH0Jffvix2#ZmaU2cI;V z%A@9}@~pJ5ukxgNM_*-BBo;}60KuCC7oHEI5?lxnyh(83`5-F6g#f{u1Q(tUq7qyP z5WGon;rSpc!G!?9n*ziEES>w#KL92e+qF>D& z%-?zeL)`qALf<2O9=>csZHJw5%q#tGNG^ICkZ+S0)GpFi2q z_`>dvzdm*yeBQn1>Yj~V>!aI_U*DeHvhRm#Z||yy!QNxvc1^wUgm2Yz9Y zuyp6S`QEcLKKbL3+xF4BdnRt&^772cM<3_IU+x^#t{r}(^Rp-Oo42}lz4hw_FQ$Yq z4u9?3?14?QlzGgYgWKnRF)TJD2VL(suVbvvJI*ec`LKR^%G&w=c<6z|!L_^2O?~;p zw#ofl-#+@`kTT)G#^!IFi5}>*>W!sues9^W{nLlG4jno2=K19pZV?ZCzJPk^%$*-J Uy|L|6`*q&d=C;`Li`M<*-}3_bMF0Q* diff --git a/romfs/res/drawable/ic_overlay_nand.png b/romfs/res/drawable/ic_overlay_nand.png deleted file mode 100644 index 25bb1f2875ca83d408a46ec5708c0a0d04c887a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15404 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 NQaIPKEr0Qfe*q;}@+ANO diff --git a/romfs/res/drawable/ic_overlay_update.png b/romfs/res/drawable/ic_overlay_update.png deleted file mode 100644 index 9da8c2654f9e9aa7b2896ed767cacf929d78f55d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15216 zcmeI3Yitx%6vqb<(HbxYQxI&jF1{L=oyYF%?u@$&r3HexkE8ciTyKXO@{M z-8K=3Vj_)?1c)KVm?%-Bpax70#wLUiG)4@3ATdEuL=xfyc^L!oL%lQmn%-`2nt<`k z-DDr<{_i>G&hOrHcD~Hc_O`Y2>K4~g6g984CD{SLA9CLJ-UB~h-Pte?e%+UE>9Hv4 z{sqo^7WL*^izrIGu61?W-KlkwV&np{YNSD+kjul_6ct-h$jizAuzhLJr|EJ3l`oI@ zeVQ8gZxm8&D&GwHwU!YRbdI!jDI)`lsQOnV>S6^62FL+h_7!qj-I5A%zZ+MAznx*m z?{lZv195+olhD_lYWFo8Ch!S?Fs*RGs4pT0I6;i^q2)fF<-!arGF*seIf)e|mh+Wf z{zM)86*JY0)RA0MN(ZgDzu&g=62lA)4F!gR0mJNLI8hWCmS=dLhBIhuShwW@ty@cr zLCSHGz*0;tZ)=9`bK=TrW6+NK{Z69Ft8`ttd?k@?mDoXwOhL{wT!3Y2IjKs;CqHOr z-KD7t1F|3oblZY{Tur}xzhN6zzcEe9iac#%xF@MpC3nsB=5jStTlVTL5J3rPO;4+9 zI1iW(U>Sp^0#rUpBoob}9cu0@5(co1SaE)#@ZNf79es0wsc z;lnggWF-^{N5g{1ms8XTGbL4vX2J71>^aBqG`qd66KtM5S{AaJ+oSTbX#uAKH*M;6&I~~@zQ=v~97Fv;$CXzxlsjws zAeND_hh!5p^}!P|v)V7lo$QDvh%wIpL00<@w!)IP+}=jatgdczBfLH8|J;b`Nmlx0 zy$`4frhJIY12fyZ;*L(Pu5!grt;U%17bU?;hNnt)D~O9RQ^u;9i}Smkip&nW^CLV9 zUkvAbmR!9u>e_>M^~$Jg)pLWc*$LQ(+=U{;9xvyAwAkW1;VmCXgasie#29oK^=Q`$ zAlsePU@x|up2EZHa(z&tren`^jvk@pSllYl&6!(;#Zh?c2k$zW%5CSA0N~OQq-*p`28zI4YCyVTQ5aP zdnsz6v1`-uHE{0D)?`yx;mnECCzmDHFZ$}*NpRx3?4CGK{M&Ynz4!69wmrK#E^gbq ze9vQ_hYvmY{*I%E!&g=vUXl4T)poA!+>O8ES8l|NC%-wgZuh{dWBWIlZ}-l=vH8}+ z^M9x0S>MhY-~Yp-<3G>YU6`;QSUV^4%QJ1))}LCr_@DD@M@8%K=-5@g{_@RZ)X#q7`1efJJ(N2RDr!6A#?Ia^-HOe(-8^^kQf1 qyl`o}VL^M(^@-u$C3CmVr%rwJ^k>gs=pTm+sMghO$xl}GzVI(z8J{x% diff --git a/romfs/res/drawable/ic_properties.png b/romfs/res/drawable/ic_properties.png deleted file mode 100644 index 4c89dc653bbd5eac98dc43e890e2401665b4b4f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15777 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 diff --git a/romfs/res/drawable/music/ic_music_bg_bottom.png b/romfs/res/drawable/music/ic_music_bg_bottom.png deleted file mode 100644 index ca2d3dcb9c1b0200b1bb3d4b3e62a566573571e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34938 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 diff --git a/romfs/res/drawable/music/ic_music_bg_top.png b/romfs/res/drawable/music/ic_music_bg_top.png deleted file mode 100644 index bccc2999529c763c16c1fc9349b064f87100ddab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61783 zcmb@tWmg&5&pl-EWk$jUxBj{RTPDS0>uMg zRa5@Elbgw?DMCR-phH2${ul56k%C2_p!|KHpiW|-pac$}pa>@21j4NSJ zSUb5pxmi0olgo;WlRLXQS=!iJKtaiXbJM&uG?sBAX1cw63#WdT0?kZ*E@q zYMd^b9@}5fyJz(9ppwg3($nGPp%cfkuR{fLPB4>H-Or(dph=B`zC$T!Np%=C_#tA9 zO-zO7QMkew!9id9>U6<4|D9zIQbPF5FVQ^#1|MfYVP-e+5-?)oMe@+dCo;FpL}w7D zGg{))SoYA1VopTynk1nLy)rfyXRKBVv_uZ`mN%)dnqGc`v?x$W4@K5TBD1&WFk)_F zfuy2;rZVhu@2+T|vBN^)$lBvM>qJp(3{&0LplK$k(al%WzdTSu zB^6F2%e@`wiUKTAFrpoCAQvsIG|cotHY~h5^mpZm-opAGD{7(3gqv9aFGRs4I*7TB zh=GYlakSj3gk6`o<6nNn6B)vX2PwR0jyGEVAhu*!R9WnVJVtXi`0#6+jR0TzV}8~X z`rt>&a64|){1c{%S!~dP#}AQ!3k3SFnB;S>!+_6Xr=F*ueO__DTlJUj z(u8JT^G?&yhfQPMu;V=r&0rNkL(9PYNJJPHAvuN;H-@hXVseIa>?3*##lS$)g2k1A zs|-SK6d_%K`zp%1g7gq%tAao%%5MDyEK0}oWja)Thp-LWG4$0KMSg_I$)7IlMV~yJ(LJ1`g7&7!n3LmYC11Wd;6^(5gb;3W66>KBU};I+4&> zfHg|iuYicHenf?k$_A{OFGPbV|NR#%zN6_3(*-{rj=V$SOtS$+7S6wOd(HX8CGb@^ z^avve1Cy{Q8lMtn=nF);DA7ACp+-77@oNdRjbulX&^|3&^dz~HDn8B^Noj&2d)2%O z6&&Pd_-0bgkSZw>DUSkQRc=+Y3f%>14uaaqCF%8o?pe%ZICtC*3_!4>SZgs$kyA0< zY&|PJ3Tkm2Xu#W)l2taNIRk4_NP*~(*b;+1K6B6%bfe>6L$X9RG4!<|eC5Zv>bdSY zh9}G`y!24z&ZP5Y8$B_4dO~#{Nq=rX>OWc+Wbf}?v|I33308Y<*QHNVK<*E|0GM8p zL|SX?b3}?T>H%6Yn!;4)r0byTWEtuz%-X1631$~6P_lQdcP5V%P(GRBB*{Q#j+(*- z;RNG^_=Nd{CR1UFl0Uh46y^ramHip!8S^;~PI5qIKt6;CBw8d@ByK9R+fHq$ramw0 z+H78D9=?w>c9zVp=KY=NJKp!Q1>Z`-$`my!HMe;j8+Dt&6O9wB6RLUbl5+C~Ha#{+ zHv3GN%uqHw^O5g}lDWHLCaPTOg#Qt-HBdCOPbC$LaMgQ@(NwG~EX>o#evMg9ERE)+ zk(5$!FlFH}g=@+2D-o4->lghQvO%&Md!VV6|CKg5!IbLudvEmHSo7Fsnjf8Vx>t&F z+BVJh_fHk*(t>h~ip0_eqklyMlWM1G>!mfNlJW^G-Q|c3r{Cef*J%n=tkmW@%`ER#R6fga(tW3^DXFQfS!mmR z8gv?x)uLCS*Q1xcChif!CoX8%ZO|?4uO%)ys7$3?EV-9QYnW1)S<$ZDr0ixWug_p$ zSFKgP^t&aeCEc^mGv)!7q%l5pnlKkWSHA-v@XN9BQfQONw;?awu*fiO%fnx1TP?r3 zS2=JN%E8#7kKOtwJ}Wf=AAu!d3w}JoTr4o|CXO#IkH`*Rn&67;X0`8|AetPZP()1Ehjk0cUGw2WI3UTZ6)Y2^1>Ob52gjn(gDh;%Zrs}ausu)r!S^&nYozH z45r&D>qzS)>tG$(919&69TPSdH?TMKH>?C11(tksZYBI)yf1HvA!;|KKAe72zWP4( zx0m-6&v36AuL2K?w+FADul)gA0iExpHM0i&`g14fZ{2TRufiy^Xgb)}X5_R#$s1^| zWfN^w3{{MN((uqe$y}dd93{>tz83No4$S8L9BVjkIBa-!UcPR;o8x!B{eMV{^7Tny7;wPT`NC6Db{2pCsGpp3rrg2KXYfr z_48=AYu9$qQkS`=yT;01+LPBW%MAQ|Phn(zIDJ@|W`*WlDMQI!rbZ^`Ux#ybJ4>=a z%4zap%6kerO`sf-vW@(3=}1}L{@8W0Pq)pu&8ki4$;(OjNwJ;%(z!1C#N$713lV~9 zwhlOrxfzXK73nrfo-~_$-IQlq2F5dq7I!X|PGM;lE5B*;I={)*h?SmtP^M*hGR{q+1dzzq?M#fhli|Z`#>|++KL}-|QLjw|7CMr7A^< zMm&v+o_3pD3tX2i!54??)h&UHrg#7*uJ3j90Y(Eq+F#;MxKp=*YnZ)e1q+StpGuQT zThsplXq{;3vgrqDaaRxPo2p7IEI$I9=`3ebqbNmD|jM#GkmUP&2-GRTVtKF zvvIPbvxSIvNeBgr-P>AMtJ?vtLhp7EZNo~U_uLaTor);B+j=Mo~6D)Xv% znfyBLQ2r_OXDVB{%}nRY3v;^({d@CPR+xE5#>8{HhkrmbxNG-*PJS&#_E++^Vp#_W zc?=(pk5$K3=PonWd)(>YwSJmI)L(-RM8F=^X8`URCwFvaG6DAepY9)JXH)X6U5rKDN{YQTl-rpp(P?rebcEXMPXF{orqu)yWL5<=4Q}kLHK=>07b@ zx3}6rE>q9lFQ3EZ+wz9vy#jZ(tJQh!LG9A@>P9S+c-={XZcj{m4%?mAUMa}BSD-5f z;p2egkKK;CjN7-4q2s^*8BQc;8C^FhC|sKV1+=Uh^)(dKVWg~tsD`)U z`MZ97fPC6gRIUH$xLidDj9jF6yL}%Pxy#RRYoyM#MvGP^qqdW@3-rE6Yk44CU1GuJ z_phBcYmToYJ)&ZxUIsd@CL+Z41#M7-H!7MMOW)Za`)96vMk4W9x4ka9*(=5CHg0{i zC-IM1DJ>-?9|3mn%b&TzpGTi>U>0u2jW(CP5wA$E>fZlq91(X9)dqSZ(f{5dfPVo& z{}+zC`uYgF|3B0bsNF_J{{L8=1Mg7n|ChA=?Gj=1f9S?jowKmW|EC%L3hny;BpLpH zll1xs{T%x|H7i_6Pfrm3kXiP6yT9c3=>gthve|DXeVDCx-ClO?np5*~JRKLK8*M%7 za20L)Uv;N@m5e-^HGAR2So)30p6Ia5^D3DqMrFltjyex)-xn68Jr#x0;yp~8Nt7y^b1PO_zJTSAivMZC&zA zLza0J2r*gWBa1%m$!yP*O$nU|9%K_rJ^Ol^G9U5pKLWoW;uqSG*6;Nm;kOj)S)WvL zx?Kdp-%2_zAN(ktUs2N?!OJK2pijFpIv0MqhMm~*c@)eM+-@_nlV_}4GFY3`1-X!# z@&TsQA&_9aT25l&5s9~j6(&mp?8kikC?_@!7D%NmNOIlKXt=GMD$Rf2?pu7V%f{5L zq;dWCGq%T3Q#^7yN8XiR+P0CKr086r{j93KmNQ`Y;~Jskx?#7Iy#Bdi9p7T7@CVGg0QA}V1Fta%9-lKIHe!0H92>K+;Zr*!=nPA% z1&O}?aIdvp%GA*-Hz(erx=NyIUMssKfw2D?y2^?Qm%Ow-?7AiR`Z1mVg8qq@iH(mC zyss9XrtYLeBfyj7clygN3(^lY%sq5N?d@PeWHW=acl~3xYh@ z)J;u&Q0oP8>3B7tI%P4H{;L=#7UYO`1yX_R+rZ ztv(n$!cJ%F@g03hCDPZ`AjR%5cXTL>F~boUDhj^h-ZKGwV?&KHQ%Shrn5j`Dj0i6N zXY8kH28ulxAD^W>*U8oCMS%`}zo*wRJd|3@me=0eDxA61r z9sRunseX1NwnbQpn9kUh%m>a!VG-#q**!$TaY}5@u$jw=F_8TcobUWdt>(W(*j*Ul zIk(O{C!q#4thy!gv9~7q)K2{L*pu4&+J zdz4&U^BREpM|E`eT)IGtS4*1cn>nK1HA8CMZJMuhmx<0#S9UlMfUltt>Gko4lpGgk z`S;K)&bi0~c|+}jI(3R&+5Q1Xo zV%mZIYTjEO^S`742&#^N^rNyJ!dh+j9P`1#(UArPATDoACNTsIzue-Yw75eIF9Zh{ z%WcLULB%$_s9MN4OC=Aa?fJ-X8Q#W$Iq zP=AL;KMtaCL(%`u-0WNcWzO)czHQ;{Yl}sI{a@Ut5-&s7jP-|K;EeY*boz{s1tec# zej$l%r%)cmJ*}M}bz5d_uh?Zb+x9FmbW5S{olL%-7 zKJ=W=6+Fc-s$5_BnX$AgT{M{2G1oh^zD}#Mn~^wK%8cfuW8lh;y_~d)D zKu@UOiwuy?XCscmndHoSTEK@|{(xKV8@bg(&GYWdEAtcBm#FGYhjA_VIj8XTYWyu} zg6(8HBlwRXcaOBfNc40nekH5@J&y@R4^0et!jVJ(afL2Pd^SYsS;UYZ?A-9i}pU`ezPV&hH)k@6vE+XA_o`P`m3 z$-zc+nj1}Kam+hqh)oY9zhxJWIu3T2QGK0FSb^Z=5b+<1aGE);2PId)%AVT-^|mvf z&AP8-ch=gB9Cgh|_cEwvNL+LRU8+2k1CnLSpORO!NWJOeZ`DP1pa04qL?mqG^d=L5 zs=4!>+Do{ISXrJR$z9v4gm3^>572f`>()dV2HW;RvH-`%96yR(D1K2&JI?H>e}e8` z8rYunNoW^H^#)0QU#T_NYiq$I@XqlY0{6tzye$vjwHKW zT~LNY9^*`1)|PF(2pHgpEaufww0GJ2ho(d|rdQSv0fG6=y^oOsd!SZDRPeNaWIXeW zCUcK3#bJQA$!R!q1UnagS$6i2P*`=xu@A!Tv-rZTy)sOA2|So{Q2WF?NW1aPp7PQ6 zNCRNVFKP;Iz0$u_>}z`=+)lVJh#{#^s(1sWjMaU&CYv=UUBJ$i?gpsObVr#ibsfTN z`7-MYvld!Lo+EYYVG&$+XrauIJp7;}JZ*s07%mGIBwNKETF#p1{9aPokHPdvgBq6B zyqj)KVJB>M+&=a%xRAZ|WhsaSETUnY6Y+NyJu3lcGF}2niRyqS4o$NLOYZt;&=wO*2 zEgPDh+GdIL;lM1iKM$=P&|Y8ZjJE1=5WU9&4YTLA-!0NDBWA!Dz9}BXlT(bs#U3|Y z(yyq=J2^!lmYdbVqGKs3Da%ZmQK*p}J<`vSIMg!5|F3nPGGXIaSVru8$xPlRd4hJO zY)v)my(Gm(6Hr83wq;PY;B^OD&`#Z8CEcO zy@L{q!EKIZK?PcXov(8lC$}@wKR8$`;*Y75s-2)XRk`>M{_$3IT3qS57nQ<_Qn{+LRJRnQe-#jankmx%IK;Em4D*Vb3%*TMwJvXd5A{J^(>>8TszNFY#ogvQ~(%_=s@g~bhgk}R%%?q z!!pw~fJ<2aR={GYa3xCi@489;w5iHfzGhcF4~7JOb4oGVR$P(i zoso0Ima!EZmq;>HhVqJnv@=qF19YiZ#+8POCtZdRcD6^UTMf{)zn7p1^7mNLpU{j( z!G>^gp%gI$f@^@F@8=u@Q`ig=ZA0HGN5BIWI`r!^i32Ukpr027X|lue^nhi#uVg^r z6^2`A6un>nnfVAtw~8oYjd!G|8{4BX#y z%^sxZj{+gy=JuH^N0(VH3wY>UXo_bvGPNtzKiy*MZZ)i6^chTQkM&ZPRe$sIncu;1 zjaF-&V@ajQ@5SqB=x+`1dD_IjD`BDejKIjE4wZllj-UzUce7XobN3i%1I~i`4R|oP zs&o;IOiPFT6NbCcPaKb%E$v7i#MSYN>R*%e6iV=jjab$2JgK-J4Sk3QIju-ryjJgmV$@;*g*O6LsgFZwHY6*b)SY5#U9kiNilFzdOi8GIyg^AvFXng;--tX~*0ejLaGtR*1@RrwE#Vq8c-6#yBMM9|9JYNPY^Pn^ z6PE$bC|-K)_|*|slvB?wcWdAvR3Cp$_Sq$+gk(dJc|L)wu7ur}&s>tFJJ5ySj7fw5 zDHje{uS3Zin=fg|&3fDDR?IDIn}*L6ET)^wq<%V7NuqD(@*9xpOspH^Z_gz-{x844 z(>zr|8mq9AnVA=`7e127dItJ9YF8e6;&bB0 zt#qZ1G=j?d$X?;NH8z#glU#xP;R*!?D|!dmvQ)!HC+;&!hH;p%W<_{|6(p-xfRJ=am!otJe5rywGui++;xbk45RK8a&t0( zZ2FDKm};8k`7Y#6q=Wn~nr3KBoXO|D3-Q4~dODscliDkP8Y-;!qWC3gteFq1e0&1+ zF(95 z@ZnMnC)`(a1dz0g)CAq1Dg&c1pQP4!*e0Nlo}12Q(v>5F<$@og#f~rTi;w6@@)hSk|t{yHm%*Y?e>WZ`>g}isN-1B zRw}zyUNQbd#)PPL#wf)Jf&K%BFkXdt&)rxqh$rox3f$+iFo<1_>9UG;W!k(ndY+A^WeeBvTt08SFUX2$COPM$nn z5S5j=OMo_x-i2;}X;yB0kAL!cj9j}ua~jiM>2uK(du@GJPD>-csyW&=1Fp_e}!avT@N6i-G8k7C&GDXMC zYG2)TgQNYdWf+{H*SRoE#{f?4ahnA~(RaB;QZpO9Tb6BmpTCExk$Cu^VzIpp{Tbp= zINfx;p}NtFpB4O_y#&|$kNXN;XYWA+BZ9>|HHNrHH0i?E6Xov|?S{!7H~LmD4BH}p z(}HrU!xiYS{JLG4m|pL)8k9reaU|mzAEqn11H~Fws@bq8TAYHV5W>%<-c-2nR;J&^ z4AMx|O9IToCXEGZjHF`_HD4p|GA5>7XuO)3JXf`YnZ%MB#9o%8h99#JH|k@Z1VwV7 z=#pLT6%n_s{WEZyGLZIdGijrzst<7fJ`<~5k7&w2^lc@(c!wwePh1xKcV{I-x`c-l z|6-jiO%>rO3#iffnqpPodhcZ_DQ7+X^kF3Q4C%9!K^#kF2i$yw(-Fm z&)(qy5??_HMvTvXRiY@6xP3`d*_&?R+e4brZ#e zew|L^|B&_?y6JF*gBuh^aJ>{3$y?iT=no&vGBid^yb~R6uK#gOw4YO&rf+b|lzDKV zK-OW``aB>94cwkcu*?F{TRI`4m*KXG&!wyP#lh?#4lv$J$ZAEy}{sD#&-$bo)J^^x7`wp@*hGJkArAHV7BWDj|4<_8GBcVulz^PlpXfTSN7*)I1Ci5y6D{V9( zuQvtk5P8`nCL}}n?kOcjJF|*6U{zKAGuj+&6Ua6&?-Q=nwBwg<5GBL;AaXvqMlL<9 zo89~*bwn9ulXRS-9{Uz+k=8D;1!3Ecbln!ft%>|bXY)#`XTQP~mDY`p?sW6S+u{or z#hDllkB|D|dbkdUcSik>`|iz>{?UThhLmE^5|CPE8iarM#yVmcSX$JDW@>Igyu?)8 z5%$O7iNSSiwz~^V`zcKm*Vl8#PlW%M*J}1rWA&lKn32Y6^6a0q&X*Fjk@=&9 zv+uB!-ZZ|%VWI;Dy6-m>AZtCRt;Y@hyO!vwn);;M1n*~!Y-J33QztTC!@F~YEZcKV zXjIKSn3gIpX0h_L-(x#vbzb_maJHRryQ$)FB|VrNn-R^0aCj2tC@22h6I-~@5CVfD zFbUBHkg8(%Mv&mg0!90iC7(12JW$rDIml=^ab8WFrA>ixSY7&Eg;1~zHqV~ozO$%< zVtxKr?N4-MLJv^ozCLT5#*IBFmUdp9LSh@T_ygC$LQ;c!1CxM!uldVy&kifOIy-7A zuVGmv;K-n0ay6bvx#6nxD7*~X`rEKZ9Q`O6d6IU)nT$|a3nLdVtE|)^Kgix9%SoBd zbH--f!$2p7Siu~K3Hdoh>*4kZvK#&7t+J6Aoy*N`eQ!Gbqb2ZHG2WBP{qKQfm7z+f zTajuXqs5#T^3+5FJ4MyrFaw)+8yvDruT)b%Wuyk>9iI>|m>{M;+Sc6$$Qy}NA2H*(T5(@1K`+QdXX@K)QX=egEN(y)Oc0Kf0nGXxtx@)$s4ESq| z355Rhgr~V|UnlI6j9YEFIHVloOESOk>+G>sgD|a!K&TLq#tfOByBft+nKMOz=~|k% zY|aS(GDfD~suNi!{-Lo3ET^BrllO-l!j7a~Jz=BB!jSktJ#evD|8la@+R7v(6Mf_C zi&^x_!daF>qw4{QdVerTc5uZFOeeEjESZkDD0k??r4Mwk(=|UN=TlPoZ}xMnGg@{@ z^w{HX-F@k*Q><)&wNiEp9lyxC;O0H@$6hOcI_g}^fp6c}pC^oY#Y~>$LXTHn+`hn7 z#ZCF9Oe&_s9?nY=InB$!`X~W~!8&G8_p)Z+F@N}olxb*VRL_C=tnv&YPaQn^w9eHy zjh#X58D7k_tc#9mH9YDI7_D87?H3PwJrl^Cc;)9vd;du-2bjAWjo2bSA1y= z6PRID3vuszmKwTfM+c*AP$#l{tXZ%ho{9@PqW{=mf&{N6Q7lkspo(%95b~P!dcpNJ9>*O!XsWB1|ejiaV~? zJvdXTAI74iAw z`G$9Xj`d^bKPY_c+ZIw&1i4jf_Lfs~T~gn6@yBCDPnSh2i)hTv*W!x>6f?OUcZS!U z_Qkj5G1qt0-WgyO+gB40LR95-belX8scXc8c)?{kAa^uS6gp;a9jjYrb7EgCo$t2z z1U3sujC-^+Q&#EJU6CM@(zvm&<3xFGjDmmFY6?@IYi)Gl?-lX8zVHIG-}%kSAT;%S zb}=c!&@vS<@0_*`8~Dw?ne9b_P@OS4BGy@xp0DD%=y~u z0|_Ely=`%7+#0h}MpA3~M02|5W_{Rhl<>)RM-;+(FtQtpUddg)ykc@=>lcO4ictnm zn!2!tL|;s`pmNKciu0U~oAw$O1cb~}jVOWCU=_uNMMe}Io?$vr6kM<-AUhUm31L%o z=)y$Cp^xZ?4m!1G!KrRz7jePy@NCQA{Y!hs%Y9#F<)^$oSgHcZ(HiC~ApBgw zEIi8F9o8nF@n1c59zFXIpu4hS9eSKG5k}z72C!24j0g8V=cYD?7zT0_Z>;`cTX4_I zBX=nu*YE2OV$M-l-1}39X8lH@?LVSoC%G4t#wALz@OD8P=jP9!uG3|^Ib=v-0lO0L zCP1}E`3)k@!ZSjtt>F8)PIDS52ECxlGNLJdskw>nPN+Y*e+JwgksaJn6K=uQC8GR! zVBzOF1_pt?!v?yH0s9Ki?qLvTwr<;1Va!|9@zz{;ZUgt`1iES(QYJw4bO&0`ZR zlU63+%0jaIU;FWB(>r39?W>BQ*NX&MF~g>6970;Bk4Z;+&FQv{-g6q%dcS(B{G9vD zalNBi61s1oUqe$l-S)fk4=XD50v$79k*hHp4P6Uo%_DCsm^*4QLh}VgroqCJJ-(e5 z{W;sr6x5j+}A>7L3>Nv(G4&ABke1%47pvhec6U1S?<}FSh{*~8oZ;vp7B?C-C_YN_#l}q&Y*55NXNL0DU||m!(L2)6Z+ZB3{B8NWqA3**V0y1PAw|yfss~sh zIM5Ek4m?%vc{`u^*q9K0&CDO88`k`CukyH6zSlJ-;nPR$4!I_<=6{NaV`l#1dIF19 z&5X;3;Ecw}@=+&#JVxal(=Mx^hFh6+R+%N&`(cv5X@-s(sa)*3yx-sP1I%ikOfV@{}8z z=d-+&wxyUhbk5Az!EVCfLca7CVMWtNkugrS^<UKzky)T)5Vt*`M<R<^nxyV6cB{s4|rGI2z=(^Gw~0jG-Q8L7%H@vI6esY?@F0vFScpe&v<$7 z%>EsLVa9AOJzD*k%j7~&84ON(%YsvPT{8S2RMm;*Iayt*O( zMgl7%O}ann^!CHD+xPq-Q7EvJ=~glTh^4qJcLuuAybrFcmpqQl=C<+(L<~~mC6RA* zK>jCVVDBf8F{_JSo1xiy#R>@%O8H$)#~q)SESEeMNfB2_714swZ>@y88lsD}2g@QH zE#dfjNqIblmj7iO{m%&40F9zf2q4Glc&FH6ywBqC^*7{mok``XTEdUCbo5U4 zmQ$N7J4QaO1X3aNPZ^oBDY|xxI;vZnH8!*>0P#qmW3&k7}^zQMQj zStTmam1{aOU{isu3qJv_bbY zaq+U|SVJZ2jx^k1JJ`a>-^NEu zKbG2!XCY!`<9tXL58?3DAEA&6{39Lsl`R+iR6-n(J=tTD2tTU&BN(#fpJ#rvBsz%{@OEHG=a4rn&hAbk6@XdF%$jmg`1ti z>YhCl2SuiZEhu;x_sMWWC6=LHYs>(0;zX?OcaV)gT-{BFF&hz||j zhSqzY(K2k9Io`KJgDOWfvrckG;ouj0Dg2S$wn7pF8rH>oy*uk`u=c~ppY!g_j6U{| z{AIG6r$0uA;Rplq>Xos1#VbkrK6c&Sn{y`zPJ&Hz-O}xW{97TCQLryl1F@TkQ{A3C zfC}^F@{kB4fI?!K;rzOk=}V8-#d7K&#le7$(C9*W0&QRnWJI8hP2KPy2!a`Y4!rW&+_OlG1e~CK!!COGr)UJ`9kp1P^WwQ>I z*aylREo%A7*L^q*$u=vUPN8KVD^9?qN+}C#|JAc$sB!eV({}$3%a=_%wr6hkV4a@m z^?0%4;Xdwrk6H0JoHXqSS06&WZA)jk1di%Q5F?v}h5xRXBj>6`z|(lSgewm`Ld1nR zi|SUAjzx(O+NLizHGcvF_W*?0$aABURGZ4iH)n4hqpH<|Mn!&DC4js1SAL*+Ot_<; zJ!n7KV?U>5D)@JJzR%8XoUMEzY+GJd%rA`^vm2;-)^lAa+{1!!g>yC>Uq$xZNI|^O zE-f=F~(d7Es#p8_xQ70)>3#_aF2 zWR)iFn6+Qdru5Pglhff1ZI^A@mo(BC{CZe@T!ZYp`&*ytQ=fC}|@Th!@nG4fxALO~7E3)oaEgk-c)Uoh>? za5d~C0Um75P;s4mc)25Dk=n|*+*vxa)k@&6C(4rhjm%FGyVrJ?UqyRO+d z*lR8q_^^I|O}I@5&OYwilJlQh;%ocx{nTFrOxLI8;fo`4>kkrCM^@3lU6LU<)sm2o zHV%U^PkUNA(b*3>?TktHBZY_zdj@;|`DjdROo-!MCH-vdODAj?E5|#F90+apG38-~ z%>TFK@a1$NX2pq0F=+qz3MXvu3!f#=ne7KEF$yHDTItv3)(0-J&w9_E<6YJl!)?R8 zP8jR6q@?BDF5R&Vy)C{!0>+vNSHZwr&4)RD7M6TiVjxQY@R4Ch*)=(hH3=?YYp%V# z^p_#Kz~pK=+?A7}kNGY@fB6Eju^J-oy3>T_pvGnoEc+w99Hlr0V))9or*Z55mm$Am zZufUU0&;GasSoq@aFOX_vl(AHaC1$h1r+0iBz@b35#+_5iLV^#ZifLgjTTfFt?ZHn zjUKx{@5RTZ>{xl>RJ1oso$Fu8&HXof+Hm5p$ z8y_y@EJgmAYxZCyx`jm=u()bQe+T3ZSUl!65i-E%Y8PeZfO+gv6O@6MCt;h8|MwQA zeBLJ|B{8pBo%7yDE|WQ*+Y@Z34Irp@-!L;zRZbemz`QMNH*ll!^FP?i-)47oS32ZH z@a?xS&uL*r&aauHiI*}Z?2NzCbNLYnsM!mLSv3rdY)q?ZRbHyNin1WE3U3WO{igo4 zK5*p6e~u-KJLEgv^K%Q~_Njg|;X;W2E>@FWsPw+!^UcnH)<|1*yV81bO2sal-E*o- zMO7gO>nbNtkBoO~*gOwM=L^n`pLBc`&F7WOb9>#pu{{x$)cn!MOM~Xj`^n(n)YYD| zy^qWW=G$mm-`7Paf64}_Ux3bc%(f_!RwQ=^(6?x?=V@1hJ{^5WJ$y?J$2=Ip;YJ%N zjXfb3%y+*^1p23+)U&+O+-n$+GMGixW2CEIlM>JtWUVHCsL5%@y;SXUP_P=W{JjmS zuKR9Uk?Bw!Ss3!h1O-!ZP}9!HY7E-rpgWTz^H{2`}+rde7p2^^C#Sd}3OswW>US*@a0UF**dhE*9pk zWmtXMm!df=*Yv#an-fY3OI$yY274w?*8Ve~;5E>n{s>`YxUWLkVx_w9!=V)*M@N9# z6Ps}*M}PDiD6aC3?Z0QkjY^G6EX+O@R88MkN1cSdRdu5EF3;y3QR6yga}o@IEEdlY z7ObvW3kL2X6;`DSF}W05DHwu%S9`&cpQ8ub_wnUib|O34wP21h&89lBbuDY!REb~L>Yd^*|?@n)C-mc5PN~yTnkXN_qIvr4lO%nD~TY0V) zxRDkD6|n0LF)EMjPcqF;CoDpSE@e1ye4SKNCLFtD12m?r>xwv#;sWM`1IWW+D0aQpUy|x1iCdR9(EcTJD4)fUFUtr8I0+Gvsb~D0yRO z+$f_kS6OY_$SCzt{!7n0jl2e!M$a7Z+^Iki7^n}QDUR&QYi)n)uO*}XZ^Oi{33L6e zqpgv&^(;PylO%oXQ8e_PHmR=#?Uy2=Ohv5lHE{)a=2nx>C|5)yyYmRkXJJ3q$WG;2 z&+eP}?pmFFtOIshbA``hIyg2%=U}J#lCS-7^4lYi0Bc3CGFkv$)ykF_nqpo}1o?ed z@}ygYpc`H0WyNyd?_Hd9J^NN+|EYEu5(I#E-O2-O#Yo{VGV@QC{Ofqh6jgB9qfR&N zvuTaQpA@suI#@phT>0EIxud5Cv4nmo-eU%%@U=&r_Gd$~pD!Unk1Ii8H~)rxekBNl z+?+-YA2zZdd#=NWu2&e-1VDcICsJ#)X3BBXQx!XfCyR|*RUd2yx3tP~#+N9o_>KJu zff37rmSH(cCK&wPdu=?X5T|oCOW^#z-Y3+YFmA*5z^J|M^v{3!@92g-H;eT>Z^eOk zpZ7B&Oa6V^$P2*(>2KZJuiNiepW%7Vp+R?qva#3Ww2+|=OuCN9hRP^BmG0%hED?qE zm+OHNbA~Mmmh<`N10GfJm0}NPu5aN<_faQagbS#9iM5y$VKVed;FHfY0)Ni(!3-~w z755J@@J;Z}iii3SWhw-z<21K$OCf_V$XG=e?(`Tl=(!>OQ^{Tgc@_9|Q|+d+k4vkO zb^vn+<$s=dQ=tv$52^CkV%k@6GE=o~R&jeAdI^z)zN{<-QYXHqDQnb?I)8dx`;NaP zptf*D$F?+?4&+$*)XcGj81dlQ~c#%4|HF+k@Bfb@mZj z!)GG2(=mGbQRpi;!EAX%oa#enEag4QV`nr^ndgTy9(DWPzDlh`su{srEHMDF?#5(H zoxj$eJuQ#hU52Ezyj( z_DJ9GniK5|+K=mYXsbD%@4hkI`?3j;RO>>GyHoh|`C9(@c3@}px;F9pK0Bo4FTB%@ zCO#z3_eofvUy{tz1LG6iL=HL`!y2E0R;Tx6Ghw0qZ$M|Tea)?mV_HqHFMlX@yuZe( zpBFdqS2ab2#q^16ju+TNyLVLx58-LWzH7 zE5}=R9{`!;@uypwazW)psvaFi$eSfwI?#@ih~I;QP3f>Dx1v?Rfkh|h8LCW|;YBVN za@Ii73qC=Wd?#7|vx!QYDD4otz7`*88LRBN1S>R|?=XQ=Ux%Gh1$6jxJ`q2CpD8{J zaL7$P(EFIEU;gf=(orGL0bvr3Kl91yw z1a5C2GL{%+bw`itHl=QHtp+P&t?&0p#usn8%Wkqu&oRey8u~GgxhJS;!#Fb=Z?dMs zx=j2Gd&D=v4i)}}B|LXZtnThAsU-0bwhht`t0nyrope9mapA%im~>L4eb`U$mv5vz z{qr0In&p$$|Ij))-~@OsMCILfTy*sUU^z&keR}Uo$$-x==;%QMmIl`sS^=8Hm)c@H z(4m|Wy}yZZm(;CK%)$Rc{AqK_lYQAyeFJZ?qlFIvp0y z%Ger)3hG?Nq_MKoy8SWLt;W6zdWOE7tFs=Ybxnr__s&1UOcm+4w6R`ZE8o%)MUkxk zbFa+0QO%G{nXFUA2>{8)yJ}#YS}%E8?uCB0MeIFh(g1S7bfbzaZ}MW%g=Jzal?sWw zk`dwv~b;A4)n;;u43%O*PSb-WDW&93i!kp&GYe(V|+WO1;T2YdPak{jK)l8qkjas z_<+CTO`#7GR^RV}+?n<@`|K4>3um=!+e!4fIO|8*>Ke7>>(&x_T4HJ8^|7^x?{2E! z=LC-1?%oShhP1QJb`I{R0-_X!+ve%GS=fxXWj3a@_3CF<%*zz^)gb!H4grzBH;#1t zy=b^zgAV(XSbtyL4C2b}?u{t;k=io{>rbQT^KrSQr17LJv=;2@n_~MbyM0inzgLDj zPbV?t@<>J3`x;7SCZjBz%*=w)-vb+H9DvYqN)ei&)z zEV>Feq#cXPH(j%#UhlItPv;1uRuY;(9)?HC;K?CM592>Cx1WT`A-MKYvRLWRN$5kY zLAZK0LIqAq-*dTp$^&cT70FD%R7UjJ|CBNGw{gs^cf&~W+uY=Y7@3Noz*~ z+I}Q5=a;G5{k&OSS;MkI<9E`tcUwt;ikK8xZ^#R>lBz*hnfao`(L)0FqXk{QF*+S^ zS&xlxhK@=r*q`E3_N@-=EG{JHCKd zXDgwM&aB>8uE%uHBbj)~;6QsALGxq2+e=R)Zd6>F&~0RHU=y0(l+kqOG|$ZnH>Z1# zi0l7h;Vc;9>KUe8oZ?b^aWAfmyA=u)DDLi1+`T|?C@rqV-5nNpw*?k=S=^nE`+2{g zaB`B&WHPy27YEo?M{A3Qa_iwwKgQtXeM&L;m>wRe#fLPUO*|Y7FU&wkfV(A3W@J$A z*(X=q62$qTlKagI0e(^4VEN+na-Zr~o&Z~gayPK03Hz{?mDE=Q3?G1yK}fUJNwsI5 zBCFrgq>!<4F{tDWl>F>4Q{=Ez>ARx4ihM+PzGBQaXs59hbqmv#EqE^`vX;-;NV@K* zJYnbCH-q|sOFXCLgy1S{A(0a1HtE~7*X5z+S1FG2R7=F(BR#%-%%4lf%h(DY zjac45?{DL;`l!c^d0A&o3b=)%`J6pZbJ;!;E};3F80o;Pdx=Nh@bi-Zay6z-j4rg& zbaz2_meX-jUadwIeve~z-cJk zN0KyR^{!7uNjif})F;kkXsp3=vl$s#9gde;xq4F1vK?k^|dX`2cJyC*lFw%EF{ZRf-Jb{8?C$o1tiA zWf8#xa)Sz8BwK>^*WvzIZRH=VF#H@USfD)Y{ryOjhe+35^BzJYz3SWgtL3Zkl&3Re z$4IVjUs1+Ue&o6UwV)p+W&0=!k-(7f=tYEx@^G>d?jzdq!M^S`nOzUrCQu?oq@()f z!!{nR`MtgP{*>7vC{7>UdhbhII)fJL%{P8dOy8(R6_kdDj-&13rVA}CdiR(4B0$r^ zRE1O_1e9>>-7mktws=oaO_&VK*ZjbTX!J{KjJuepZ)tO(>`2yl&GkGIvfZy%9PF^- zE@-UB_9_`>b$+)XvHse|v@T=a*kJ?p)vsIsTShZF62d+Qeu{ug_vvaC0?kY4f9sn< z744MNk5O8RY9J6%4+$0wWA$p#YivZd6hI;pr!m|%GV3Gvqu*LfVb&d92oR#N#waS! z1ngWuCBf@?E|UdU(%_Z|9v7L)tOo`k=t>`_8wW?Ff1^vlk#B6h($H8vrlEG-g?PGm z8T@EVc$tXnQ61Ryisa=?EYOx!ciA=r+`o9CIi56{Z+3i#r_kP6Mg_4%a8i#oMLDab zN565e%n_HjtbX7zRBX10N=9O^gpL;B!mlO=sC@b~lpONIo|qKB)g+=QAh4i!2E5xl zzv+XO1Z&*~NaxGCZHNY$BP=i_067zB2bA4zw7I>iy{2U{`R0FTOH4RP?gjlM^m|+a zlsd~(Y43HrsGdCscA7Uh;5bEr>2BIk5{{y+xDjKbdXbmi;vsP!sVQbP(ujm{mC=bo zJx2Q>6lYlwB;l)mDi2>?n8J}S$I+N+Yx4!HO9LS1Zjb&sIpL2+%O{3j9Oe8bBHlrQ zaND~EJ(S4iE$PtV4h)=y^gx|PiO;(yTAx0fy?#@VhQQqxfCbn!$7wyHef!t8jXRvy zZ81v|mwQto**zh?mwd(ifYzzlcX>f^TlBE^#NJ`*3m@^+9vE!0d-_UTFLRq;|+ z14jgwZ%u@d|15v_H1Aj_w!SYdi8JC7jpgS%oyZ22R9yg`p%2&y+~h|~%XfQE(o6p!A&7r_L}bR>zLSEiA4VS!fNex4Cw^^?%`^bi`ZvW? zIT%IC3nzWyB)0LEPz6$p{0%2%#aci|5WPUjNgs!cD8ip>_z(4-1vv)O*3KYm-z&KxbgRfe)kPnA&iCElRJGM7@@r0B-ECx-|H9~s4n#gJyvzZD zD%bCZZgv%2a`??j;#vg|i69PZE>KQv=$dBax%6BG_}=8YoMfRO}=QvsN$sjC8Opkao8BVllId}e~JC;8Cc#mh+3 z4JeY4ec^9@PIqHre@Coo>?l@|c)xzBHC`6fKZahh>8hyHoa@ni_iDl3>vj>T%ARl3 zikG)0-^VN4woztwjCv#;?a!}LP#0B@oaAS$U9w|Zld}>Tc+^02j%HGtjgfKD97%=t z8k5ragzS~Ee)s^;Lun&(|L%Tq~fZa|V zn7!pwo#;F*S;Zw@I1#~|0q*a|@bx({z1t=~xGm<`=Ihq)LpW1hdkmZ+e)B6>rg7<2 zDv5e}WuD#xbhc+)%p0al?%!H}?SPyE>^ir0f5gTDi=U0v_Y5#{j&c5N$2^vog6z%r zo-75|>M7Z>bxb+WSC$5BLmczC{0~X<^HB6ryXH}Bh>~iZ9930(6!|Q-?mCp~^~ydO z)=qA%E1<^l5Kz!vFnSv3h=FXhdr_p0b>13r-fDJQHSIK{tZ6RM8}dA7)&CeGxI#$_ za)JtOwXk{#Ine-3ZaBSyogns`fmmMmgO6W*4GL|km+3$&e4d?ZCB7xXuRgO2DFV6D z!nN%#wtSkDuB%3peu^1ZkNQQr_*&OkZ9c6Vb0D18xcfaDWcU5|0>~ zso1L!VeRNF%)x4pEmvjVw+M`dF9w#?WCjFoGp1^H8wgJ_EF? zkFQN#Ld~Hr%SU69@dqH2Z+BByoZAs#e8YyXeedZk;X$zDgO34Bxj8kfX=%n1W>T2P zioOR1-coDa1XTVmHXHMYk&nGH`OTJ80i6go|3s;{3neFy#P+g_7_???Qf(@aCV6Wo z>)5LAoZAx+ykIKV8k=nq;nt`Yr7PGKQ3!w9a?&g1gPHr&>rkKpUB}N$NWXN!pp7#v_W|g?3DxXxU5IBkj1WCS-?h+g z{IN@k?HE_ki7Tme1XMp%4Rt99IdxN1EI|^;{>84CWnj|9F&&Ecy$VT8uZGEsAP@G^ zAtHz959_~=;u5~@HO;xwP57?nXKR73#rKau5t?(yopV(H-OL$-dV(JUlVUzbbn2E+-Z$^J~O#+O1>pY5j_i@G2bewcyl-^F}HMkhAE$6axp z`H-5eaCPw>}t7k_gtKh7YigqKlfKp5f& zUzjXV1ax+PsJPH$#zKwJS%P7VZ934kH(cAB93hml%LYb03K1b`5@*IIX` zW0?$7^=iMUB!ug(02ff2P#bpR(y4!)@U8AHJw#gLSo|!`EO@1+u=@ zR^)m`gjKT?w6Sx9-T_(>kH7T{4&9QqSfN!kedbwK(mW99a;}md z@7Dz#YKLQ3rgo0ghe;{}$0TMsFG3BOdtf~q33dsF!4{t6hgl&w+nRp&sl>H#Fw+c!sr-636u0$1uxlgQ-kNxSEVI;q@8^q@gq(9Xa>ol!P`Vt5qs zO9yWS`t}!f0C_ay44prglu_!7qJj=Tv2bN7n^zRMO#&QWbM^|JbH)SOs_bMix$tu$yXPZRX%o*v; zACFsbPT;zQFwsd7`+7Aj$Pd&E%PVln2Mvb~#;x<) z&XXT_yT@c=NF~G6%pb3G@8&J$fbc5I9rf>Jwzw_Pv$;OhMMK@LBUlkN` z|EF@Ro~2_y=lP0dY>`JV?t1JCbVT>>=4cA;0vc$HUh+o?)&(UJV5qk+RqjcoyrXI0 zb1Oi2p|Qx|u&nWUp*mU?zcu_%x9CTPxSd-q$ys)1R$1ylcC_1_HPWYloPP;JF_Iu= zlaED%O~=9`vD#i}Zmj%&`v%=xin{K|d}q}&$ELy&#fQ2c*eMvCt*$M&+nUbETWP6^Ni*trSI7QC4DfULNqTGQOuJ)@O;6A0*#cQo!^u5Hgqs~7ll~$Gd6D7_z zzT$=uay`9WmalFl#-DYP*=$=m|MJs2+kX~zVkz0xGk$1jb85TvV^(1QZ z-C6NCO3XB+C=*aq>(8Z|2IP3}E9mTFY{uW-z$ld078yFGk)n{>U{QMk?tJGCf+Nv$ z9?u8BNi*q%bUp(jj*!&WxoKPf$L>&z74__eC_9zEp}R*ur~dcY{pOOgU^NoetQ?`lAt0ZgLqhe4}77nFm1>kQjX5*vcn3N*SvbpP#D zCsC)Z5IQD~+W4`X=tS`>1UZLr&p(j6ENWeGx&9b?PhnhRco4uY%aKJ1+)fMskUBI+ z;)WZ|$ejHL3gR9Ck>0Aa82+X7=)$?2YZ~OIY4KpnzMiV=joqmfLzr~h?=N>t>dtRU zHCQp!RdAe@-}5OE$^P0a7`6as@mqZ=pBja||rs%TOFSGLNa2p_07 zSU`8l1oNgZQ$HwOx+Auar20U4zLt<$R?36d!1_?T7T9DzK5O_o8bI_CL*j^7ZjOJV z{1WrFJi2iG;i2d0>@38~E*21p-z{y~#_Ycqan&^()>F6vue%XLmAW1=%Cx@56hM-v zY|y6vTtgFwi4tJTAi`9=;nCZE^ugn#hip_RaF}b?lC^B^7m@%N@sdN?jiP6NR$>0T z+a&>Eymr>CWFtQ_3)2&zIdXKghWc(E#P>_X(yzZS?#Ju*kQq$X>oM&m2;gZ2$?Q4$ z^PX1mL}IBcs<%dZ*IFa_uVw z-$I1B94Qv}_j<}TM4bJ4fP>Kcl-Bf9Z}OwtWb^v@8Y-^=Wx^y-0UVn`QBy2^o!t4n zE9bc4No|x{m(=-e_zGk*d|Ar&dFUZnyR#_1If2`SnRy^(`q?=`NY5+FDsGz#64=`IlTKMkgqwXZjGTRL&FL{DcQF_|o9_m%e7hEGiTJv|zLiGBll7cJu3V z#)C-paTIAyjp^Fx0PXj$Q#BIFLNEi8)giYpT6Dc+Ag}_XU)(3StZ?ZVZa0OqaJ<52 zv{2HIS4#z)q!yCMglj9f^0}P z=jFJ|Ce%+a)&kQ9xzCF(Ytbw`GCxC3q}ShBP>vW4l~*4g9w#3?&btT6eRI%jfRwy} zJnK;_plb)n_a6{DuOdPw!zDgDBi@hcb`!N(q3ixor{Ab%r86MDOYQN_N)e*x2Vq7^>*LgGQ$9`x0kyHei2WvH`;)M#QAJ= zY{}#CJc&=2Vxi@>L_F=Nl!bbn>F!;gj=0!Ne;S!7AsT^^;g!1(qYy{k7-Yj}8VJ%o zXsTjpH@P?eL^7mM>ChUun`7#@o)qW@AayZP{TX01D=`J;8t5YO-Y`O8n#Rp&6*9}I z^r~qYfnT~}WTTal za66f~@lqUjf*G9B?1x-^QimQU1e|XUvUNTyf(jyPMM~ZoU*L`Y@eyzvg-F_!Lp;y8 zzv*lZ_H(6<-~GL-ZI82EspG3v3dS<`bP{HI87bRMEDNcdtNqYDd~dph>uUUyF6&SY zqfgZ4$!u-ps`s-llIhaaVKA%n!TFflTj1aOgp;uK5$YbE=1t)C_e6HrJyEtT>asmz znX~8Xx=q7NbD(bjS>ti>eyOBP8N?P4UasRgER1o}3uO6nuyCKOI81lOcVWt%2`8e+ zxdh;ec*W|>ueAeP8eB3izureQid^Jn-<3glZnx$W$+ho(Uj&5?o!`;F&sv-tnUVFL z$B~83Q>4K*0d~+iD=JBpZ??d?e;_7B4G(y^+>W@~4W}-IV$>mg>C+kZg~)mWt|~9i zrY&(YueHY&?bm56P^F`I%wnudPVhfa>1Vt2DM1Zoz1(>U*XZzc?DV7zGW%U9BaVE0 zl?z&B!GU6PJBVjj{pHl8Qec$j&T27tc7CRokzPFS+HcbnJs_sdZ1jPhknqx(i!M`a zJ|uSfvTHj`%RkN&fV$^AwUD)}!+|&GR1rR%($M&=U%-~iwjPZj9*?mlpppis&hUQM z5fb-Czw-+3ygeR#b3*YIl@}5PO^ZLa6-tC59Do=ay=draHCekQ227!A`~oor7g zhCD~kjukJt*xne({w)&7w@;+mOmds-&noU2t}+CihiKl_pU%Eh<1WGyNPA2yUMx*N zb`W{*Yem@E7a$#*p2GIoFwJoZGG1i?&n?2{_v-z29Z0T8!<7a0IO_6QOrh+BI!|cJ zE(kjRJlWR#QuI8+m>{&piv-ryFL840xBNA)F9>{d{mrvz#kaiXgu8f z^6RS)T9`^Xdwm~6ZNSELw_~kT3V=ml@OR0ZcrSkW zMjJKU7Q5)7NjxA&g$pK?T4GekFGF0{clutt^*Kwn!It)1`6cryt{S+$BbZ)fNu<{z zC$^UzP9Zow1(4DH6gA}=@J6~RFpuMM2mrlQ#=EwC4B0;sere#oVr4iHyHIF{)ve#_ z@-K39sY%ZY68@a+*7}vax~L1hTX)k{kB*Si7#BQRd)HfJpKYWhen)B=;*;=4@bQP< zEiJj64eKhMMKtb&WzTU0_JHl{y~aLutuq^MWP*2f|c~4dN8L7 zPxl!bzIBz-t)2|fReIA|0Uvv93Q)V(M=bvND*!EX-?NLQ?&O){kdG~sQ-)zF-%+i1 zO5i}mk>F*L$`&!Ba(e`a2vS+l)%QN_tJBKatvZBh zg}?fim7=5Zpp>)W?*QRDmOeKJUH5z!{XjHMi3`lyUH1-ormn=5_VO*n^ICDJ&swiF zq{PUB`)}maLpwzc)~U}<%;xrpaJFyku{^KlVn^^tZiwj*0EgtDf(^bIVHX+qq3rKr z0i>TOOrL4$8%@>yn}|fO8cu?&(u=FsYpt^+FIF+1m z5wSxru>dhqL65{#sK1|D-Vd3mS!eg12u`@kVhBITAX&Z4TbgRv=o3)pxJ;Z{g zJPDSo@6=_Kl;F>}A@CU`c5-E@C<9Z+mEu*2Sv-II$&%M;7nmrz z7Vbv{VdV&FL`OI#kc4Z+3C-(lg|6#&}h4K!84-tcBMa&R)$DjtGG|MXTIrx&TSFZ1T) zEgyt<HN*E1498ryPu+GeCPN z?U!_`PAyLU{~K|jJlM4w{N%QY)M4B;Z*n$zRBd26WxWTPQO!N$15X8*niNz`LJ+GH5qFSyQv}ocuak_z&?J4gmE)VLYxhZTv;*0TpwAxJ1o#=* z`8Ey(A~QuQj}p~w-IFWkC^P}+I?jdejOhv=_%W4xS$1+ZyNt4uF>8!;ECf`1JK!_y zIFngK-#qygHxt4Yf^koNKPiVWiJCqcuEupDh4M})v+vNgT#>#-rnWgn=yISb8`ZVS zB_C8QXeM;+o!3&25w)NzTQ|?bjAFZcd1Aa^!e?(D(JCGPpQ4ce*k@}3_jnMrHc50+ zuMO@}PhIbY8i4%Kb;7N-`b}($2vXB9AU4Z^@9f-;KCJ!?*DP1%z_qZw10cfIdadT< zmu#i7s1CN!v|g~F(%@(9Z;_I8kk}IYg>OFv_2r#>gA-o!ZziLy5!nXS<*>Cy(PlMjbg)iaLg5$udo(n@#>qetGpNILsk7_} z{S*|RP+O}9*v(|6rUV}C7n~rFj;(1~{L-6B3D#8N;pBgz68u#GJ|9RGz0?2t@hljV zyK1`rZJ~2~A;)|A{jE>}Zg6{xu62#YFd)R%$(|m7zdYBX{72P4HT$IHvMHed)y%cs z`dF+Gr=!v~=Md5`mrkIUjxe@vm2`}Sn~7v}2xKJ~{WHb+nBban+x{Qb)e(Q~gU--1 z-i*$JjG8TpQPEx}6>)@)V`uH)x|GjH4l)WQ`_vipQIx;rWRd7eXbJtFzq>GjT!3(N z`qik;#QYFMX~+e=#MVZ$AG>{arZJh&09Ds+rvC+tHZ4~tx*1m%OcQM(q{w9FZI@4W zzKdJPN%okRl?Jw^u=BX22)=A@x9!(X4W-K->jQ*{BF@;;lK3%{bfF$jX?aF@0f+R; z8-2AE+jc1`o9mam^OK0FdRk_=94iadfY2rW!L{$dNq!QnxZzgeb{bGs1MWabCuo0; zI;+51sBOpzt^f~Nh^K?GgAl;)*HpZBRy~fA}{ISlaWn(r@R9o`5 zS8FBnSoX$9DQj0b48MT0TS(zmGoX+OFLH~0B{6g-^4CuU{aB_=Au&6k#N)+t)dRzG zWe5BethFEFDa(fpYV*9Di+TSi``Vt`(CXdx(A!AM>2U1fz2UanHJJjOcucuzfgLdA zff4RvZN!{LG$&NjvuC}ts(7t_t$B7JLZWfyIG%j3!%uw6U=xQK+$NRE8J)KlN@%dl zAZjegD`XxmzWeq>u_Pvz@2xMHBw6Hmj78f3~>G?(PM&8f1yq@9ez{GV{3NszUER*K0OQg|lt*4mtD=oO-#+QLTD)GrAbF`SjUM_y3lwUA`|W zbcJfosM>*c0r89_K4fTlR5NeYuph;0;$U$o&9%#TmdO1Q{h)taGQ%Ok0Q*8iB6K;3 z9P>e;*~Ax%oCl5Xe6wL(=9wde;J8=lg$NpnL9~qhhAavTsdm=V6M!q)cdKYg*p6OB zs!KN=_uL@9`UiYs-p?yn%>44TDWC78F3F4elf7NKJsHU(#kQ5h_GhBFKvRiQ@|G!j zs<-W;)AsxSR^@nb!C2b9zJos=cRIrp1uCh~YpKOIodBMew7gyOYu0eZ;2ABWQpNND zz9CG|bJ8Rp^(p%ik|ESm?qF})9X{&le3&v{B~-s_fLI@pw?!zOcjq`pj5AXGmWpC} z<2in|k!iWdl)O_Kf?Og;h&`Bvs$CIG=((3;&#^~=K@-eb`E zJGXPlN^LRRgE5NMddI2lg&_#O$~-|h&YOxM82phAF}4}u z(Tx*=`&Lygep8k4Q-YvL4Bz<-?be~OgBe#jkI|g|0zE4cCwdHwIyGShF4j}nyH?B@ zO^okwgYK0zp-!<#RgrhYo(zRk{#=$WUMH}PSvRx0XU5lXD&NuleYi83UP-1lsn2XB zMVO|d@6l#N$B;z&hV(A)_V3T~&))r26Y9H3zl4gL%kcpB9yNE?P_G5L<_DXW>2t41 z^EL$}+$AcZNC)`p&q#)h)3ek~0SoZQzD|;7W#$|#_HGy@8s!Il7n`=9IU-lWsBVjb zN60R*FH>7emO61F{a8<1iI|i_^`+YpkD?U^wOC1AVEVtGhA9KTaS>SLqR)|;h>E8Q zr_oiqefA#T&C!o4fqlku|2k%!r3>YdgDMB$f2`g<1=~^s?&#h6G-^wS>!*c~?Ux^U z3@+(rUUap9Yn|jbPFCPizRU+(9^oan~6Wj}HRI2WYtLL$Q(y`)3E)p13s#;lu z{pC=uKgz~lH+rtD5Oz{Yf@<^VUE#!rvGvpMf=DWS!KoF$Kog$Ln7m>dJ#&8_VvRq= zA~5=`H>p=$%@(V?{Fb#zXcaD!Yf{u6u6P4X;!S6#xToL3$({VSvD6viR_}NX9+Mx< z!`@vir^h8@S{}+<2rV;(QHQynn!f;aKGStaJ7^i%g&w;cJJpt*MK76C#V#5C zelnL?C>UzRi3yj{Hwcgxl$ksfUKW#tX; zsIgs2;he@;tUV=%)j!wkyqv{M%&SZ_7y^8v&QC+m66NbMFZeEk;ZA-kb7!Pu`pcm& zqmW*WVL6vGqRiwy0e!bBP-Rgo$~(vA54XR78!Lhf>wUE-)p+lKal zW(akbl&_djjZg(wqs5G3S*p`g(7)yN0MTQe^-y$y%T#01*WK-H+xsRrGYz>)*v(v9!N#P*zeZ*LUKmBj^A0+}Nt+3hRQ^yD_4E1k>pR@ywku zauwbW`0e^rN2Dd?EkS{Wra1B!Ny?G;Ru2iUWqj*(YI;?lpaPh=WO)M_R|Bm`u^v>bNee0 zCaQ2RtI{V~8XF~GK%L|8W|oN^Oi6e69~byw&(@Z-*7W$1?;6hx#APz5cVK50-qXBa z@@*J@aet5DSjFoJ$pY>H$cW7TFyr41wTg_5Y$mA_#?$5Z}N(rNDW(cVcTfdcFm**rcLr zSGz&ciB%YyABCG>&^#xPQu;nwcC>ZPGdbMH+CMA1`tcr5J;kd?mwobcePd`FULQi? z;**ZH=pX^0e7oPt8tj7PCetzT;t1(28nMmD3?90j%rM8rGSSGTcmOkm);`kssBlwF zGUg=2`+UJEmkR9GvkBWWD<4LyLB)8Xl*^cs^OKFogYl2jG=nIg#!qtPsWR#xg1yGB~j6Xs*!XJsZYXD z$$0n8%qUj2Y2s^L<@FgT{cWY>Atpf<_XoVVOwUc35wXY0_}h78?U76!RABb01h`SjO9{cRICZO}l3;*WOR%qU%n>m4y}Xj*0omz1#X@f0DR3Gtkg1v zz>o1{U%Sg0+Ii_XVnn@Ei?3ZvVT@PQB)_;v6~e4@?gzMwqH@%!^PIQ71X*)gO}2IU z{R8h^D#bghDb(G}w7u`vB ze?xmue-L$P*2(xaT9z26BJX7HOfc*9nVy0IpK-Rm0+q$2(~N(H_#rdG7*48ECb~Q` z$&amm@GoTQ+Hz|ca==Om2D1q*QhXz!i;-0!^LHTje9aKFHx>PkIKq5)S-)_)MEjd=yh+^5H_0seoLkt#Ipo-md2@JreoR42*YcJc!Z7dpCEsh z3#&vioX*x<|KrMFguraQnT5G+T0-U8Q`fA~= z{0TsT8a8bphzm0_loY}jTPN^A<6{M3NBBwz-_*k#nJa}v0FLLw5Wj^N(>{|Yaji;_ z-m+Le*SYl%t9)-yiU#Kg{p-X`I%&h+AalANMee)RJJ8hCR!b{TT8j<7Vp81-?AP7F}_c0GC7(kRLZei0mT0C>p= zJXSsY8x)ZjKHxnGWbFxIs2O0N3MRF?I=f-dMRMIUB zOXJwGzc8u9jH`V*mQ+0=<={71z8a~&7bbQ5#kGume^B}Jh38SHcVUQJ6;}~uh(&!{ zH3hc>J#G}){%q1-TlOAuOFHU&vfw=ldBv@si`aPDAKvaND07k^i6C4`;suA8E6QCk z$pfc&lle|AiL;d4yBjb+DO8+w3_6OgiNd|#BXoODRMq)EU*^6E%bgKaD)Y`z+Cotm zzbW^@ydFGw$rJJCv)VN_Y^Dv!%oo=Qq_x5;@H+2Jx;9aS$C;nd!jQ zh^Y}P!WDFPN#tOc@5Oc3>6n&|V?sWH*-dUJjbRmG0JlwTv$+fg4pw4YDKC zvpuw^t8G#iMk^eb7)JkvZCITS@vfs9pro{)6jsU_S{7{$|ESAb0DM%tMKGVa2r&kl zvellz``#nP$vBq-Kl}FZxNR>WJX1HlLzK^f6@Hx|ZeyK$=D4x;?9~O#-fv?xdN&`^ zZKI@h3*vl3cIr(q&m$OW3R}l3rV(0iw@~5$oUQNw(AiJ{gsiV2$)|$HU=(0GOgHMl zCnl1BiJE7t&x4;Wk_~$mE67RCjjdxb;GA&Fzug`SD0iU!xV!NX43Ak8mSdk~d_-pA zOT4uq>CG>y9HvO#W#n_MW4+q=rQBiF?b%ux%CklY%F4gRZN=Cri~{nzzPgC(W&>&e z75MCr8ooMfuiZBcI6m2Tp6n_ZKC03^0kQ11Fh%Y+cJ~8b_gC)n z0^iaEG=W{ye`==@Kb!loZR;B0>^aT2%I#0g+>Iyt>oEs}v12`Lj9tb%1Nbo4toGnrF1gf^7LkRAxJBd zTAS~c&H#wrDs`-C7YerVVB9!&hE>r-f^slO5Z|$-3I+c~dY-%a{Nht&_0iUo7B%8{ z`0Xld%_>G`Z3C6rFAMoMl$0_&)EWM48|2Lp0Nijb*#4jN}6oYHjy*o!4`ZYAkeYM z@0;NJI|wN*g*BR1A!IB5v5120QuB6=(<6_-q657-n57yepsj>JrB0C-o_!#>TT5Gx zNvR8|4W=5>$e5{1lwT8rtTHti#~{JWZg#QZX1)lQ8&XE;J!f9Nl3ceh&}@5vG_*mO zKb3E44e$>6UySGVkGRQz-F(D{CDhTNO>Bt_C=1Wfw| zjh#y2pKkGG^%*fGRj;|tjVyXvSCV`30%5zk`fOFT=)wTheWBatOU{Pt69wh-qi%p9 z4lbYX5&%GnyeVe$v!a!qRIm@w6XrDFAw zJyM`kfzrp(r(e3?Z|tYV)&%ks14x<5ogin6_L`5p=^;$cl~|8sgGn@46||&i>W8I* zn&}Ai8s!pn?hhn|;o}tOzEjF1;fF8ZtX?QTUHa^z^45*Nu#~^o0ko&c{jPIwy9RGg z1cb%BGi{wLF^@B`B3aVr13(HH*HWX~=2?o#tacH)pbhS1Wx#PeXIi73X`Q)UCQyhw z8iGR8nsMj#jn`(PW`{!PGL!3iFNDmz<&R>FcsTOQM9T|{TCe*OaO79z2@peJvzy|_ zv$an{T{m*LKX1+&0ykz9T5wh2?DF+5`sh#3&kT1KaGB(E(k|n0dG@Iu!J-df_wd?n zA`eP&RKRK#={o24wdg|}Xo~D&ViQ>cw0uBxkHa30HRylY*h%l(gfW{r_(1^87B0p- zV5>H0%Lf7+-!t?n*6oSft43b>H}DrY0wzVYA)SeKuHg7Epdxy$l$FcX2>c;uB459a z)g-s+m2p2!Ox6^_lkMLc);2@py^(sZ)9Z50hGhrUeE#0d5)QdK zgb^H5Wu!d_h7iyZ{1wzUE0l zTVu_cwuav1JnByqz}4gm1o4UwR0KgTgfb&bsBF^ zDXpBG{>WUru2~)62##xZ=W)$mpZ)AWrvLZRQoH{CCd1AL@NCOLW7WnfVnB|hGQY*k zs*xet)zej?Z}H84vCHf^4%kEl6v_hx)Bkgi!rmV|sdZmE@&rz_v9aIXt%QG2Z_2@( z0DMUqET~k7j}b+K_u440qP2k=gfFlT`TaB-@?`VxDJQKK`+!h05zpctVm zt&5E|`?|e*WjtZ)|9Q^FO}NA7Pc@p&9?}d;C&Gc{9{smVVB9zyp}hg8pKHgbt_fHw zO*wHmHigoAomsj8RVb7i&$+ErLhMtam>cPZh9A+!o~%M@bRYgfL(qRJ&(7B(onOJW z$!3=#r>4Mgdj}t)a>^V@Epp%9CJFhC;-=GEclMOw9da}$tvG&v`r$x5Roi1!G|#mI zVi2+Zs;i@z%bF{0mba%Q7oDUii)6nXvG^n4R=CGu#;8w6jMtXI?1N=%QiACo#{=_i z#OpQs`yJjL;~wROA1Z$B-G>03$5kkJsjG0v9&X-V%#J+zVzbmeMaBX zaaLULu-udlga%(VL9_f-B@^W zb7Q#H#L@PN6>YqyZ#6s z)dXGsO?C`;T5f$?kiKZ_{-Myw85K;yD7VGt6esY51>wmyU4CZ!4zPWF?umFY(o zoG3=yRs%#IEU#v^{USCFaKAvPK)^?IpybsJ}=Q7gJ!aGeZx$^<|wl~W5 zGQ;M#fEka-<158nRfE`Zro~ zg%pJa@Ve`uT<+UCNVaKVGm)gyZMwku38(qixaw>sjm%%I5Ya2-#WnkxceZN^MZ_T?2TZ)Y zvXr0kS9?C7f&h_kppz%kwFAKE8m8?q!7B0~<(#vj(~WJfd!gslFZX95S8%Rw7aNEx z*eLs``%r@-s}h>n)tW)^JxV_w(r5F52^pUcoJXw>3>v)D)e51ncj^bFL~09$(2wjX&3NKie+dERg?SWq@Vp zM)-%ZUEtBo-D8vgHRxDTS68Omxwo9#O>9X&+0U+TH0;+Lb4?oYEabD**=0>;%X0CM?=hkjl|_4Okv>Rw013hWZb;=BK6!tX$m7 z{q9t9VibLIxc^7eS+GUbeqEf95@A5POS+`HOQl6RhR%VZJEgmnl$Mh2ZYk-Gp>ts9 z9`fb+zu(~8=bG7P-+TYoS`A6vLMfnoJQQkRV&iepqN=OktQVTd$XkYCvohX&a}ZOL zy-O*Rcaw?B@}O`Gblm5d%9P`>_ChFK0oawd;RK zO#iZc!&>TZ5eb9dJ4Z(zE8T?SlP9Ou^6t#*1Y(%N2c91^kfdGeWri_ijjtcpQ@WzU zMFHPcyCXQ8#HlKEFDDU;i}nkN7c%>1zuSg#yOt2B;tVqqNcb984DxWM`CH$ysM~#4 zJ{~5D)Up4Jqh`jc6uqG#(+Lc$hd#IW;`*+WnE6$(^7bGTVVFFMGGw+01=%4qFjGR?Vz zxa>4eJ^qC$UsSgDwAG;;yr;dHf7c*W2K!e3(>8aP!dN^Z(699tylLM7P*|w9AR|xq zVpeQGJ-v&j9T*a-AT)2^x72scc`q@ITU7o*k_mOBKKBwFLt4GS9QMZJcIxi!<51G{ zdse%0I)~(g%)aJkrT^IHmR zzV?4D%xV`AhUX~}>2Ir8>fA@cPuv8FVz~GJwLihoxnIb!gr7%V)yLNw+XWvIaxD(z zt_O@Se^lRSe%xYL>M(wY21!+F9XQ9jMw062n7!LA+x})U7!>zp$~T`MA0X8+rb-R$ z2hb(U81e09JR{aRu}|;cDLdTK@GMMJ5A;1L{^I(pib_+X2|I(N8b7RO{UsLBv`Fgw zgzUEl7h&KygS&3Xl6 zS9|ahDNo5Qo3;uY~7#W!m3wYb=l&Y&J@0Bf(Pj8NK@B1Bg0R_I+hzW@~00Xa4?g zluxvbutW!PGO2n(sq%pEYU`Mr>KMydqtK?fE1WX^g=cE>dw#z=eb0bZ=|AHf>}v*) zz~xoZm4K30Ac?n*D_OKn`ukaT;~=oOAGoFAUFs^2swyFSzM!V)g*NED19grJf^DZf z^Y#&?@U*uh`G?YcKgW+Q1eFD<$(q!_l zENf`i$#1r3^hkOGZoNm-ztet9ro#$&UK#(donx5Nr|D7|O1de;snF&4*kG0txe;vt zHt?vOGkP^4uQQ=a%u=7p{_V3TflZrJ#2-hr!d-3=*Jhaty9ljwZ_L{nMwV{1Bu7&*@BH{ z^w{6(xFsu_0{HZ-;L84ZD>bn?^mCX_eff*)##th+d@F6rWGH}_-AVLl&mP=(XzSzW zBGo$RmWd1XxL9qnf3arr(XPAexSF2s=k?E}>=*?_Nlsl>d-{WO^_Vxbn@?Fs6Mg2M{_`p~c7E2R};uE~Hfsn|4 zvLe%{FR73`1P`~}R`(PGG=f>PQH2wTQzeZfD!nXM{Hw8tr}VPHK}-pqj||zTQrD6m zo1ssi7Ef=3ZLTlI72xyKCmf3*oeA^;FJZ{DoHdcqi8lr^{afF^KJ;K4%8Q!gr3-TX zTf}ku;L^o^wjq3y>$TYAP}=m|Y(1b&uKVguV3z^;6pz^XKqF1efNO( ziWS5d-Tt!dz^K>vCBCZc`LuJX{GOzUKgYkZD)`H9flQU^zZ^bhgN9`FS7%o2Ju_b= zhil@Znb99aW2Af!-$q+F4*=6yRr&}O$Tc3!E47F})QmF9dQV~qmu_SzNu@@+(yioP zFFCbXvdyz255L#ip=2!qC3*dh!o|=|ZeS}J5W2621sF6R&84oQOW?GHp0ii8Ji65z z`CXJw`B2*ydh7``Yi|^)+-FB`PL~pLY!6(QQgHS8AI+EWXy0nIpjt{VZgM#zPO#2~qZ=ZZfE%Z^&$Ec$I{`C#BhbjskTw z?gEyt;*XCX;pwSC5)}W4kBq8GQXRe~VFYCAG6=?{z5Ue^=zy#wXs)lae)WbsNU)5C z1J0upAIX{65)ODE{ddUhoMRn3>ZFL>6;A!WB2^l zO80@VV-D}(0iTiFwF*lKQ%-519J_koZ%*~Q?(*EfP~=6|r~4{7dLUCa)8kZY>;e>5 zdW)dR2?Nl`ge_YK3JGmex<6q**tn|0fD`R&XaTH%8S}s5z(Ey1yiDndj@-+QVySKf zD3A-f2v9*ZS^Bcm$`a9Ulkkd7=?8d;rCtZOJd=DaeV^?Kw0QYXaK$5eqR;|o z-Uyooomy^(getC~q5~x@j!79I>hx>d{|*5OC2Ask*aaH;6o{@OB<|864ZxvPFE#=nvncKP0ajOs8r==Ojx79UcW$xz@sP;74uVXm-5n6;Ygl&KLI#PC2 zAD2|sKi;K#ja%=X38_9T3mhuIE02p^)hmUV3nk#N4Emf%QC&9*^em*v#r%w8AJmmT zxa~j|{VRSS5g^#~?tK%;mx=(%Z-`JGTzPG_gUOq249uAA#9%A8sEGR+|N_yh_@!{##*@ z1|Sz^a`qXz(VT-)G>7F5A-UYJ$LdUFtUUS5UD~;)o*nF+V>pTR94-LIfwe4r8IZ`g z>@9eDmiW+Wh{DAUQupVlGtK^|)msq#)cZRKMn?%RKGw77B<&Nh!+V$%ecc|K4dAqe zf$`}YN(&W#OHm0YCpu{ z%iGj;bsLMHkOr#TOOupe_Q-|)yCF4U5{gX#I_R#$g!s#-I|ZuSqJ`5k_gW*x$t)Mw zMqwMxAHwYC5L-4$Z##Ohe5bQ?zB4$U%nN#za72>Ql$o2LtjLGOK2;n0IwUzX2_pm% zh^P#Nn3?-U2<_YQOaIDYJhskHVqJ7XjuEM>ekZ3R7ymJ8JXW=Z07PDw_LlY!1566m zMsi2kg1)7&cFDKtHRuYLqasnc)7@F`7U^J=Pe?2X`21$L`Xg1@Jg=+>JS9xdK`Zc+ z(mLMSBGdkV-IV2~vR+#+8AF>4lQac)CXL=b>qoYW;5p42E@MI=m+E6-kxjV*%RPucS|uoeT^0esJx-i z-1^?tVg4b1yZJohv0)Q!j)KTfIEYu4qL@6R)`sprT-Cb*f7ybcX1nYh|9Eq*xr~SV zghnkPrrer9!)T7beSFTn<19GD$oCCl>H3uA3b()H&XYVMe_|0Qj^Y29e9OeAAq880 zA}Tq-3%yQULJ>S7YV+c6JTJ`EB>HxXD%wQ2+@3hT45MG})t0|rM-)xXvP)9Q(+k;O zjzt=p{jT8^?ovq=hfSsZ81Lep^z8{x@9HCFHnV>HRHtJ2Jv-5SB&x+Z`C*^kesTVU zZWW6hT^Yg=FlKU}RY%Xy?Y0pK75W-fo#DHR`P4fQ;4Y|V7r~OTBI|HV4VJb_^WWwxHXtcNi=7^FkSa@MZ2x}_Y4~r;u zKwL?XoLk;VL2R)(jD?nHQ+G0c;)PjVR4C!Mvf<>m9UWBMgJO;-7ejS}+c=OH^Fk)c(*N_laKEcZ zmHQP^?Ppw2U?Ywp8E;2I2c7}4$#wuqwz6KfLnh9VLar?!D7)KoRvc6ei#Sb?J2TY) zR~1A^L!X#)Ul}nM-XMGXi5|v)t};$~;;T)Y@4mAQc7Ye0E{`MLJ!a^8Oy9rF>1;db zltw>{_Q@V{Ed496Lt|NZY+T?IbU)ED6Dfswb3eb_h5~t^@i+l z>-Wdiw?ksxNqkk%mK%#Jh77NpNQ4R7SJYkH;otVTQgSG?+V{~Fe6_xy%BDlKIG$x;} zYxa#RU4_s6qypXqWs{fRbyxE^>x+mIH|phjGJ+jGor!0%)Cz9hB|^-+Oi^HmUn9|w zL4Cj#BS)<&H-X+!HEV!3O!TA<)$sz-dFldP4L&eyF&a8DE99`)@hkRTgI}a}8Jq|( zc;|Z&`)*hv&_j##QPg7KWaEh*NkF@9M)Xp8WFNC8bBTZdT$Em>QU7$aF;C1+V z(NN_#cFgF%#c1^0O?8JnAE4#?1Oo=ZU*8+J<8h1gd26Ii_1WctOs_q|5hO zs>6P0UASta3b?!Izopx+y7aEA>F(cGF-n1(+i8R(Agv~05NR7H+gt`lSwvJ`Um*-G z2F-Wbk<@;6ABuS^hpp22$z-Mxoc!K#OsaL^=k{H7WjAsuqTj*aJ+LTq@k2!V<$I4m z7ctbXr71M7!Y-9T%d=)%Idjyk5XK01SX)tw>z|mKQ26Ef*n<-v3hE_~SD@jT{sJdX zCT?TM{;2m2mdIp7qqor9r%LP3M{W&E1*PAqyk0xAGvEcDphUq-KB_|%Ai z|E~U$+14v?jqpk z`a<7-1bohzbo%DC^_|)5FK>BfmAGC2cIKm@XC8{NKXdwTicyJ4 zU(8kZb{k7q30eeI(ex_gECSw}yO8t2=S3(tgyf_kogfOYJ_GRaaIF7 z0QM*A-_rP)03t(I)20YmJ~(ybelLO!2BuM6gmh@QUFt^oXOKX&;E zfw>wzErIIgoGJj$6Rk}K40WYW zKNMU_p}|7nmtU&npBz6?UAEWlt{dtU2|lZ2ZBw&u>;;woIPtr?2-8*#T-mo~by@j? zA~Y{bWMDqYfBxZRh*d|$aIP5pehV{`h5WmvqgIU06CfV99TNK%_mZi2jjIL7GJ7UK-p zh@)T^YK(yuA(7zYy%Yg38|=|c2ksZ`Lf-ne3Hta||GD1bZBDi1bT*r~p8s~nx}1wi z7#G~(I9450WPkfJML=a#awnGxyL`|WN!i^0bI|*eJC_%P&CQ;sjAtm-Q^E6WPgAyo z?9Kx#Ou8F3n5AAtWTORE(D1im^Of-ux0B|TNfsb26z~?in=RVGW z$M_eg7t6U=0aSbM6SHS^6Mlu$poXi$5r!5J4n|cbX1(%_$^oQ2ISo6V&K&cJooGoR zNjbc0n+7}+8FkwC$JuzD{om9~L_-J}*SfO?h0K|h!PU2qM0@f<40H30{9Td*#D^H= zzt|T6GjGK05FvzYFij0#z3iwmhahmO#Q?0Dhk#d=X+$%6LYl)+dpAhg+?B8pug#Vp zOWifmcxHDc$>!U;x&1lMD{R_ZHwG;T<$0d6Zh2Jg>DG#tdjL27;7W5OtR>N+{-Nf) z#V@cL{VwR1>M=NAQsQG*m&Nr>w+J;DxietW*yXlKr59BfK9FQGQlvxnCb>QExcLmF zW0WQEju7s1$m$q4PKHyYS^W8q#^t1P@M4lVnA|d&G7e=R>E-5|5(g`J``{O{$`Z(5 zotzKuU3-^Xm?gW+EChruMk4xDYMc5BNSj9HipW(NCdUyr`HmtZPC&|ER%M(Fm zaf9_z!(U}kN*wIfW@skvUGBDDjpthENqU?XIn45Yna$AR;$pU5G<1T0_)>79DSZd$FTzSx$Z~i`_bVbex8hY{>#2*5_1dN>Kc(p5g()hI0ISt;XHB2t#KueA%(TtBt z@*5J7B~|P}8W z8Te!9Zr-W@Y*eStbZT#wrOQ0*J@MpnRi8lILYy8ce=`JGSb`LC;VpPec_)rx5X%V5q>Y?%XQ-M6VMCd<-7Yj`op?Obgo zzQ~1qb=FCiy-q;~2vrQ}pG6K$ZTz=?h}bK89*+V~-F-AHBLfqyz0(~IHESU<=ktgq z>N=WllEvQrE-)CQoHKKSnA!oB-D6t%HE^aN-(DC!kEKh$k5Y*SI5*Sc=ikq#uqS0T zbY#`PC43-tJFxOBtC%r>yGmBa4h_Dj3EGW#2G1^gW0!iJDUe2QeoIRiR(a0*{0a6N z*F;lDEUsf#om1R5YyH4QgsB{V8nuk=MK96oCdCic#lWeXEn?G>yEaEA61ygo&Ei(d zoIs{Cpv5m%oX`GkSg?GdKTpP`&M~?>^MbeQj0BDPaPoRIq&pFSD~>X@uQ z`p3%~lYehy=yWcj!sA;1IFQg$27DQwqa0l#`~uu7CGsKO`vZcQKl9^T+FK!(z}d&e z9dS&`3qsj3h3u0XIWe!_g|jIFiYrgmUv9_&ysC*iHSz4io@yrSmrd}L?i9jU7tG_S@?mE17ULdxXPHx7JH~NE3nViPJc>7QH(ay32z*LZrl z2(Xa~EHk1bYf(#6AD&z8-}FvM+`VyrIeUD-SY~KQiprtPwR6!1kj4f8EA3;y*lCV9^nTOA=v%cF~b+3^=e220?O#X7e7Z z)yi->rtEpipa|b(wzXLLxZkv2)|Vj$4lDaU3)4?uNzvaI$VWgl_i^DO& zI|Dz=Xp9aj%5@2y@Js>Y&a7*eIKyfZFaVst_W3}FyB5)IJ88s#j*PXI-H%{xPJ}aW zDLmz{w!uIm$;fY4;w-@yy>c%iKB{s$A%jAQvAGbBH;we4`}oAU z{%Lwqq|hMNl4|@ZCNQ%Q@-q7Vgt1*eQce8Qi1Jz82`c8%9>i*7Pw{7P#h9$K&?GUF zk3U^RO^&3K2zj2VX5z~Ug>U98F@((NTT-ru^9YkEYit1sK{eZvUx(Mz5&g^>%dcr* z*3e8gWsj8ale4#uO58gmA~K*_ZD~fm3_dM5d_`jUwOl)IKj7-lb}Sz3_X11$w%i=M zcM+h^Z{w2P&M?%n#GdBe0viudm0-Os8NPD`JH2~s)3xq8xD5ZM!$P?gJMthW#KFEw zSRM$R{PKWQ?8a+6iT3ls#H|KxA+Sua$nX&PWyr6*L zSM4iS_t?UY6mke{o`g04hapnp$#46Z&gU!UiUjghhoL^ws6YD?=JMY6<)bhq@0lEE zvpOhsFilGnf$D>;UoM+~$v#u2O3m_(KA++AwUlF4G=;un4EeuUWk!ErNW9GNUBrNs zc-SmXW8a|bF&tx>napWjyHXp07_6nz_GYa*nXx4YOy z)}llhH65c8NU9)*lz}n)Pn2qJx9BZBaeh{k{A!UOEpnQF>uv~fGpb;HyZ*E8{AB-( z>b*Zqf1Tyf2e2{;_T47qr1HNr`RvgCw(1EKNS+o*XK(5dVP6NGx$72h5A*B4)WY*} z5QGk&ZVV0-Sn}2N{mKbXYsv<5UB3JZ5vLYEm#s?hBmObYUDsRw)3ag`?GyBl!CF8q zJYvkY2vVWUakhf1I(Abnw~wc^;p?kj=K^>~n8a_O9KPTdTQG3wdH7`HgvPD8-lp1G zA>v+=5+22{}~1C2t#_HH7MgoyDZ0SyW+#3n9H>-#jt3 zf!%O%=g?eFdhT~T;uyMIws^hXh!%fzJ&treUL1o1+J3L=Z*1ONO1xZFUYf|P3U*)I zPz{x>qv~E3hMqQb@k?HAVlVC|@5Apu>7|h!*&y|cotRv${XX_lW_Qv*XWggrzmfu( zzs#aNXre`Zt#ne}j=aGz{Ymb=>tko@3;RPy4d#4tY3aGuK+N=jl{z&{AnQVnBms&! zCLnqRcfm>x>=L_?7<`8$A%8fN5If~iml$(Qv2OeU={%QRoKGy5>Zg>_Yx>04ml23# z4n}0>!wrQx)mBT`pE8(Byy&kQLECU%5xUJJonsGMfyOK&Z=&PF z4Rp8F97%C9otXqO9J&)2Cy_)j47!7ZWxwRXVV^?%0{X&;`m>dZBs=4$;JSsxAhTQ<(ov3YE7CRQ#=R}QEt_iHW#h;7 zcr#5qk|U>GH6k3a+KfcWAan8eS*ZFDgd(I45&bJ8_8fiEUy@^%xp?c`=RlE@w(P@z z$HaRP7Z3{a1%qPL-!-*33y+V#>n^$r>fR1(ImZ{1A-6smJTQ-ZH~%c`(SyR8 zRMiNcLRj9xu|Tae0o@kCaxYLdH3CBFosH9-aP7^ovxOy1VhbN#jSr(ErDx~{sBNjJK}Z~8L^OLX6`Jss#*fpk(u<-8NANIh`HTvJpzd!xbO?WE{GP=0zOXV|cJ zDwnSJtQF42Y+xs4haqEJa;Xw*PoFURy;jJU&Gpx7RzwE4Ui+ngb#xu|hP~zfg7r9t zEN5a4IV7`xLZb8jHB<4w!7kguEvje9Q)a*=*yvR>7o4+IM8mK#THUDg-eKWI3;yUm33{e2wJ5h`@5vkI04?vahU5Uk z;Z*Gp6Qc6{??n#v`}OZN7)7*Uv*B$k|2-=M zZNjTqRph$auFyIClSS-|L9yihU)&RNpnH>L`uz%soWGE`1Y%a#wstNfoKQ$w#sQvw z8S#KWf$fE;&_K@LPYkU(=N53HU#qUdCRoqw9l)(tkUF|hq*8zJW<~5JVkEOHD;Aj! z!7#E2mUTE$hyw?B5FoFp*Lr6$|qcij09e49I zb$IBl%G?1d`hv`&5e-q1%t73J^CA6v#Sbl3Q-(9_-_RiobM0#=z9*E_fpWco8KHW3 z!_vBv+MvYC{>w^^xVOyeo$D3%%~wmjlCcU4gCZBOyOjBaN3==KD$>;m!zzqe=%OsX zPv%iQcyd{T-$5N;6!Psl<;fwY)322d44^nD@Aon(gk!vlD8Ym9MR%eiak znAdo}-<@B z$JCdL-JHkdfQ!tV3O4@)RsX{@fy)6anHz#|`3#G>dD977@cbD=oH{%#dnk-;V2Hu3 zT{YMbQf=)!rd_M$abkj9a`0JKp7uJ=0Q={uXzrIme&A&=)y0iNX1A6vza$q9CbU zNA=xfB8Q+qAbznvZ4b{1*v7|T@7ub8tI}-OX{v=*t?1Kl7RjTK13^O%-bZ*Te!0r& zeB6E=G6(cg(e7+-_7nsBd5&5U9efF`$Ln*Kx~o)k-c6Po3Pf|=Vq9%Js6}$>{DncK zXZN$W3a|`ns@!N5=87Co{w1(__eqgXj*a{|x*hPiKD@~8s(Cqj!WH_xHJFp)X{Dle z&+$l7QkeN<(~nH`oV0`*w0B5)VvMBZQq7a|XSM=A2PtzgjYEZbpR`+7`fYOGm?)gx zW~>ps%%cmJEK$9?WkngRLQQEk^BUKKbX1!otD2{}28E0`&#p)k`ROi+gTII`cRw{uJI3-P9Vr-;I6?W(%ADwtK?wMiRBc}e;>$O zG&}`7_e_>&{ta$z+WoK}bY15k^+^oHU2?Sc=hOK+N2sRt%-%Q0lg6R!6DOk6qPHhV zjpm2!Y!h`7AYroc1)OU1noMv;g&WHX7R`L)IHWoezBp3v-lxV5h^F65_HA^!w1P|s z+|ykAH(d6Chrhh81X^E|LUP;^_D^4)x=sUlJtagteeMwUfWmc2bKcwD$ezbmJhznS z3r{P=W@ta<8ehRXZ=oZ`q0?-|M~`VK)tED$liKpMaI1YUcFU&_nnm@&MCx~4Bmiy+ zxwTr1eKykVM&wD}tF$>z0XD?y4(R6mA5rpUjzk{yoRgYA^AB$~V9DPP$=bWP^TwZptWmW1RJr>F* zbAo&o@4rVZt9rP4FZdUJ$sj80e_tGMzuWZ$et`fl!8YgiEL+d3uLH8I{pX!?I~0C^ z;){3pgnc)7N2xQ2&0h$89NkB8nB{?t$PLa;e3r(x)7b`my z|NF#VLeO2wnZD%!u37Fa%q3X+7vZN*Pm4c~mEH>X{78*~U;PpvdD83Qk9P?Dutpg{ z>MBklhPh5?EdIvVv$UbbzIpU+j!$bjGOA`F+PKetMZ!jf@96g>vZC&MdFqSIU z>c7hh^ny3N+^pXt{-O%Sr_!muf_%_zGZC(vTJH0Zo zw4)g(9?g$U?$6zQY@cwGPO8&GkT7&XPwtrf0&1%((>&F@$Q3 zRXK2mTjP>GBWd3`-$7y09a$rX*nSgf<^#lE%_~6%auXgKxY9G1!2!8JiP$66&8X84 zlo;&^-gxaU4mD5tglgKhq(1lsT0$)y2{ri1J0g11e&ako6RN6;M2H;&U(aQa1&w%8_-wT`rRVq>5`%}H^9_Q@^ zRIz*`Ir#~nCZJhVs@M%DMj?b>pKFwF`TrDrKGL=<_%s=dPV=$Ak5SLul`nXe9 zY;}`m|6&9q*EAn_4^iDnz}&p!nR_`kM>Q0gzRw?-K*y0Qx57@n)bQ8Zuj850!w3mbR1Ln^l$h>-v|<4te`U2vR( zC|dOJsWpnY@|pg(M;iIg{(e9|oae*U*<)-)!0qXC*0H37vBTqiV1Ksu$xOxp*M4KE z7}vX_x*cgS`C!jDD&eqRl?AKm^c(~TJeN52V)+*Txeeb~KP1(!s_m-^MEM&PlT0g( zE~C(UEoJRgP1Y#G+dqY7N)kI<=v~g}$%O9Y-+ai^bO*sxQ?S}Fz9918FeqJLV%7Jz zk*$jx#@&Qh!g=#_oyx9Ki&4+byw&pj-{Q3R#-=kB@mnX0Ec1% zl8n>R3`^swx6t?)v}ntbPo+UNPBm>g5Y_TQb0?WP%{bm@?2_iDh93kSybae2I3^nt zX_`C&wd(ks^Z8&bR3DlEY_bW^)fL4Ee?}WRC&skliJ(Ux5mj9_)ocB}U?+^2uv->5 zn-R8dF$2>zJ9PV%7L$vH7!6my_ftL*E(Vz=`Z|$9|J9A!W%r6Iu=*gyDuXe;b-Mk} z;plVq{F9gtVI3iN^pbuymOSfduyr)e-MoxT1#zPV-XH9FwEEbO7k5CiGIor1;X83~ z2@135X*=teymb_-GVyK+bnEz;%fB}cWyR-Tpz!m;$t&Vax(0&|z&avZl~5%=S`l_F zBQy&8&)V&e6^m4#7vZFUri(vCLRzB5$K-&t&cn+FW8p&?Z0`0Qkm0le+rB7ys}vRA ziK&Z^Ya+9G)x6FIFp9V`VzbhkSu6io0Wwb&aFs{K@E?9pCJ&AyN3yfB?!UDgmc5}I z3YsD_C62J9%0Z*#ZJh#r_!=Q5d)Y3+F5kUP!UpuOSvE@gBWtj3&o`5ASri^sOa*V*(|7|gLVL#! z=gW>Y`wCihX|REK%hddOn_2kj1S0?~TFnH_Jqs(GB+N(jeXBwmM@WXBYYmV&PmzLL zsdtHTfi`d(f4wW{;}l<@yWJ8x@^X^MMD(kz(0^JW(kEmk&igesv(5dq=Dg zlx*|luZ}1a6=2o^(o#+ty`fnztv#gaT?zQ&JArCV#>pDTNG{Us9b1um^}v^SC9LVj zxhS~>K#LZ(lq8UXU1X0 zX5K&b-5J-!6n-L$)_wf`;W7AgGI5FF7N005rK+1;bcvCp`kdsTK_hbG(lAJC-5X-U z0gxV^Z`&NFp z_JmqkaAf_JV}Fnwp>)A+5QQ*|Hx4N~cjb7x=!(MSr&^fu;LuU65DrDr^G`3AS6QX{ z4lozCVJg!iry{TrKqb<^7h=-fEyCJ5pRV3#=cBR{7~}7MBe_EP_}h~857A`!&warw z1n0q&Y5?t^)nMhXP;ms(d0^mhu;DeDdGKn3AF06^1KCKOtuQSO*+4>>>pPeThfs`+ zK!)K7(G!w$!zEL3KhIYyqm}Q`x+K`;=-E^e`P!VvbzA}-H8m@jQ;!5z_}e|=_3Sj^ zwuw~(V6Xi=&en@+N-y0nPp<)zufG|h4inDI-qW>iJ0H1-DA-)8qBY){=#!Sn^iG+= z`&z|k?EC7>Fxcbt37OmAgGUSa+$iC0%^3n%NCzVuf7fwg~~b z&(YHyIx?5z(=EK$1S=KyW*|e|#IP>a7WEc#4K91gVl&K(=BqY(O~_87+PM%G`98S# zYD`JugZneqT}g(l*Lm4Lpk|9L5kTHk_{Tk3;Z)DazPN#n3rKe;E?p~{ZoO#ZY$ummnWdfr;dVRvcDB=4+UNBn+ifJ$QzGLg zn|E~WX@o!Ur0z%^2*c}3VS9*cK_F4`-w`iz4r(qPzx4o@s9at->g$%lKrhK(QHaXG zt-wC@tJkDd3yb*mi^5_o30U+q?y=5=q=>Wt*z=OIr>LyvniyHXOaaN?Uz;I(yl^k z5RTPeS5At?1#Zg6K+wlb3!Sil8oYF{4_j9(I}qnTwo5{h0XrU(1NIq%IY0MN@{*Ux~lb}*f}ywwZnc%l2y}HI10N;EB_OPrVWT!9uiZVcnY3R z<*PJ%sH)LX>3i5NZdIsfmwE1D7CZHim5AnXIfZgF{FY%GvrXZoUTi*B7|S=f;y*CX z|4=OoGbsYoFI7WEGQDK+!qu?RsJtV$%>NYbv2@v(YOO&3kVFeA8i~^zxJ?!G8M$Z# zIK1Y^t$6lL(k>uPSX%X1)mR-&7*0uY!WX$70*zgGSO4>-4Cn=Lm}76QC7;cB7^YockRlgeN*(x#Ej~@qHj>_ z*J67|S)H58KeBN)ejt%=blWWQ)^kqlL}A;3?B(y}ko;&eTU=t7;%ZCLW_6I-C3+fz zC8%?VFQY$fW8vlP%z z1m^4-k+aMkbPz=N$ps4{^~L$ZiX*<4l?pL9eimWN+5WmeH?6m{2x7|oJuZINk}giqlmI1q|F>U~kq zLz=Mit&@Dx$mN=f3u9|S6!uI(^9$YztrtN;&=+dALRGV^3ZZi)%lds;N|bS4JE!0r z0(@mgzU==mdxYNn-G2*qT8^S%VM2Zvcj@R#nJR#Y% z2ej7aAvC`Q`#^f+Q!1L1e7OGq1>i0p(MszC15q4soK9jUHYNw2M0>z0zm3K3XtX?x zZ05^A^J5M&qVuj@-8Rs)?ey5d;=CK1j(33?^yP;w^IE(Oj?n1S#a?~=<5=Kp+qWI) zc#TQhu8rG4m^KE2=+|v^y?QKAvs=lJILtt1;eLSJc(Y&udmr(yxb-o9FZO~3*$DYT zBt8$jNVqya$J#bdT5*Uy>1SAaTG^LqK{AW|%K+U3ncGdx5(w}t#udx9de1}N**6|S z#3Z1mA`Rp?k6&0YHGJUvojYj0kwdBO1@h9_uBmqJ{t6q{y;#Kp#V*RR4qeATfoyvP zk9J!>ZKvJoGi+L65%a>>XTDUB0A+ls+hPYQJ;0`0qfMUKcOi#Q^`6UML=#$`tH&;q zHOikc(dB-C+<3d_cSt-uys%5&o8@{i1dp;vPWe3O4)jI@(7pNArYwlJyzQ;0grljh8m|GQ{yO8P1=86@!df*2&FA32<*o-ic$Xhq#9EQ?( zIFPQR<^i65If_`!_Frlva$nb&=xZ(u_|T>-Rz=A-FO!iqT*j`Extq}!MXeYVcy#0* zl*OvAP-Ba9*i5@C56t3Q$rf3B#}ZuU)&GLI7EGBRu|yKbbMWr{Gmsl^8Cl`Ayv6k5 zXET~k%cdyZ>aV!S@#%P=NgoWfrt{k<8$m;I3HA`-Uu4hGwkgXJZ22Slpz2!ST}Si2jM$YcCjE-i`U{rN=o6Z2B@sgT zcsJGPIqCWUQCzpfmEQg7>jPH3TdAgn>1*IdjdMe^Ompu`G~qBPx)nxv=Tci^yvFHT zu6Zx$m4~@MM``Wf50D#g7PXwckomPj=pg;&s2xDxwm^2?Jk_glQ0xcKsWaRDeEOL{ zY<)l28VI{JL_3FYgxQ{5f`8M--#V&ep#8*|>e?L7Xw2YUri z>Pno7S0QaB+Z@tWMZ1;_KP2aOj!*V~9W2cEuFl zdFQ>{hx=PtH@+|6%`i8F7W;#2!nNxaKZ?nDznJZKEGv zK^=nuVS1xOjxz<|XOn6Vuk=I4kl@n%uSm0BT9Z2+w4VJ_(7{ z8?AezPVXuDq=UP2+IUFJ*hCj<#=W_#E8(fn?!g zo5*M)u_?w*w6dz-@GdtTP$fyf?aQMT)zO|;ChT!7j1 zt)rEWFMMEy`vG#}jeih5%*ZBu}Zmgjf&p7v&{f3DmB6 zn0KeCnoOlFbRE?uQvC*|&F6s;E)j~;+j4(fh@+<247e$sOO=))}MTI&yJ#WY~$ z&%M&0`K@`!&qe2H_! z3UX*Y;(rh zu(;Mp5?_J|ouK}hW<@phkPG%lrg6a)}vg5-$E=7Lk| z()9CR%W^op)h;&IsE=Ix(UgiDMAwVl0i1)8|R=dLHL*C)t>+-Naq3zS+T~qr$Nwe&+X*YA2 zxj4_Ah3-Oonrb4eGuA=7;LMvoQnc;1N8Q!{&E%h9M|c*eWXcm+q${Qi8h)i%dSTK4 zzP%2e1g+aRxov^B+L|wVIxYiq!f4#HWfEa#xa}lU^IB}#at<;R&9_Y>@=hCU-(V_d z>|V%u9D!c<_XFg{n}s)%7qXUh{zcgdi;f*LfD+x%SBREc!$tD(@41Y5HUQfWzSM4% z=y>$C@r*&6vJpi4_4sz&Ht`|S!f=Joiho_i_Tkk3NBeUmvawII^$AfSzT-qyPml=dn4jg# z?qTd!EHtiMW0YB6eh=XLMuUgHc+|B7F zw->1HxBmQhuz-x?{Q>e0KP0%Ev;IBULhy#nzPtd9X$YRe!i{xM9S6ej)6M+C@GV%b z{hRJ_66CIk&-AiD)-L^VVZMXE1tVLx!ee~bfj>dT7CgYxj*0XQ1wV8dveIAP?tVx4 z8z^5*Yq3doBqUqt$5*EF7)M;9IMg({Q>e0KQy?sA8VY2 z9mZS#Ed;FHh;GKfZNte*gVpnAb<4k#D9M&s{o)Xy_Xm$YryW>h;0VJ@zyCzXS{&E3 zk=4BNLbYyQN4LDY<`eo*yEM>F6_5YRvXZyp``#aLAM=2R5-m7hR%7v{7hf8<%U*q# z*Y=dGYGbafBY4q#`1~FY{0SM~W#We-Tj;b^(LZfv!04RzXW;k^yoA2&yCxa2eQrof zUq;Kj;En^)h;g~^zXN%P9~QEZUF})SZo-8eh6rGt%^A3DvWI%`CHu^|=}o&d0*DtK z)k@(G18juK&V~bRzT~in=IRu1I6nOJ1BNmxmu=x@Gg;wLY+}M1^7j(Vz39rHP|w!*Aub=<@!#P178a+!@niG;0C|TW z5?J8-hX1sIJGGWlaZ1VfUQ86}XwNcIhXtupT~u)gAt% zHxPcgQWr2rWG@gyf0wWh#y6e$4o^C8g_+!fQqy`Lqkk}5x{KK#w%(^*@EQU7sd2$t zqw{KaU<5w&tVL-^jj!D}HM+F!2sEopE1cf1~%Eh;SXl z>H}}8K9cy34uIe;yy$*8bU&WA9<0sly6o5X*|>nUWsLrp|H62IcPrdI`$9N}Ir_oy z_x=ERhaVR9uR;3HT#E*}xY^`qv+sr<9`bt=*g&YQ?g*iFZ`}_b;MfkVsSu+>g4|_+ z7gEONd;A!fHjL$0?Z5+{{z9Ix?7H3&v{x2q8}$xP`qDS&gSQ8vS8M8U>D@k+1;Afh zH3J$J1s1*Q(eAbj⪚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 QhyVZp07*qoM6N<$f~OVlNdN!< diff --git a/romfs/res/drawable/music/ic_music_forward.png b/romfs/res/drawable/music/ic_music_forward.png deleted file mode 100644 index 1901df682ffb7687b865e100ed902a9c7f3da175..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4454 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 d5SzTG25^kDj`c64q)XsP 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 H?ZW;KTt8D8 diff --git a/romfs/res/drawable/music/ic_music_play.png b/romfs/res/drawable/music/ic_music_play.png deleted file mode 100644 index 958847e0d5b32dde388344cea58cf8b488f68d5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3913 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 diff --git a/romfs/res/drawable/music/ic_music_shuffle.png b/romfs/res/drawable/music/ic_music_shuffle.png deleted file mode 100644 index ad041c7715aab51034900c27e5d439f9299e2c6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5427 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 diff --git a/romfs/res/drawable/music/ic_music_status.png b/romfs/res/drawable/music/ic_music_status.png deleted file mode 100644 index eee3526731abaf7a2c5d003fa1cb91a8c70864dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3014 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 IM6N<$f^X82u>b%7 diff --git a/romfs/res/drawable/textviewer/textview.png b/romfs/res/drawable/textviewer/textview.png deleted file mode 100644 index 0c36b69ee79d5c1dbf0334c4abcbb59838f4d38b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1606 zcmeAS@N?(olHy`uVBq!ia0y~yV4MJCf8byPl6p2qNdX0YL!E^^dF>855GEFYo+*jM4JXKUE;F=y8Kd^{>Ybb8OTV7<}Hm<493f zc+Sew(3#76Bs2n#;UJs0=_%)Pu`*y~q-?IqNx3LZ1oIii<{q<|FzVX>yxaMx#xpc-i5rGc3EMopv@7-6KTQ6I= zCE9;a`985{R;Ks+?2etyIIeK^$I836&NFTJeCKnO-B$P%}|DOBn?_ zPwz}lkHxlvGkvXQvJHCjpgUBZd}ZN?YO>uPA@~OdDr(<2bs~x|pSz^VFKM!8lf8Tb zNq%{r@Avn6?(_V~y}6^qLl1R!Z0|q_bq)?>M#y)U_ujCMd@lC>cz}Fuv<4;|gf`vk zy>CFrU%3sTo_qAMad$ksS60nJNYTtm94Z$qqDCmOyKE`y40gFmJf$1Sz^@;i4sg1b z4D63)g{+mv)B3=ijYsE(#?-kPHLeAAr#cd4nItG+SK-Qqyy3{@WWdiWlh|wK1Dr2$ zXOe+luOK&`9p=)ejk##3JE#iXF)kJliP2ar5_^!71To4BVLmJbrLY_kUXXZ63KB)onKfLc95kFAl_aZq zGT2dV-Ewu);Jm!bq*-*6fq+-2_Nrc2!KxK9oGLp)kuNJ2FNOrZl}S@;9;;~Q{h?_p zkMp>I4c8$)v8B(NHeJ)1Hrte}b+;`{<|Lb~)owZ7LZM}8$L&8t5LA)2bUI_R7UoB= zV-{@{_a7mbvZK}2yLzrZISrY-p0;DhWxb8f>#5lMrBIon1i4l@ZDYkX?J?8Lrz)## zcoiX+PW!8hyMMoKXlBWIAi+0V>hoRChBJzbQ)E5Hf}#)D4TPT{i7+aa#dNN7Ua=XIcM4h#ZBo2WeW3_F_qx!jX+sj zRdU+Qn>HC1PKgP=RWoq;D@`6W99J<^JeWz5hLEmnvLc0JINXDS-HM5u-lDY!=$rq|TUX(c3N6)Y>(s2KF_nAF*|E(kXeM@kW2GC0e9eQ))nW z$oiZG>z1ru|12BDhZ|d0JMy}JCRvK@U~k7I1FKuEc8Jx^D9_{XTv<`Q(=4TW8v<(y zzM-`~teUfG)Bo=TR`LUT`x>QbY+PwWRZ{t?_DLKw=av*3_fC;DaW#9rlC{y+oIb&O zr+;2=oQO(m_Sr@5o0aiX0ObX1L%sRDoJ{!CU{$;|?}PvL7fDvgs|+X&Y=s9(`_P7KqrB9I0( zX1J(`pn&0mG^jDdMMVS!3>Tz9jTtT~A}C8Ps4>GuMFa&57oj3(}y* z3>Os<6fj(n1~q25sED9|;es@%F~dbg1O*Hiq(O}tE-E4@V7MR+YJ5dp9rZ{5utA>p zE0G8L-aWAN8F?s>QwK(}2+i$8=&1#S{=Q7Ue?h1yAhdD_A^By5wwW(Jer6BRzB8EV z9V>tBp3Oeor)*h&fiG-bKi_xmqqlc&-ge^-`}u>XcgbJAx#4sBzOL?h$KvRR-52vYMGqC<`16_0qpux%Z|@eI zvv;MJPki4Wx#jZC#dDkHPh~fb&fI)v`Gtd*v=cvl@p0el`wl(k@_(%?@0(m%xpeG( z{>&3IFJ1oj?%$UBZ(k@~oH#$c`QHn_-+AvR=fB(cgMh@RfA0Ke-K(FH@uR{1q0Gs? HL-YRu8?*OK diff --git a/romfs/res/drawable/wifi/stat_sys_wifi_signal_1.png b/romfs/res/drawable/wifi/stat_sys_wifi_signal_1.png deleted file mode 100644 index 049a3880422a927eb0e923c7f39f28b10cd31a2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14689 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 diff --git a/romfs/res/drawable/wifi/stat_sys_wifi_signal_null.png b/romfs/res/drawable/wifi/stat_sys_wifi_signal_null.png deleted file mode 100644 index f12b0a63bd0dd02868895ec321458cfe3cd3a851..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14675 zcmeI3TWB0r7{`yLYD}pVe6ZeN*j9<+?9A*Xn;mAirrFd4-6T!6G%bjo%$!Ze?9NPQ zCfQ9YDAkG=@S#{x5qz-^TB;Tl#0pX`AgHAxN-a`~v=IeS5HD3qJu|b}{dTk4(@G!D z!tQ)I=l}n{Grx1myv#FWBloZEzO@@6w03wXH%_iM`roeAjBr>r?I6N&hR>Yv(umF;>V)2*>16osm43sO`R+L?5%?y={bQZO`K z6L1NavFW+QC$;t2GnQw$GggO^_3n;^$(-c#_1bO6TQ0XP?Rtar1VIgHTc=xCu(2?X zU2D$K@ZdbTl@0Cg-ZP4g$!W^u_jDXPt{NR|eotr%mO?l~3b|K#z`?3#IR(oqWy4iA zwv3P)7zkDs*R$O)b*tjutq84_#(X!j;hgH>ELo2!UJ`jRRuDxwCdtVJFD7MC1W7_= zOOCvy8^wioIbyQZ3v&QvYYwu1bk$QYD+|5n*fd{4B57mGi0ihnh^*0vVSY%mLXvE7}^BL;a*br=__-pNfg(BS`*l%?i6) ztvfDlh25>zhI5-~c-dI3T1zu<`6q99EKHDx1f1r&68Z;2v<31+|gA|4AZe9kOnno zxTuJrfZ>8Ps4>GuMFa&57oj3(}y*3>Os<6fj(n1~q25sED9|;es@%F~dbg1O*Hiq(O}t zE-E4@V7MR+YRqs^5kUdN1!+)YhKq^_3K%X(gBmkjR76m~a6uZ>nBk%#f&zvM(xAo+ z7Zni{FkFxZHDIYL5&$MDk3OgxF8K`d`Vp0jaUD$N#6IX zkQe*b-O}>|c`1<7hQ{*Yb;TP9Kqen|Y&Pt>>05 xb{#uBdhoj=`}Uss=AXYG{deiKQ98Q^y}kPTL*swwtH_|y@Zd=9&CR>^{s*qt^Sl56 diff --git a/source/C2D_helper.c b/source/C2D_helper.c new file mode 100644 index 0000000..c8368e0 --- /dev/null +++ b/source/C2D_helper.c @@ -0,0 +1,53 @@ +#include "common.h" +#include "C2D_helper.h" + +void Draw_EndFrame(void) +{ + C2D_TextBufClear(dynamicBuf); + C2D_TextBufClear(sizeBuf); + C3D_FrameEnd(0); +} + +void Draw_Text(float x, float y, float size, Color color, const char *text) +{ + C2D_Text c2d_text; + C2D_TextParse(&c2d_text, dynamicBuf, text); + C2D_TextOptimize(&c2d_text); + C2D_DrawText(&c2d_text, C2D_WithColor, x, y, 0.5f, size, size, color); +} + +void Draw_GetTextSize(float size, float *width, float *height, const char *text) +{ + C2D_Text c2d_text; + C2D_TextParse(&c2d_text, sizeBuf, text); + C2D_TextGetDimensions(&c2d_text, size, size, width, height); +} + +float Draw_GetTextWidth(float size, const char *text) +{ + float width = 0; + Draw_GetTextSize(size, &width, NULL, text); + return width; +} + +float Draw_GetTextHeight(float size, const char *text) +{ + float height = 0; + Draw_GetTextSize(size, NULL, &height, text); + return height; +} + +bool Draw_Rect(float x, float y, float w, float h, Color color) +{ + return C2D_DrawRectSolid(x, y, 0.5f, w, h, color); +} + +bool Draw_Image(C2D_Image image, float x, float y) +{ + return C2D_DrawImageAt(image, x, y, 0.5f, NULL, 1.0f, 1.0f); +} + +bool Draw_ImageScale(C2D_Image image, float x, float y, float scaleX, float scaleY) +{ + return C2D_DrawImageAt(image, x, y, 0.5f, NULL, scaleX, scaleY); +} \ No newline at end of file diff --git a/source/cia.c b/source/cia.c deleted file mode 100644 index 683a05c..0000000 --- a/source/cia.c +++ /dev/null @@ -1,116 +0,0 @@ -#include - -#include "cia.h" -#include "progress_bar.h" -#include "utils.h" - -static Result CIA_RemoveTitle(u64 titleID, FS_MediaType media) -{ - u32 count = 0; - - Result ret = AM_GetTitleCount(media, &count); - if (R_FAILED(ret)) - return ret; - - u32 read = 0; - u64 * titleIDs = malloc(count * sizeof(u64)); - - ret = AM_GetTitleList(&read, media, count, titleIDs); - if (R_FAILED(ret)) - { - free(titleIDs); - return ret; - } - - for (unsigned int i = 0; i < read; i++) - { - if (titleIDs[i] == titleID) - { - ret = AM_DeleteAppTitle(media, titleID); - if (R_FAILED(ret)) - { - free(titleIDs); - return ret; - } - break; - } - } - - free(titleIDs); - - if (R_FAILED(ret)) - return ret; - - return 0; -} - -static void CIA_LaunchTitle(u64 titleId, FS_MediaType mediaType) -{ - u8 param[0x300]; - u8 hmac[0x20]; - - APT_PrepareToDoApplicationJump(0, titleId, mediaType); - APT_DoApplicationJump(param, sizeof(param), hmac); -} - -Result CIA_InstallTitle(const char * path, FS_MediaType media, bool update) -{ - u32 bytesRead = 0, bytesWritten = 0; - u64 fileSize = 0, offset = 0; - Handle ciaHandle, fileHandle; - AM_TitleEntry title; - - Result ret = 0; - - char * filename = Utils_Basename(path); - - if (R_FAILED(ret = FSUSER_OpenFileDirectly(&fileHandle, ARCHIVE_SDMC, fsMakePath(PATH_ASCII, ""), fsMakePath(PATH_ASCII, path), FS_OPEN_READ, 0))) - return ret; - - if (R_FAILED(ret = AM_GetCiaFileInfo(media, &title, fileHandle))) - return ret; - - if (!update) // As long as we aren't updating 3DShell, remove the title before installing. - { - if (R_FAILED(ret = CIA_RemoveTitle(title.titleID, media))) - return ret; - } - - if (R_FAILED(ret = FSFILE_GetSize(fileHandle, &fileSize))) - return ret; - - if (R_FAILED(ret = AM_StartCiaInstall(media, &ciaHandle))) - return ret; - - u8 * cia_buffer = malloc((512 * 1024)); - - while (offset < fileSize) - { - u64 bytesRemaining = fileSize - offset; - ProgressBar_DisplayProgress("Installing", filename, bytesRead, fileSize); - - ret = FSFILE_Read(fileHandle, &bytesRead, offset, cia_buffer, (512 * 1024)); // (512 * 1024) - chunk size - ret = FSFILE_Write(ciaHandle, &bytesWritten, offset, cia_buffer, bytesRead, 0); - - if (bytesRead != bytesWritten) - { - AM_CancelCIAInstall(ciaHandle); - return ret; - } - - offset += bytesWritten; - } - - free(cia_buffer); - - if (R_FAILED(ret = AM_FinishCiaInstall(ciaHandle))) - return ret; - - if (R_FAILED(ret = svcCloseHandle(fileHandle))) - return ret; - - if (update) - CIA_LaunchTitle(title.titleID, MEDIATYPE_SD); - - return 2; -} diff --git a/source/config.c b/source/config.c new file mode 100644 index 0000000..7732dce --- /dev/null +++ b/source/config.c @@ -0,0 +1,105 @@ +#include +#include +#include + +#include "common.h" +#include "config.h" +#include "fs.h" + +const char *config_file = + "config_dark_theme = %d\n" + "config_hidden_files = %d\n" + "config_sort_by = %d"; + +Result Config_Save(bool config_dark_theme, bool config_hidden_files, int config_sort_by) +{ + Result ret = 0; + + char *buf = (char *)malloc(256); + snprintf(buf, 256, config_file, config_dark_theme, config_hidden_files, config_sort_by); + + if (R_FAILED(ret = FS_Write(archive, "/3ds/3DShell/config.cfg", buf))) + { + free(buf); + return ret; + } + + free(buf); + return 0; +} + +Result Config_Load(void) +{ + Handle handle; + Result ret = 0; + + if (!FS_FileExists(archive, "/3ds/3DShell/config.cfg")) + { + // set these to the following by default: + config_dark_theme = false; + config_hidden_files = true; + config_sort_by = 0; + return Config_Save(config_dark_theme, config_hidden_files, config_sort_by); + } + + u64 size64 = 0; + u32 size = 0; + + size64 = FS_GetFileSize(archive, "/3ds/3DShell/config.cfg"); + size = (u32)size64; + char *buf = (char *)malloc(size + 1); + + if (R_FAILED(ret = FS_Read(archive, "/3ds/3DShell/config.cfg", size, buf))) + { + free(buf); + return ret; + } + + buf[size] = '\0'; + + sscanf(buf, config_file, &config_dark_theme, &config_hidden_files, &config_sort_by); + + free(buf); + return 0; +} + +Result Config_GetLastDirectory(void) +{ + Handle handle; + Result ret = 0; + + if (!FS_FileExists(archive, "/3ds/3DShell/lastdir.txt")) + { + FS_Write(archive, "/3ds/3DShell/lastdir.txt", START_PATH); + strcpy(cwd, START_PATH); // Set Start Path to "sdmc:/" if lastDir.txt hasn't been created. + } + else + { + u64 size64 = 0; + u32 size = 0; + + size64 = FS_GetFileSize(archive, "/3ds/3DShell/lastdir.txt"); + size = (u32)size64; + char *buf = (char *)malloc(size + 1); + + if (R_FAILED(ret = FS_Read(archive, "/3ds/3DShell/lastdir.txt", size, buf))) + { + free(buf); + return ret; + } + + buf[size] = '\0'; + + char tempPath[256]; + sscanf(buf, "%[^\n]s", tempPath); + + if (FS_DirExists(archive, tempPath)) // Incase a directory previously visited had been deleted, set start path to sdmc:/ to avoid errors. + strcpy(cwd, tempPath); + else + strcpy(cwd, START_PATH); + + free(buf); + } + + return 0; +} \ No newline at end of file diff --git a/source/dir_list.c b/source/dir_list.c deleted file mode 100644 index b999d72..0000000 --- a/source/dir_list.c +++ /dev/null @@ -1,378 +0,0 @@ -#include -#include - -#include "archive.h" -#include "cia.h" -#include "common.h" -#include "dir_list.h" -#include "fs.h" -#include "language.h" -#include "menu_gallery.h" -#include "menu_music.h" -#include "menu_textviewer.h" -#include "pp2d.h" -#include "textures.h" -#include "theme.h" -#include "utils.h" - -int position = 0; // menu position -int fileCount = 0; // file count -File * files = NULL; // file list - -struct colour TopScreen_colour; -struct colour TopScreen_min_colour; - -static void recursiveFree(File * node) -{ - if (node == NULL) // End of list - return; - - recursiveFree(node->next); // Nest further - free(node); // Free memory -} - -// Sort directories alphabetically. Folder first, then files. -static int cmpstringp(const void *p1, const void *p2) -{ - FS_DirectoryEntry* entryA = (FS_DirectoryEntry*) p1; - FS_DirectoryEntry* entryB = (FS_DirectoryEntry*) p2; - - if ((entryA->attributes & FS_ATTRIBUTE_DIRECTORY) && !(entryB->attributes & FS_ATTRIBUTE_DIRECTORY)) - return -1; - else if (!(entryA->attributes & FS_ATTRIBUTE_DIRECTORY) && (entryB->attributes & FS_ATTRIBUTE_DIRECTORY)) - return 1; - else - { - if (sortBy == 0) - return 0; - else if (sortBy == 1) // Sort alphabetically (ascending - A to Z) - { - char entryNameA[0x200] = {'\0'}, entryNameB[0x200] = {'\0'}; - u16_to_u8((u8 *) entryNameA, entryA->name, sizeof(entryNameA) - 1); - u16_to_u8((u8 *) entryNameB, entryB->name, sizeof(entryNameB) - 1); - return strcasecmp(entryNameA, entryNameB); - } - else if (sortBy == 2) // Sort alphabetically (descending - Z to A) - { - char entryNameA[0x200] = {'\0'}, entryNameB[0x200] = {'\0'}; - u16_to_u8((u8 *) entryNameA, entryA->name, sizeof(entryNameA) - 1); - u16_to_u8((u8 *) entryNameB, entryB->name, sizeof(entryNameB) - 1); - return strcasecmp(entryNameB, entryNameA); - } - else if (sortBy == 3) // Sort by file size (largest first) - { - u64 sizeA = entryA->fileSize; - u64 sizeB = entryB->fileSize; - return sizeA > sizeB ? -1 : sizeA < sizeB ? 1 : 0; - } - else if (sortBy == 4) // Sort by file size (smallest first) - { - u64 sizeA = entryA->fileSize; - u64 sizeB = entryB->fileSize; - return sizeB > sizeA ? -1 : sizeB < sizeA ? 1 : 0; - } - } - - return 0; -} - -Result Dirlist_PopulateFiles(bool clear) -{ - recursiveFree(files); - files = NULL; - fileCount = 0; - - Handle dirHandle; - Result ret = 0; - - if (R_SUCCEEDED(ret = FSUSER_OpenDirectory(&dirHandle, archive, fsMakePath(PATH_ASCII, cwd)))) - { - /* Add fake ".." entry except on root */ - if (strcmp(cwd, ROOT_PATH)) - { - files = (File *)malloc(sizeof(File)); // New list - memset(files, 0, sizeof(File)); // Clear memory - - strcpy(files->name, ".."); // Copy file Name - files->isDir = 1; // Set folder flag - fileCount++; - } - - u32 entryCount = 0; - FS_DirectoryEntry* entries = (FS_DirectoryEntry*) calloc(MAX_FILES, sizeof(FS_DirectoryEntry)); - - if (R_SUCCEEDED(ret = FSDIR_Read(dirHandle, &entryCount, MAX_FILES, entries))) - { - qsort(entries, entryCount, sizeof(FS_DirectoryEntry), cmpstringp); - u8 name[255] = {'\0'}; - - for (u32 i = 0; i < entryCount; i++) - { - u16_to_u8(&name[0], entries[i].name, 254); - - if (name[0] == '\0') // Ingore null filenames - continue; - - if ((!isHiddenEnabled) && (strncmp(name, ".", 1) == RL_SUCCESS)) // Ignore "." in all Directories - continue; - - if (strcmp(cwd, ROOT_PATH) == RL_SUCCESS && strncmp(name, "..", 2) == RL_SUCCESS) // Ignore ".." in Root Directory - continue; - - File * item = (File *)malloc(sizeof(File)); // Allocate memory - memset(item, 0, sizeof(File)); // Clear memory - - strcpy(item->name, name); // Copy file name - strcpy(item->ext, entries[i].shortExt); // Copy file extension - item->size = entries[i].fileSize; // Copy file size - - item->isDir = entries[i].attributes & FS_ATTRIBUTE_DIRECTORY; // Set folder flag - item->isReadOnly = entries[i].attributes & FS_ATTRIBUTE_READ_ONLY; // Set read-Only flag - item->isHidden = entries[i].attributes & FS_ATTRIBUTE_HIDDEN; // Set hidden file flag - - if ((!isHiddenEnabled) && (item->isHidden)) - continue; - - if (files == NULL) // New list - files = item; - - // Existing list - else - { - File * list = files; - - while(list->next != NULL) // Append to list - list = list->next; - - list->next = item; // Link item - } - - fileCount++; // Increment file count - } - } - else - { - free(entries); - return ret; - } - - free(entries); - - if (R_FAILED(ret = FSDIR_Close(dirHandle))) // Close directory - return ret; - } - else - return ret; - - // Attempt to keep index - if (!clear) - { - if (position >= fileCount) - position = fileCount - 1; // Fix position - } - else - position = 0; // Reset position - - return 0; -} - -void Dirlist_DisplayFiles(void) -{ - pp2d_draw_textf(84, 28, 0.45f, 0.45f, RGBA8(255, 255, 255, 255), "%.33s", cwd); // Display current path - - u64 totalStorage = FS_GetTotalStorage(BROWSE_STATE? SYSTEM_MEDIATYPE_CTR_NAND : SYSTEM_MEDIATYPE_SD); - u64 usedStorage = FS_GetUsedStorage(BROWSE_STATE? SYSTEM_MEDIATYPE_CTR_NAND : SYSTEM_MEDIATYPE_SD); - double fill = (((double)usedStorage / (double)totalStorage) * 209.0); - - pp2d_draw_rectangle(82, 47, fill, 2, RGBA8(48, 174, 222, 255)); // Draw storage bar - - int i = 0; - int printed = 0; // Print counter - - File * file = files; // Draw file list - - //scroll_x = 395; - - for(; file != NULL; file = file->next) - { - if (printed == FILES_PER_PAGE) // Limit the files per page - break; - - if (position < FILES_PER_PAGE || i > (position - FILES_PER_PAGE)) - { - if (i == position) - pp2d_draw_texture(TEXTURE_SELECTOR, 0, 53 + (38 * printed)); // Draw selector - - if (strcmp(multi_select_dir, cwd) == 0) - multi_select[i] == true? pp2d_draw_texture(TEXTURE_ICON_CHECK, 8, 66 + (38 * printed)) : pp2d_draw_texture(TEXTURE_ICON_UNCHECK, 8, 66 + (38 * printed)); - else - pp2d_draw_texture(TEXTURE_ICON_UNCHECK, 8, 66 + (38 * printed)); - - char path[500]; - strcpy(path, cwd); - strcpy(path + strlen(path), file->name); - - if (file->isDir) - pp2d_draw_texture(TEXTURE_ICON_FOLDER, 30, 58 + (38 * printed)); - else if ((strncasecmp(file->ext, "3ds", 3) == RL_SUCCESS) || (strncasecmp(file->ext, "cia", 3) == RL_SUCCESS)) - pp2d_draw_texture(TEXTURE_ICON_APP, 30, 58 + (38 * printed)); - else if ((strncasecmp(file->ext, "mp3", 3) == RL_SUCCESS) || (strncasecmp(file->ext, "ogg", 3) == RL_SUCCESS) || - (strncasecmp(file->ext, "wav", 3) == RL_SUCCESS) || (strncasecmp(file->ext, "fla", 3) == RL_SUCCESS) || - (strncasecmp(file->ext, "bcs", 3) == RL_SUCCESS)) - pp2d_draw_texture(TEXTURE_ICON_AUDIO, 30, 58 + (38 * printed)); - else if ((strncasecmp(file->ext, "jpg", 3) == RL_SUCCESS) || (strncasecmp(file->ext, "png", 3) == RL_SUCCESS) || - (strncasecmp(file->ext, "gif", 3) == RL_SUCCESS) || (strncasecmp(file->ext, "bmp", 3) == RL_SUCCESS) || - (strncasecmp(file->ext, "tga", 3) == RL_SUCCESS)) - pp2d_draw_texture(TEXTURE_ICON_IMG, 30, 58 + (38 * printed)); - else if ((strncasecmp(file->ext, "bin", 3) == RL_SUCCESS) || (strncasecmp(file->ext, "fir", 3) == RL_SUCCESS)) - pp2d_draw_texture(TEXTURE_ICON_SYSTEM, 30, 58 + (38 * printed)); - else if (strncasecmp(file->ext, "txt", 3) == RL_SUCCESS) - pp2d_draw_texture(TEXTURE_ICON_TEXT, 30, 58 + (38 * printed)); - else if (strncasecmp(file->ext, "zip", 3) == RL_SUCCESS) - pp2d_draw_texture(TEXTURE_ICON_ARCHIVE, 30, 58 + (38 * printed)); - else - pp2d_draw_texture(TEXTURE_ICON_FILE, 30, 58 + (38 * printed)); - - char buf[64], size[16]; - - strncpy(buf, file->name, sizeof(buf)); - buf[sizeof(buf) - 1] = '\0'; - - pp2d_draw_textf(70, 58 + (38 * printed), 0.45f, 0.45f, RGBA8(TopScreen_colour.r ,TopScreen_colour.g, TopScreen_colour.b, 255), "%.45s", buf); // Display file name - - if ((file->isDir) && (strncmp(file->name, "..", 2) != RL_SUCCESS)) - { - if (file->isReadOnly) - pp2d_draw_textf(70, 76 + (38 * printed), 0.42f, 0.42f, RGBA8(TopScreen_min_colour.r, TopScreen_min_colour.g, TopScreen_min_colour.b, 255), "%s dr-xr-xr-x", FS_GetFileModifiedTime(path)); - else - pp2d_draw_textf(70, 76 + (38 * printed), 0.42f, 0.42f, RGBA8(TopScreen_min_colour.r, TopScreen_min_colour.g, TopScreen_min_colour.b, 255), "%s drwxrwxrwx", FS_GetFileModifiedTime(path)); - - } - else if (strncmp(file->name, "..", 2) == RL_SUCCESS) - pp2d_draw_text(70, 76 + (38 * printed), 0.45f, 0.45f, RGBA8(TopScreen_min_colour.r, TopScreen_min_colour.g, TopScreen_min_colour.b, 255), lang_files[language][0]); - else - { - Utils_GetSizeString(size, file->size); - - if (file->isReadOnly) - pp2d_draw_textf(70, 76 + (38 * printed), 0.42f, 0.42f, RGBA8(TopScreen_min_colour.r, TopScreen_min_colour.g, TopScreen_min_colour.b, 255), "%s -r--r--r--", FS_GetFileModifiedTime(path)); - else - pp2d_draw_textf(70, 76 + (38 * printed), 0.42f, 0.42f, RGBA8(TopScreen_min_colour.r, TopScreen_min_colour.g, TopScreen_min_colour.b, 255), "%s -rw-rw-rw-", FS_GetFileModifiedTime(path)); - - pp2d_draw_textf(395 - pp2d_get_text_width(size, 0.42f, 0.42f), 76 + (38 * printed), 0.42f, 0.42f, RGBA8(TopScreen_colour.r, TopScreen_colour.g, TopScreen_colour.b, 255), "%s", size); - } - - printed++; // Increase printed counter - } - - i++; // Increase counter - } -} - -static Result Dirlist_SaveLastDirectory(void) -{ - return FS_Write(archive, "/3ds/3DShell/lastdir.txt", cwd); -} - -// Get file index -File * Dirlist_GetFileIndex(int index) -{ - int i = 0; - - File * file = files; // Find file Item - - for(; file != NULL && i != index; file = file->next) - i++; - - return file; // Return file -} - -/** - * Executes an operation on the file depending on the filetype. - */ -void Dirlist_OpenFile(void) -{ - char path[512]; - File * file = Dirlist_GetFileIndex(position); - - if (file == NULL) - return; - - strcpy(fileName, file->name); - strcpy(path, cwd); - strcpy(path + strlen(path), file->name); - - if (file->isDir) - { - // Attempt to navigate to target - if (R_SUCCEEDED(Dirlist_Navigate(0))) - { - if (BROWSE_STATE != STATE_NAND) - Dirlist_SaveLastDirectory(); - - Dirlist_PopulateFiles(true); - Dirlist_DisplayFiles(); - } - } - else if ((strncasecmp(file->ext, "png", 3) == RL_SUCCESS) || (strncasecmp(file->ext, "jpg", 3) == RL_SUCCESS) || - (strncasecmp(file->ext, "gif", 3) == RL_SUCCESS) || (strncasecmp(file->ext, "bmp", 3) == RL_SUCCESS) || - (strncasecmp(file->ext, "tga", 3) == RL_SUCCESS)) - Gallery_DisplayImage(path); - else if (Music_GetMusicFileType(path) != RL_SUCCESS) - Music_Player(path); - else if (strncasecmp(file->ext, "txt", 3) == RL_SUCCESS) - TextViewer_DisplayText(path); - else if (strncasecmp(file->ext, "zip", 3) == RL_SUCCESS) - { - Archive_ExtractZip(path, cwd); - Dirlist_PopulateFiles(true); - Dirlist_DisplayFiles(); - } - /*else if (strncasecmp(file->ext, "txt", 3) == RL_SUCCESS) - menu_displayText(path);*/ -} - -// Navigate to Folder -int Dirlist_Navigate(int dir) -{ - File * file = Dirlist_GetFileIndex(position); // Get index - - if ((file == NULL) || (!file->isDir)) // Not a folder - return -1; - - // Special case ".." - if ((dir == -1) || (strncmp(file->name, "..", 2) == 0)) - { - char * slash = NULL; - - // Find last '/' in working directory - int i = strlen(cwd) - 2; for(; i >= 0; i--) - { - // Slash discovered - if (cwd[i] == '/') - { - slash = cwd + i + 1; // Save pointer - break; // Stop search - } - } - - slash[0] = 0; // Terminate working directory - - if (BROWSE_STATE != STATE_NAND) - Dirlist_SaveLastDirectory(); - } - - // Normal folder - else - { - // Append folder to working directory - strcpy(cwd + strlen(cwd), file->name); - cwd[strlen(cwd) + 1] = 0; - cwd[strlen(cwd)] = '/'; - - if (BROWSE_STATE != STATE_NAND) - Dirlist_SaveLastDirectory(); - } - - return 0; // Return success -} \ No newline at end of file diff --git a/source/dirbrowse.c b/source/dirbrowse.c new file mode 100644 index 0000000..830732c --- /dev/null +++ b/source/dirbrowse.c @@ -0,0 +1,354 @@ +#include + +//#include "archive.h" +#include "C2D_helper.h" +#include "common.h" +#include "config.h" +#include "dirbrowse.h" +#include "fs.h" +#include "menu_gallery.h" +#include "menu_music.h" +//#include "menu_book_reader.h" +#include "textures.h" +#include "utils.h" + +int initialPosition = 0; +int position = 0; // menu position +int fileCount = 0; // file count +File *files = NULL; // file list + +void Dirbrowse_RecursiveFree(File *node) +{ + if (node == NULL) // End of list + return; + + Dirbrowse_RecursiveFree(node->next); // Nest further + free(node); // Free memory +} + +// Sort directories alphabetically. Folder first, then files. +static int cmpstringp(const void *p1, const void *p2) +{ + FS_DirectoryEntry* entryA = (FS_DirectoryEntry*) p1; + FS_DirectoryEntry* entryB = (FS_DirectoryEntry*) p2; + + if ((entryA->attributes & FS_ATTRIBUTE_DIRECTORY) && !(entryB->attributes & FS_ATTRIBUTE_DIRECTORY)) + return -1; + else if (!(entryA->attributes & FS_ATTRIBUTE_DIRECTORY) && (entryB->attributes & FS_ATTRIBUTE_DIRECTORY)) + return 1; + else + { + if (config_sort_by == 0) // Sort alphabetically (ascending - A to Z) + { + char entryNameA[256] = {'\0'}, entryNameB[256] = {'\0'}; + Utils_U16_To_U8((u8 *) entryNameA, entryA->name, sizeof(entryNameA) - 1); + Utils_U16_To_U8((u8 *) entryNameB, entryB->name, sizeof(entryNameB) - 1); + return strcasecmp(entryNameA, entryNameB); + } + else if (config_sort_by == 1) // Sort alphabetically (descending - Z to A) + { + char entryNameA[256] = {'\0'}, entryNameB[256] = {'\0'}; + Utils_U16_To_U8((u8 *) entryNameA, entryA->name, sizeof(entryNameA) - 1); + Utils_U16_To_U8((u8 *) entryNameB, entryB->name, sizeof(entryNameB) - 1); + return strcasecmp(entryNameB, entryNameA); + } + else if (config_sort_by == 2) // Sort by file size (largest first) + { + u64 sizeA = entryA->fileSize; + u64 sizeB = entryB->fileSize; + return sizeA > sizeB ? -1 : sizeA < sizeB ? 1 : 0; + } + else if (config_sort_by == 3) // Sort by file size (smallest first) + { + u64 sizeA = entryA->fileSize; + u64 sizeB = entryB->fileSize; + return sizeB > sizeA ? -1 : sizeB < sizeA ? 1 : 0; + } + } + + return 0; +} + +Result Dirbrowse_PopulateFiles(bool clear) +{ + Dirbrowse_RecursiveFree(files); + files = NULL; + fileCount = 0; + + Handle dir; + Result ret = 0; + + if (R_SUCCEEDED(ret = FSUSER_OpenDirectory(&dir, archive, fsMakePath(PATH_ASCII, cwd)))) + { + /* Add fake ".." entry except on root */ + if (strcmp(cwd, ROOT_PATH)) + { + files = (File *)malloc(sizeof(File)); // New list + memset(files, 0, sizeof(File)); // Clear memory + strcpy(files->name, ".."); // Copy file Name + files->isDir = 1; // Set folder flag + fileCount++; + } + + u32 entryCount = 0; + FS_DirectoryEntry* entries = (FS_DirectoryEntry*) calloc(MAX_FILES, sizeof(FS_DirectoryEntry)); + + if (R_SUCCEEDED(ret = FSDIR_Read(dir, &entryCount, MAX_FILES, entries))) + { + qsort(entries, entryCount, sizeof(FS_DirectoryEntry), cmpstringp); + u8 name[256] = {'\0'}; + + for (u32 i = 0; i < entryCount; i++) + { + Utils_U16_To_U8(&name[0], entries[i].name, 255); + + if (name[0] == '\0') // Ignore "." in all Directories + continue; + + if ((!config_hidden_files) && (strncmp(name, ".", 1) == RL_SUCCESS)) // Ignore "." in all Directories + continue; + + if (strcmp(cwd, ROOT_PATH) == 0 && strncmp(name, "..", 2) == 0) // Ignore ".." in Root Directory + continue; + + File *item = (File *)malloc(sizeof(File)); + memset(item, 0, sizeof(File)); + + strcpy(item->name, name); // Copy file name + strcpy(item->ext, entries[i].shortExt); // Copy file extension + item->size = entries[i].fileSize; // Copy file size + + item->isDir = entries[i].attributes & FS_ATTRIBUTE_DIRECTORY; // Set folder flag + item->isReadOnly = entries[i].attributes & FS_ATTRIBUTE_READ_ONLY; // Set read-Only flag + item->isHidden = entries[i].attributes & FS_ATTRIBUTE_HIDDEN; // Set hidden file flag + + if ((!config_hidden_files) && (item->isHidden)) + continue; + + if (files == NULL) // New list + files = item; + + // Existing list + else + { + File *list = files; + + while(list->next != NULL) // Append to list + list = list->next; + + list->next = item; // Link item + } + + fileCount++; // Increment file count + } + } + else + { + free(entries); + return ret; + } + + free(entries); + + if (R_FAILED(ret = FSDIR_Close(dir))) // Close directory + return ret; + } + else + return ret; + + // Attempt to keep index + if (!clear) + { + if (position >= fileCount) + position = fileCount - 1; // Fix position + } + else + position = 0; // Reset position + + return 0; +} + +void Dirbrowse_DisplayFiles(void) +{ + //Draw_Image(icon_nav_drawer, 20, 58); + //Draw_Image(icon_actions, (380 - 64), 58); + float title_height = 0; + Draw_GetTextSize(0.48f, NULL, &title_height, cwd); + Draw_Text(70, 18 + ((34 - title_height) / 2), 0.48f, WHITE, cwd); + + int i = 0, printed = 0; + File *file = files; // Draw file list + + for(; file != NULL; file = file->next) + { + if (printed == FILES_PER_PAGE) // Limit the files per page + break; + + if (position < FILES_PER_PAGE || i > (position - FILES_PER_PAGE)) + { + if (i == position) + Draw_Rect(0, 52 + (38 * printed), 400, 38, config_dark_theme? SELECTOR_COLOUR_DARK : SELECTOR_COLOUR_LIGHT); + + if (strcmp(multi_select_dir, cwd) == 0) + { + multi_select[i] == true? Draw_Image(config_dark_theme? icon_check_dark : icon_check, 5, 61 + (38 * printed)) : + Draw_Image(config_dark_theme? icon_uncheck_dark : icon_uncheck, 5, 61 + (38 * printed)); + } + else + Draw_Image(config_dark_theme? icon_uncheck_dark : icon_uncheck, 5, 61 + (38 * printed)); + + char path[512]; + strcpy(path, cwd); + strcpy(path + strlen(path), file->name); + + if (file->isDir) + Draw_Image(config_dark_theme? icon_dir_dark : icon_dir, 30, 56 + (38 * printed)); + else if ((strncasecmp(file->ext, "3ds", 3) == 0) || (strncasecmp(file->ext, "cia", 3) == 0) || (strncasecmp(file->ext, "bin", 3) == 0)) + Draw_Image(icon_app, 30, 56 + (38 * printed)); + else if ((strncasecmp(file->ext, "zip", 3) == 0) || (strncasecmp(file->ext, "tar", 3) == 0) + || (strncasecmp(file->ext, "lz4", 3) == 0)) + Draw_Image(icon_archive, 30, 56 + (38 * printed)); + else if ((strncasecmp(file->ext, "mp3", 3) == 0) || (strncasecmp(file->ext, "ogg", 3) == 0) + || (strncasecmp(file->ext, "wav", 3) == 0) || (strncasecmp(file->ext, "mod", 3) == 0)) + Draw_Image(icon_audio, 30, 56 + (38 * printed)); + else if ((strncasecmp(file->ext, "png", 3) == 0) || (strncasecmp(file->ext, "jpg", 3) == 0) + || (strncasecmp(file->ext, "bmp", 3) == 0) || (strncasecmp(file->ext, "gif", 3) == 0)) + Draw_Image(icon_image, 30, 56 + (38 * printed)); + else if ((strncasecmp(file->ext, "txt", 3) == 0) || (strncasecmp(file->ext, "lua", 3) == 0) + || (strncasecmp(file->ext, "cfg", 3) == 0)) + Draw_Image(icon_text, 30, 56 + (38 * printed)); + else if ((strncasecmp(file->ext, "pdf", 3) == 0) || (strncasecmp(file->ext, "cbz", 3) == 0) + || (strncasecmp(file->ext, "fb2", 3) == 0) || (strncasecmp(file->ext, "epub", 4) == 0)) + Draw_Image(icon_doc, 30, 56 + (38 * printed)); + else + Draw_Image(icon_file, 30, 56 + (38 * printed)); + + char buf[64], size[16]; + strncpy(buf, file->name, sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + + /*if (!file->isDir) + { + Utils_GetSizeString(size, file->size); + float width = 0; + Draw_GetTextSize(0.48f, &width, NULL, size); + Draw_Text(390 - width, 180 + (73 * printed), 0.48f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, size); + }*/ + + float height = 0; + Draw_GetTextSize(0.48f, NULL, &height, buf); + + if (strncmp(file->name, "..", 2) == 0) + Draw_Text(70, 52 + ((38 - height)/2) + (38 * printed), 0.48f, config_dark_theme? WHITE : BLACK, "Parent folder"); + else + Draw_Text(70, 52 + ((38 - height)/2) + (38 * printed), 0.48f, config_dark_theme? WHITE : BLACK, buf); + + printed++; // Increase printed counter + } + + i++; // Increase counter + } +} + +static Result Dirbrowse_SaveLastDirectory(void) +{ + Result ret = 0; + + if (R_FAILED(ret = FS_Write(archive, "/3ds/3DShell/lastdir.txt", cwd))) + return ret; + + return 0; +} + +// Get file index +File *Dirbrowse_GetFileIndex(int index) +{ + int i = 0; + File *file = files; // Find file Item + + for(; file != NULL && i != index; file = file->next) + i++; + + return file; // Return file +} + +/** + * Executes an operation on the file depending on the filetype. + */ +void Dirbrowse_OpenFile(void) +{ + char path[512]; + File *file = Dirbrowse_GetFileIndex(position); + + if (file == NULL) + return; + + strcpy(fileName, file->name); + strcpy(path, cwd); + strcpy(path + strlen(path), file->name); + + if (file->isDir) + { + // Attempt to navigate to target + if (R_SUCCEEDED(Dirbrowse_Navigate(0))) + { + Dirbrowse_SaveLastDirectory(); + Dirbrowse_PopulateFiles(true); + } + } + else if ((strncasecmp(file->ext, "png", 3) == 0) || (strncasecmp(file->ext, "jpg", 3) == 0) || + (strncasecmp(file->ext, "bmp", 3) == 0) || (strncasecmp(file->ext, "gif", 3) == 0)) + Gallery_DisplayImage(path); + /*else if (strncasecmp(file->ext, "zip", 3) == 0) + { + Archive_ExtractZip(path, cwd); + Dirbrowse_PopulateFiles(true); + } + else if ((strncasecmp(file->ext, "mp3", 3) == 0) || (strncasecmp(file->ext, "ogg", 3) == 0) + || (strncasecmp(file->ext, "wav", 3) == 0) || (strncasecmp(file->ext, "mod", 3) == 0)) + Menu_PlayMusic(path); + else if ((strncasecmp(file->ext, "pdf", 3) == 0) || (strncasecmp(file->ext, "cbz", 3) == 0) + || (strncasecmp(file->ext, "fb2", 3) == 0) || (strncasecmp(file->ext, "epub", 4) == 0)) + Menu_OpenBook(path);*/ +} + +// Navigate to Folder +int Dirbrowse_Navigate(int dir) +{ + File *file = Dirbrowse_GetFileIndex(position); // Get index + + if ((file == NULL) || (!file->isDir)) // Not a folder + return -1; + + // Special case ".." + if ((dir == -1) || (strncmp(file->name, "..", 2) == 0)) + { + char *slash = NULL; + + // Find last '/' in working directory + int i = strlen(cwd) - 2; for(; i >= 0; i--) + { + // Slash discovered + if (cwd[i] == '/') + { + slash = cwd + i + 1; // Save pointer + break; // Stop search + } + } + + slash[0] = 0; // Terminate working directory + } + + // Normal folder + else + { + // Append folder to working directory + strcpy(cwd + strlen(cwd), file->name); + cwd[strlen(cwd) + 1] = 0; + cwd[strlen(cwd)] = '/'; + } + + Dirbrowse_SaveLastDirectory(); + + return 0; // Return success +} \ No newline at end of file diff --git a/source/fs.c b/source/fs.c index af1d1e8..e79a38a 100644 --- a/source/fs.c +++ b/source/fs.c @@ -91,7 +91,7 @@ bool FS_DirExists(FS_Archive archive, const char * path) return true; } -char * FS_GetFileModifiedTime(char * path) +/*char * FS_GetFileModifiedTime(char * path) { static char timeStr[20]; u64 mtime = 0; @@ -125,7 +125,7 @@ char * FS_GetFileModifiedTime(char * path) } return timeStr; -} +}*/ u64 FS_GetFileSize(FS_Archive archive, const char * path) { diff --git a/source/misc/keyboard.c b/source/keyboard.c similarity index 91% rename from source/misc/keyboard.c rename to source/keyboard.c index 20f493c..712c792 100644 --- a/source/misc/keyboard.c +++ b/source/keyboard.c @@ -4,13 +4,13 @@ #include "keyboard.h" -char * keyboard_3ds_get(int maxTextLength, const char* initialText, const char* hintText) +char *OSK_Get(int maxTextLength, const char *initialText, const char *hintText) { static SwkbdState swkbd; static SwkbdStatusData swkbdStatus; static SwkbdLearningData swkbdLearning; - char * str = malloc(maxTextLength); + char *str = malloc(maxTextLength); memset(str, 0, maxTextLength); swkbdInit(&swkbd, SWKBD_TYPE_NORMAL, 2, maxTextLength); diff --git a/source/language.c b/source/language.c deleted file mode 100644 index e8e93e2..0000000 --- a/source/language.c +++ /dev/null @@ -1,194 +0,0 @@ -#include "language.h" - -/* - The languages are in the order based on CFG_Language: - - 0. Japanese - 1. English - 2. French - 3. German - 4. Italian - 5. Spanish - 6. Simplified Chinese - 7. Korean - 8. Dutch - 9. Portugese - 10. Russian - 11. Traditional Chinese -*/ - -const char lang_welcome[][3][100] = -{ - {"こんにちは", "ご機嫌よう", "今日は"}, - {"Hello there", "How are you today?", "Today is"}, - {"Allo", "Comment allez-vous aujourd'hui?", "Aujourd'hui, c'est"}, - {"Hallo", "Wie geht es dir heute?", "Heute ist"}, - {"Ciao", "Come stai?", "Oggi è"}, - {"Hola", "¿Cómo estás?", "Hoy es"}, - {"你好!", "今天过得怎么样?", "今天是"}, - {"안녕하세요!", "오늘 하루는 어떠세요?", "오늘은 "}, - {"Hallo", "Hoe gaat het?", "Vandaag is het"}, - {"Olá!", "Como está hoje?", "Hoje é"}, - {"Привет", "Как поживаешь?", "Сегодня"}, - {"嗨", "今天好嗎?", "今天是"} -}; - -const char lang_months[][12][50] = -{ - {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}, - {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}, - {"Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"}, - {"Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"}, - {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}, - {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}, - {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}, - {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}, - {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}, - {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}, - {"Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"}, - {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"} -}; - -const char lang_days[][7][50] = -{ - {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, - {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, - {"Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"}, - {"Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"}, - {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, - {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, - {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, - {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, - {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, - {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, - {"Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"}, - {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"} -}; - -const char lang_files[][11][50] = -{ - {"Parent folder", "Folder", "APP", "Payload", "archive/zip", "archive/rar", "image/png", "image/jpeg", "audio/mpeg", "TEXT", "FILE"}, - {"Parent folder", "Folder", "APP", "Payload", "archive/zip", "archive/rar", "image/png", "image/jpeg", "audio/mpeg", "TEXT", "FILE"}, - {"Parent folder", "Folder", "APP", "Payload", "archive/zip", "archive/rar", "image/png", "image/jpeg", "audio/mpeg", "TEXT", "FILE"}, - {"Parent folder", "Folder", "APP", "Payload", "archive/zip", "archive/rar", "image/png", "image/jpeg", "audio/mpeg", "TEXT", "FILE"}, - {"Parent folder", "Folder", "APP", "Payload", "archive/zip", "archive/rar", "image/png", "image/jpeg", "audio/mpeg", "TEXT", "FILE"}, - {"Parent folder", "Folder", "APP", "Payload", "archive/zip", "archive/rar", "image/png", "image/jpeg", "audio/mpeg", "TEXT", "FILE"}, - {"Parent folder", "Folder", "APP", "Payload", "archive/zip", "archive/rar", "image/png", "image/jpeg", "audio/mpeg", "TEXT", "FILE"}, - {"Parent folder", "Folder", "APP", "Payload", "archive/zip", "archive/rar", "image/png", "image/jpeg", "audio/mpeg", "TEXT", "FILE"}, - {"Parent folder", "Folder", "APP", "Payload", "archive/zip", "archive/rar", "image/png", "image/jpeg", "audio/mpeg", "TEXT", "FILE"}, - {"Parent folder", "Folder", "APP", "Payload", "archive/zip", "archive/rar", "image/png", "image/jpeg", "audio/mpeg", "TEXT", "FILE"}, - {"Parent folder", "Folder", "APP", "Payload", "archive/zip", "archive/rar", "image/png", "image/jpeg", "audio/mpeg", "TEXT", "FILE"}, - {"Parent folder", "Folder", "APP", "Payload", "archive/zip", "archive/rar", "image/png", "image/jpeg", "audio/mpeg", "TEXT", "FILE"} -}; - -const char lang_options[][9][100] = -{ - {"アクション", "プロパティ", "名前を変更", "フォルダを作る", "コピー", "削除", "切り取る", "Paste", "キャンセル"}, - {"Actions", "Properties", "Rename", "New folder", "Copy", "Delete", "Cut", "Paste", "CANCEL"}, - {"Actions", "Propriétés", "Renommer", "Nouveau dossier", "Copier", "Supprimer", "Couper", "Paste", "ANNULER"}, - {"Aktionen", "Eigenschaften", "Umbenennen", "Neuer Ordner", "Kopieren", "Löschen", "Ausschneiden", "Paste", "ABBRECHEN"}, - {"Azioni", "Proprietà", "Rinomina", "Nuova cartella", "Copia", "Elimina", "Taglia", "Paste", "ANNULLA"}, - {"Acciones", "Propiedades", "Renombrar", "Nueva carpeta", "Copiar", "Eliminar", "Cortar", "Paste", "CANCELAR"}, - {"操作", "属性", "重命名", "新建文件夹", "复制", "删除", "剪切", "Paste", "取消"}, - {"작업", "속성", "이름 바꾸기", "새 폴더", "복사", "삭제", "잘라내기", "Paste", "취소"}, - {"Actie's", "Instellingen", "Naam wijzigen", "Nieuwe map", "Kopiëren", "Verwijderen", "Knippen", "Paste", "ANNULEREN"}, - {"Ações", "Propriedades", "Renomear", "Nova pasta", "Copiar", "Eliminar", "Recortar", "Paste", "CANCELAR"}, - {"Действия", "Свойства", "Переименовать", "Новая папка", "Копировать", "Удалить", "Вырезать", "Paste", "Отменить"}, - {"動作", "屬性", "重新命名", "新資料夾", "複製", "刪除", "剪下", "Paste", "取消"} -}; - -const char lang_properties[][7][100] = -{ - {"プロパティ", "詳細", "名前", "親ディレクトリ", "タイプ", "Size", "OK"}, - {"Properties", "INFO", "Name", "Parent", "Type", "Size", "OK"}, - {"Propriétés", "INFO", "Nom", "Parent", "Type", "Size", "OK"}, - {"Eigenschaften", "INFO", "Name", "Übergeordnet", "Typ", "Size", "OK"}, - {"Proprietà", "INFO", "Nome", "Cartella", "Tipo", "Size", "OK"}, - {"Propiedades", "Información", "Nombre", "Padre", "Tipo", "Size", "Confirmar"}, - {"属性", "信息", "名称", "父目录", "类型", "Size", "OK"}, - {"속성", "정보", "이름", "부모", "타입", "Size", "확인"}, - {"Instellingen", "INFO", "Naam", "Omhoog", "Soort", "Size", "Oke"}, - {"Propriedades", "INFO", "Nome", "Local", "Tipo", "Size", "OK"}, - {"Свойства", "ИНФО", "Название", "Вверх", "Тип", "Size", "OK"}, - {"屬性", "資訊", "名字", "根目錄", "型態", "Size", "OK"} -}; - -const char lang_deletion[][5][100] = -{ - {"削除を確認", "削除されたファイルは引き戻せません", "削除しますか", "いいえ", "はい"}, - {"Confirm deletion", "This action cannot be undone.", "Do you want to continue", "NO", "YES"}, - {"Confirmer la supprimation", "Cette action est irréversible.", "Voulez-vous continuer", "NON", "OUI"}, - {"Löschen bestätigen", "Diese Aktion kann nicht rückgängig gemacht werden.", "Fortsetzen?", "NEIN", "JA"}, - {"Conferma eliminazione", "Questa azione non può essere ripristinata.", "Vuoi continuare", "NO", "SI"}, - {"Confirmar borrado", "Esta acción no puede ser revertida", "¿Desea continuar?", "NO", "SÍ"}, - {"确认删除", "该操作结果不可逆", "是否确认继续?", "取消", "确认"}, - {"삭제 확인", "삭제한 파일은 되돌릴 수 없습니다.", "계속 할까요?", "아니요", "예"}, - {"Bevestig Verwijdering", "Deze actie kan niet ongedaan worden gemaakt.", "Weet u het zeker?", "NEE(N)", "JA"}, - {"Eliminar arquivo", "Esta ação não pode ser anulada.", "Deseja continuar?", "NÃO", "SIM"}, - {"Подтвердите удаление", "Вы не сможете восстановить удаленные файлы.", "Хотите продолжить", "НЕТ", "ДА"}, - {"確認刪除", "這個動作無法復原", "你希望繼續嗎", "否", "是"} -}; - -const char lang_settings[][9][100] = -{ - {"一般的", "システムファイル防御", "大切なファイルの削除を防止します", "カスタムテーマ", "現在のテーマ:", "Recycle Bin", "Enable 3D-Shell's recyle bin."}, - {"General", "Display filename in Gallery", "Toggles filename display in Gallery.", "Custom themes", "Current:", "Recycle Bin", "Enable 3D-Shell's recyle bin.", "Show hidden files", "Displays hidden files in file browser."}, - {"Général", "Protection de fichier système", "Empêche la supprimation des fichiers systèmes.", "Thèmes personnalisés", "Actuel:", "Recycle Bin", "Enable 3D-Shell's recyle bin."}, - {"Allgemein", "Systemdateien-Schutz", "Verhindert das Löschen von Systemdateien.", "Eigene Themen", "Aktuell:", "Recycle Bin", "Enable 3D-Shell's recyle bin."}, - {"Generale", "Protezione file di sistema", "Previene l'eliminazione di file di sistema.", "Temi custom", "Attuale:", "Recycle Bin", "Enable 3D-Shell's recyle bin."}, - {"General", "Protección de archivos del sistema", "Previene la eliminación de archivos del sistema", "Temas personalizados", "Actual:", "Recycle Bin", "Enable 3D-Shell's recyle bin."}, - {"通用", "系统文件保护", "防止系统文件被删除。", "自定义主题", "当前:", "Recycle Bin", "Enable 3D-Shell's recyle bin."}, - {"일반", "시스템 파일 보호", "시스템 파일의 삭제를 방지합니다.", "커스텀 테마", "현재:", "Recycle Bin", "Enable 3D-Shell's recyle bin."}, - {"Algemeen", "System file protection", "Voorkomt het verwijderen van systeembestanden.", "Aangepaste Thema's", "Momenteel:", "Recycle Bin", "Enable 3D-Shell's recyle bin."}, - {"Geral", "Proteção de arquivos do sistema", "Prevenir eliminação de arquivos do sistema", "Customização", "Tema atual:", "Recycle Bin", "Enable 3D-Shell's recyle bin."}, - {"Общее", "Защита файловой системы", "Предотвращать удаление системных файлов.", "Темы", "Текущая:", "Recycle Bin", "Enable 3D-Shell's recyle bin."}, - {"一般", "系統資料保護", "保護系統資料不被刪除", "自製主題", "當前:", "Recycle Bin", "Enable 3D-Shell's recyle bin."} -}; - -const char lang_themes[][1][100] = -{ - {"テーマを選択してください"}, - {"Select a theme"}, - {"Choisissez un thème"}, - {"Thema auswählen"}, - {"Seleziona un tema"}, - {"Selecciona un tema"}, - {"选择一个主题"}, - {"테마 선택"}, - {"Selecteer een thema"}, - {"Selecione um tema"}, - {"Выберите тему"}, - {"請選擇一個主題"} -}; - -const char lang_ftp[][5][100] = -{ - {"FTP起動しました", "この状態ではブラウザが使えません", "FTPアイコンをタッチすると、FTP接続が終了します", "Failed to initialize FTP.", "WiFi not enabled."}, - {"FTP initialized", "File browser cannot be accesed at this time.", "Tap the FTP icon to disable the FTP connection.", "Failed to initialize FTP.", "WiFi not enabled."}, - {"FTP initialisé", "Le navigateur de fichiers ne peut pas être accédé en ce moment.", "Appuyer sur l'icône FTP pour désactiver la connection de FTP.", "Failed to initialize FTP.", "WiFi not enabled."}, - {"FTP gestartet", "Auf den Dateibrowser kann zur Zeit nicht zugegriffen werden.", "Wähle das FTP-Icon aus, um die FTP-Verbindung zu deaktivieren.", "Failed to initialize FTP.", "WiFi not enabled."}, - {"Avviato FTP", "Non è possibile accedere al momento al browser di file.", "Tocca l'icona FTP per disattivare la connessione FTP.", "Failed to initialize FTP.", "WiFi not enabled."}, - {"FTP inicializado", "El explorador de archivos no puede ser utilizado actualmente.", "Toca el ícono de FTP para desactivar la conexión FTP.", "Failed to initialize FTP.", "WiFi not enabled."}, - {"FTP 初始化完毕", "文件浏览器当前不可用。", "点击FTP图标以禁用FTP连接。", "Failed to initialize FTP.", "WiFi not enabled."}, - {"FTP 초기화 완료", "현재는 파일 브라우저를 이용할 수 없습니다.", "FTP 아이콘을 탭해 FTP를 비활성화", "Failed to initialize FTP.", "WiFi not enabled."}, - {"FTP gestart", "Bestandsbeheer kan niet worden geopend op dit moment.", "Klik op de FTP icoon om de FTP service uit te zetten.", "Failed to initialize FTP.", "WiFi not enabled."}, - {"FPT inicializado", "O explorador de arquivos não poderá ser utilizado.", "Toque no ícone FTP para terminar a conexão.", "Failed to initialize FTP.", "WiFi not enabled."}, - {"FTP запущен", "В настоящий момент файловый браузер недоступен.", "Нажмите на иконку FTP для прерывания FTP соединения.", "Failed to initialize FTP.", "WiFi not enabled."}, - {"FTP初始化中", "目前無法取得檔案管理員的資料", "請點選FTP的按鈕來結束與FTP的連線", "Failed to initialize FTP.", "WiFi not enabled."} -}; - -const char lang_update[][4][100] = -{ - {"Checking for updates...", "Install failed", "Install success", "Exiting.."}, - {"Checking for updates...", "Install failed", "Install success", "Exiting.."}, - {"Vérification des mises à jour...", "Installation échouée", "Installation réussie", "Fermeture..."}, - {"Suche nach Updates...", "Installation fehlgeschlagen", "Installation erfolgreich", "Beenden..."}, - {"Checking for updates...", "Install failed", "Install success", "Exiting.."}, - {"Checking for updates...", "Install failed", "Install success", "Exiting.."}, - {"Checking for updates...", "Install failed", "Install success", "Exiting.."}, - {"Checking for updates...", "Install failed", "Install success", "Exiting.."}, - {"Checking for updates...", "Install failed", "Install success", "Exiting.."}, - {"Checking for updates...", "Install failed", "Install success", "Exiting.."}, - {"Проверка обновлений...", "Установка не удалась", "Успешная установка", "Выходим..."}, - {"Checking for updates...", "Install failed", "Install success", "Exiting.."} -}; \ No newline at end of file diff --git a/source/main.c b/source/main.c index df8da7c..3ec3d34 100644 --- a/source/main.c +++ b/source/main.c @@ -1,14 +1,39 @@ +#include #include <3ds.h> #include "common.h" +#include "config.h" #include "fs.h" -#include "language.h" -#include "menus/menu_main.h" -#include "menu_update.h" -#include "pp2d/pp2d.h" +#include "menu_main.h" +#include "C2D_helper.h" #include "textures.h" #include "utils.h" +static void Term_Services(void) +{ + Textures_Free(); + + if (Utils_IsN3DS()) + osSetSpeedupEnable(false); + + FS_CloseArchive(archive); + + C2D_TextBufDelete(sizeBuf); + C2D_TextBufDelete(dynamicBuf); + C2D_TextBufDelete(staticBuf); + + C2D_Fini(); + C3D_Fini(); + gfxExit(); + romfsExit(); + ptmuExit(); + ndspExit(); + mcuHwcExit(); + cfguExit(); + amExit(); + acExit(); +} + static void Init_Services(void) { acInit(); @@ -19,48 +44,37 @@ static void Init_Services(void) ndspInit(); ndspSetOutputMode(NDSP_OUTPUT_STEREO); ptmuInit(); - pp2d_init(); romfsInit(); - - FS_OpenArchive(&archive, ARCHIVE_SDMC); + gfxInitDefault(); + C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); + C2D_Init(C2D_DEFAULT_MAX_OBJECTS); + C2D_Prepare(); if (Utils_IsN3DS()) osSetSpeedupEnable(true); APT_SetAppCpuTimeLimit(30); - Utils_MakeDirectories(); - Utils_LoadConfig(); - Utils_GetLastDirectory(); + staticBuf = C2D_TextBufNew(4096); + dynamicBuf = C2D_TextBufNew(4096); + sizeBuf = C2D_TextBufNew(4096); - Load_Textures(); + RENDER_TOP = C2D_CreateScreenTarget(GFX_TOP, GFX_LEFT); + RENDER_BOTTOM = C2D_CreateScreenTarget(GFX_BOTTOM, GFX_LEFT); - language = 1; //Utils_GetLanguage(); - - BROWSE_STATE = STATE_SD; - MENU_DEFAULT_STATE = MENU_STATE_HOME; -} - -static void Term_Services(void) -{ - if (Utils_IsN3DS()) - osSetSpeedupEnable(false); + FS_OpenArchive(&archive, ARCHIVE_SDMC); - FS_CloseArchive(archive); + FS_RecursiveMakeDir(archive, "/3ds/3DShell/"); - romfsExit(); - pp2d_exit(); - ptmuExit(); - ndspExit(); - mcuHwcExit(); - cfguExit(); - amExit(); - acExit(); + Textures_Load(); + Config_Load(); + Config_GetLastDirectory(); } int main(int argc, char *argv[]) { Init_Services(); + //Config_Load(); if (setjmp(exitJmp)) { @@ -68,6 +82,9 @@ int main(int argc, char *argv[]) return 0; } + MENU_DEFAULT_STATE = MENU_STATE_HOME; Menu_Main(); Term_Services(); + + return 0; } diff --git a/source/menus/menu_delete.c b/source/menus/menu_delete.c deleted file mode 100644 index 42e4052..0000000 --- a/source/menus/menu_delete.c +++ /dev/null @@ -1,96 +0,0 @@ -#include "common.h" -#include "dir_list.h" -#include "fs.h" -#include "language.h" -#include "menu_delete.h" -#include "pp2d.h" -#include "textures.h" -#include "theme.h" -#include "touch.h" -#include "utils.h" - -struct colour BottomScreen_colour; -struct colour Options_title_text_colour; -struct colour Settings_title_text_colour; - -static Result FileOptions_Delete(void) -{ - File * file = Dirlist_GetFileIndex(position); - - if (file == NULL) - return -1; - - if (strncmp(file->name, "..", 2) == 0) - return -2; - - /*if ((recycleBin) && !(strstr(cwd, "/3ds/3DShell/bin/") != NULL)) - { - isMovingToBin = true; - char * buf = (char *)malloc(1024); - copy(COPY_DELETE_ON_FINISH); - strcpy(buf, cwd); - memset(cwd, 0, sizeof(cwd)); - sprintf(cwd, "/3ds/3DShell/bin/"); - paste(); - strcpy(cwd, buf); - free(buf); - return 0; - }*/ - - char path[1024]; // File path - - // Puzzle path - strcpy(path, cwd); - strcpy(path + strlen(path), file->name); - - Result ret = 0; - - if (file->isDir) // Delete folder - { - if (R_FAILED(ret = FS_RmdirRecursive(archive, path))) - return ret; - } - else // Delete file - { - if (R_FAILED(ret = FS_Remove(archive, path))) - return ret; - } - - return 0; -} - -void Menu_DisplayDeleteDialog(void) -{ - pp2d_draw_rectangle(0, 0, 320, 240, RGBA8(BottomScreen_colour.r, BottomScreen_colour.g, BottomScreen_colour.b, 255)); - - pp2d_draw_texture(TEXTURE_DIALOG, 20, 55); - - pp2d_draw_text(27, 72, 0.45f, 0.45f, RGBA8(Settings_title_text_colour.r, Settings_title_text_colour.g, Settings_title_text_colour.b, 255), lang_deletion[language][0]); - - pp2d_draw_text(206, 159, 0.45f, 0.45f, RGBA8(Settings_title_text_colour.r, Settings_title_text_colour.g, Settings_title_text_colour.b, 255), lang_deletion[language][3]); - pp2d_draw_text(255, 159, 0.45f, 0.45f, RGBA8(Settings_title_text_colour.r, Settings_title_text_colour.g, Settings_title_text_colour.b, 255), lang_deletion[language][4]); - - pp2d_draw_text(((320 - pp2d_get_text_width(lang_deletion[language][1], 0.45f, 0.45f)) / 2), 100, 0.45f, 0.45f, RGBA8(Options_title_text_colour.r, Options_title_text_colour.g, Options_title_text_colour.b, 255), lang_deletion[language][1]); - pp2d_draw_text(((320 - pp2d_get_text_width(lang_deletion[language][2], 0.45f, 0.45f)) / 2), 115, 0.45f, 0.45f, RGBA8(Options_title_text_colour.r, Options_title_text_colour.g, Options_title_text_colour.b, 255), lang_deletion[language][2]); -} - -void Menu_ControlDeleteDialog(u32 input) -{ - if ((input & KEY_A) || (touchInRect(240, 142, 320, 185))) - { - if (R_SUCCEEDED(FileOptions_Delete())) - { - Dirlist_PopulateFiles(true); - Dirlist_DisplayFiles(); - } - - wait(1); - MENU_DEFAULT_STATE = MENU_STATE_OPTIONS; - } - - else if ((input & KEY_B) || (touchInRect(136, 142, 239, 185))) - { - wait(1); - MENU_DEFAULT_STATE = MENU_STATE_OPTIONS; - } -} \ No newline at end of file diff --git a/source/menus/menu_file_options.c b/source/menus/menu_file_options.c deleted file mode 100644 index 3faa514..0000000 --- a/source/menus/menu_file_options.c +++ /dev/null @@ -1,448 +0,0 @@ -#include -#include - -#include "common.h" -#include "dir_list.h" -#include "fs.h" -#include "keyboard.h" -#include "language.h" -#include "menu_properties.h" -#include "menu_settings.h" -#include "pp2d.h" -#include "progress_bar.h" -#include "textures.h" -#include "theme.h" -#include "touch.h" -#include "utils.h" - -/* -* Copy Flags -*/ -#define COPY_FOLDER_RECURSIVE 2 -#define COPY_DELETE_ON_FINISH 1 -#define COPY_KEEP_ON_FINISH 0 -#define NOTHING_TO_COPY -1 - -struct colour Options_select_colour; -struct colour Options_text_colour; -struct colour Settings_title_text_colour; - -static int selectionX, selectionY; -static bool copy_enable, cut_enable; - -#define CAN_COPY (((copy_enable == false) && (cut_enable != true))) -#define CAN_CUT (((cut_enable == false) && (copy_enable != true))) - - -/* -* Copy Mode -* -1 : Nothing -* 0 : Copy -* 1 : Move -*/ -static int copymode = NOTHING_TO_COPY; -/* -* Copy Move Origin -*/ -static char copysource[1024]; -static char selected_file[255][255]; // 255 slots, of length 255 - -static Result FileOptions_CreateFolder(void) -{ - char * buf = (char *)malloc(256); - strcpy(buf, keyboard_3ds_get(256, "", "Enter name")); - - if (strncmp(buf, "", 1) == 0) - return -1; - - char path[500]; - strcpy(path, cwd); - strcat(path, buf); - free(buf); - - FS_RecursiveMakeDir(archive, path); - - Dirlist_PopulateFiles(true); - return 0; -} - -static Result FileOptions_RenameFile(void) -{ - Result ret = 0; - File * file = Dirlist_GetFileIndex(position); - - if (file == NULL) - return -1; - - if (strncmp(file->name, "..", 2) == 0) - return -2; - - char oldPath[500], newPath[500]; - - char * buf = (char *)malloc(256); - - strcpy(oldPath, cwd); - strcpy(newPath, cwd); - strcat(oldPath, file->name); - - strcpy(buf, keyboard_3ds_get(255, file->name, "Enter name")); - strcat(newPath, buf); - free(buf); - - if (file->isDir) - { - if (R_FAILED(ret = FS_RenameDir(archive, oldPath, newPath))) - return ret; - } - else - { - if (R_FAILED(ret = FS_RenameFile(archive, oldPath, newPath))) - return ret; - } - - Dirlist_PopulateFiles(true); - return 0; -} - -// Copy file from src to dst -int FileOptions_CopyFile(char * src, char * dst, bool displayAnim) -{ - int chunksize = (512 * 1024); // Chunk size - char * buffer = (char *)malloc(chunksize); // Reading buffer - - u64 totalwrite = 0; // Accumulated writing - u64 totalread = 0; // Accumulated reading - - int result = 0; // Result - - int in = open(src, O_RDONLY, 0777); // Open file for reading - u64 size = FS_GetFileSize(archive, src); - - // Opened file for reading - if (in >= 0) - { - if (FS_FileExists(archive, dst)) - FS_Remove(archive, dst); // Delete output file (if existing) - - int out = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0777); // Open output file for writing - - if (out >= 0) // Opened file for writing - { - u64 b_read = 0; // Read byte count - - // Copy loop (512KB at a time) - while((b_read = read(in, buffer, chunksize)) > 0) - { - totalread += b_read; // Accumulate read data - totalwrite += write(out, buffer, b_read); // Write data - - if (displayAnim) - ProgressBar_DisplayProgress(copymode == 1? "Moving" : "Copying", Utils_Basename(src), totalread, size); - } - - close(out); // Close output file - - if (totalread != totalwrite) // Insufficient copy - result = -3; - } - - else // Output open error - result = -2; - - close(in); // Close input file - } - - // Input open error - else - result = -1; - - free(buffer); // Free memory - return result; // Return result -} - -// Recursively copy file from src to dst -static Result FileOptions_CopyDir(char * src, char * dst) -{ - Handle dirHandle; - Result ret = 0; // Open working Directory - - // Opened directory - if (R_SUCCEEDED(ret = FSUSER_OpenDirectory(&dirHandle, archive, fsMakePath(PATH_ASCII, src)))) - { - FS_MakeDir(archive, dst); // Create output directory (is allowed to fail, we can merge folders after all) - - u32 entryCount = 0; - FS_DirectoryEntry* entries = (FS_DirectoryEntry*) calloc(MAX_FILES, sizeof(FS_DirectoryEntry)); - - if (R_SUCCEEDED(ret = FSDIR_Read(dirHandle, &entryCount, MAX_FILES, entries))) - { - u8 name[255] = {'\0'}; - for (u32 i = 0; i < entryCount; i++) - { - u16_to_u8(&name[0], entries[i].name, 254); - - if (strlen(name) > 0) - { - // Calculate buffer size - int insize = strlen(src) + strlen(name) + 2; - int outsize = strlen(dst) + strlen(name) + 2; - - // Allocate buffer - char * inbuffer = (char *)malloc(insize); - char * outbuffer = (char *)malloc(outsize); - - // Puzzle input path - strcpy(inbuffer, src); - inbuffer[strlen(inbuffer) + 1] = 0; - inbuffer[strlen(inbuffer)] = '/'; - strcpy(inbuffer + strlen(inbuffer), name); - - // Puzzle output path - strcpy(outbuffer, dst); - outbuffer[strlen(outbuffer) + 1] = 0; - outbuffer[strlen(outbuffer)] = '/'; - strcpy(outbuffer + strlen(outbuffer), name); - - if (entries[i].attributes & FS_ATTRIBUTE_DIRECTORY) // Another folder - FileOptions_CopyDir(inbuffer, outbuffer); // Copy folder (via recursion) - - else - { - if (R_FAILED(ret = ret = FileOptions_CopyFile(inbuffer, outbuffer, false))) // Copy file - return ret; - } - - // Free buffer - free(inbuffer); - free(outbuffer); - } - - ProgressBar_DisplayProgress(copymode == 1? "Moving" : "Copying", Utils_Basename(name), i, entryCount); - } - } - else - return ret; - - free(entries); - - if (R_FAILED(ret = FSDIR_Close(dirHandle))) // Close directory - return ret; - } - else - return ret; - - return 0; -} - -static void FileOptions_Copy(int flag) -{ - File * file = Dirlist_GetFileIndex(position); - - if (file == NULL) - return; - - // Copy file source - strcpy(copysource, cwd); - strcpy(copysource + strlen(copysource), file->name); - - if ((file->isDir) && (strncmp(file->name, "..", 2) != 0)) // If directory, add recursive folder flag - flag |= COPY_FOLDER_RECURSIVE; - - copymode = flag; // Set copy flags -} - -// Paste file or folder -static Result FileOptions_Paste(void) -{ - if (copymode == NOTHING_TO_COPY) // No copy source - return -1; - - // Source and target folder are identical - char * lastslash = NULL; - int i = 0; - - for(; i < strlen(copysource); i++) - if (copysource[i] == '/') - lastslash = copysource + i; - - char backup = lastslash[1]; - lastslash[1] = 0; - int identical = strcmp(copysource, cwd) == 0; - lastslash[1] = backup; - - if (identical) - return -2; - - char * filename = lastslash + 1; // Source filename - - int requiredlength = strlen(cwd) + strlen(filename) + 1; // Required target path buffer size - char * copytarget = (char *)malloc(requiredlength); // Allocate target path buffer - - // Puzzle target path - strcpy(copytarget, cwd); - strcpy(copytarget + strlen(copytarget), filename); - - Result ret = -3; // Return result - - // Recursive folder copy - if ((copymode & COPY_FOLDER_RECURSIVE) == COPY_FOLDER_RECURSIVE) - { - // Check files in current folder - File * node = files; for(; node != NULL; node = node->next) - { - if ((strcmp(filename, node->name) == 0) && (!node->isDir)) // Found a file matching the name (folder = ok, file = not) - return -4; // Error out - } - - ret = FileOptions_CopyDir(copysource, copytarget); // Copy folder recursively - - if ((R_SUCCEEDED(ret)) && (copymode & COPY_DELETE_ON_FINISH) == COPY_DELETE_ON_FINISH) - FS_RmdirRecursive(archive, copysource); // Delete dir - } - - // Simple file copy - else - { - ret = FileOptions_CopyFile(copysource, copytarget, true); // Copy file - - if ((R_SUCCEEDED(ret)) && (copymode & COPY_DELETE_ON_FINISH) == COPY_DELETE_ON_FINISH) - FS_Remove(archive, copysource); // Delete file - } - - // Paste success - if (R_SUCCEEDED(ret)) - { - memset(copysource, 0, sizeof(copysource)); // Erase cache data - copymode = NOTHING_TO_COPY; - } - - free(copytarget); // Free target path buffer - return ret; // Return result -} - -void Menu_DisplayFileOptions(void) -{ - pp2d_draw_texture(TEXTURE_OPTIONS, 37, 20); - - pp2d_draw_rectangle(37 + (selectionX * 123), 56 + (selectionY * 37), 123, 37, RGBA8(Options_select_colour.r, Options_select_colour.g, Options_select_colour.b, 255)); - - pp2d_draw_text(42, 36, 0.45f, 0.45f, RGBA8(Settings_title_text_colour.r, Settings_title_text_colour.g, Settings_title_text_colour.b, 255), lang_options[language][0]); - pp2d_draw_text(232, 196, 0.45f, 0.45f, RGBA8(Settings_title_text_colour.r, Settings_title_text_colour.g, Settings_title_text_colour.b, 255), lang_options[language][8]); - - pp2d_draw_text(47, 72, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), lang_options[language][1]); - pp2d_draw_text(47, 109, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), lang_options[language][3]); - pp2d_draw_text(47, 146, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), lang_options[language][5]); - - pp2d_draw_text(170, 72, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), lang_options[language][2]); - - if (copy_enable) - { - pp2d_draw_text(170, 109, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), lang_options[language][7]); - pp2d_draw_text(170, 146, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), lang_options[language][6]); - } - else if (cut_enable) - { - pp2d_draw_text(170, 146, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), lang_options[language][7]); - pp2d_draw_text(170, 109, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), lang_options[language][4]); - } - else - { - pp2d_draw_text(170, 109, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), lang_options[language][4]); - pp2d_draw_text(170, 146, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), lang_options[language][6]); - } -} - -void Menu_ControlFileOptions(u32 input) -{ - if ((input & KEY_TOUCH) && (touchInRect(37, 179, 282, 217))) // Cancel - { - wait(1); - copy_enable = false; - cut_enable = false; - MENU_DEFAULT_STATE = MENU_STATE_HOME; - } - - else if ((input & KEY_TOUCH) && (touchInRect(37, 56, 160, 93))) - { - selectionX = 0; - selectionY = 0; - wait(1); - - MENU_DEFAULT_STATE = MENU_STATE_PROPERTIES; - } - - else if ((input & KEY_TOUCH) && (touchInRect(37, 94, 160, 130))) - { - selectionX = 0; - selectionY = 1; - wait(1); - - FileOptions_CreateFolder(); - } - - else if ((input & KEY_TOUCH) && (touchInRect(161, 56, 284, 93))) - { - selectionX = 1; - selectionY = 0; - wait(1); - - FileOptions_RenameFile(); - } - - else if ((input & KEY_TOUCH) && (touchInRect(37, 131, 160, 167))) - { - //selectionX = 0; - //selectionY = 2; - wait(1); - - MENU_DEFAULT_STATE = MENU_STATE_DIALOG; - } - - if ((CAN_COPY) && (input & KEY_TOUCH) && (touchInRect(161, 94, 284, 130))) - { - selectionX = 1; - selectionY = 1; - wait(1); - - FileOptions_Copy(COPY_KEEP_ON_FINISH); - copy_enable = true; - } - - else if ((copy_enable) && (input & KEY_TOUCH) && (touchInRect(161, 94, 284, 130))) - { - selectionX = 0; - selectionY = 0; - wait(1); - - if (FileOptions_Paste() == 0) - { - copy_enable = false; - Dirlist_PopulateFiles(true); - Dirlist_DisplayFiles(); - } - } - - if ((CAN_CUT) && (input & KEY_TOUCH) && (touchInRect(161, 131, 284, 167))) - { - selectionX = 1; - selectionY = 2; - wait(1); - - FileOptions_Copy(COPY_DELETE_ON_FINISH); - cut_enable = true; - } - - else if ((cut_enable) && (input & KEY_TOUCH) && (touchInRect(161, 131, 284, 167))) - { - selectionX = 0; - selectionY = 0; - wait(1); - - if (FileOptions_Paste() == 0) - { - cut_enable = false; - Dirlist_PopulateFiles(true); - Dirlist_DisplayFiles(); - } - } -} \ No newline at end of file diff --git a/source/menus/menu_ftp.c b/source/menus/menu_ftp.c index 9cb2c43..8829b68 100644 --- a/source/menus/menu_ftp.c +++ b/source/menus/menu_ftp.c @@ -1,30 +1,24 @@ #include #include +#include "C2D_helper.h" #include "common.h" -#include "dir_list.h" +#include "config.h" +#include "dirbrowse.h" #include "fs.h" #include "ftp.h" -#include "language.h" #include "menu_ftp.h" #include "menu_main.h" -#include "pp2d.h" #include "status_bar.h" #include "textures.h" -#include "theme.h" #include "utils.h" -struct colour Storage_colour; -struct colour BottomScreen_colour; -struct colour BottomScreen_bar_colour; -struct colour BottomScreen_text_colour; - void Menu_DisplayFTP(void) { ftp_init(); touchPosition touch; - + char buf[25]; u32 wifiStatus = 0; @@ -34,55 +28,59 @@ void Menu_DisplayFTP(void) { ftp_loop(); - ACU_GetWifiStatus(&wifiStatus); + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + C2D_TargetClear(RENDER_BOTTOM, config_dark_theme? BLACK_BG : WHITE); + C2D_SceneBegin(RENDER_BOTTOM); + Draw_Rect(0, 0, 320, 20, config_dark_theme? STATUS_BAR_DARK : STATUS_BAR_LIGHT); // Status bar + Draw_Rect(0, 20, 320, 220, config_dark_theme? MENU_BAR_DARK : MENU_BAR_LIGHT); // Menu bar - pp2d_begin_draw(GFX_BOTTOM, GFX_LEFT); + ACU_GetWifiStatus(&wifiStatus); - pp2d_draw_rectangle(0, 0, 320, 240, RGBA8(BottomScreen_colour.r, BottomScreen_colour.g, BottomScreen_colour.b, 255)); - pp2d_draw_rectangle(0, 0, 320, 20, RGBA8(BottomScreen_bar_colour.r, BottomScreen_bar_colour.g, BottomScreen_bar_colour.b, 255)); + //Draw_Rect(0, 0, 320, 240, RGBA8(BottomScreen_colour.r, BottomScreen_colour.g, BottomScreen_colour.b, 255)); + //Draw_Rect(0, 0, 320, 20, RGBA8(BottomScreen_bar_colour.r, BottomScreen_bar_colour.g, BottomScreen_bar_colour.b, 255)); - Menu_Draw_MenuBar(); - - if (!(wifiStatus)) - { - pp2d_draw_text(((320 - pp2d_get_text_width(lang_ftp[language][3], 0.45f, 0.45f)) / 2), 40, 0.45f, 0.45f, RGBA8(BottomScreen_text_colour.r, BottomScreen_text_colour.g , BottomScreen_text_colour.b, 255), lang_ftp[language][3]); - sprintf(buf, lang_ftp[language][4]); - } - else - { - pp2d_draw_text(((320 - pp2d_get_text_width(lang_ftp[language][0], 0.45f, 0.45f)) / 2), 40, 0.45f, 0.45f, RGBA8(BottomScreen_text_colour.r, BottomScreen_text_colour.g , BottomScreen_text_colour.b, 255), lang_ftp[language][0]); - - u32 ip = gethostid(); - sprintf(buf, "IP: %lu.%lu.%lu.%lu:5000", ip & 0xFF, (ip>>8)&0xFF, (ip>>16)&0xFF, (ip>>24)&0xFF); + //Menu_Draw_MenuBar(); + + if (!(wifiStatus)) + { + Draw_Text(((320 - Draw_GetTextWidth(0.48f, "Failed to initialize FTP.")) / 2), 40, 0.48f, WHITE, "Failed to initialize FTP."); + sprintf(buf, "WiFi not enabled."); + } + else + { + Draw_Text(((320 - Draw_GetTextWidth(0.48f, "FTP initialized")) / 2), 40, 0.48f, WHITE, "FTP initialized"); + + u32 ip = gethostid(); + sprintf(buf, "IP: %lu.%lu.%lu.%lu:5000", ip & 0xFF, (ip>>8)&0xFF, (ip>>16)&0xFF, (ip>>24)&0xFF); - pp2d_draw_text(((320 - pp2d_get_text_width(buf, 0.45f, 0.45f)) / 2), 60, 0.45f, 0.45f, RGBA8(BottomScreen_text_colour.r, BottomScreen_text_colour.g , BottomScreen_text_colour.b, 255), buf); + Draw_Text(((320 - Draw_GetTextWidth(0.48f, buf)) / 2), 60, 0.48f, WHITE, buf); - if (strlen(ftp_accepted_connection) != 0) - pp2d_draw_text(((320 - pp2d_get_text_width(ftp_accepted_connection, 0.45f, 0.45f)) / 2), 80, 0.45f, 0.45f, RGBA8(BottomScreen_text_colour.r, BottomScreen_text_colour.g , BottomScreen_text_colour.b, 255), ftp_accepted_connection); + if (strlen(ftp_accepted_connection) != 0) + Draw_Text(((320 - Draw_GetTextWidth(0.48f, ftp_accepted_connection)) / 2), 80, 0.48f, WHITE, ftp_accepted_connection); - pp2d_draw_text(((320 - pp2d_get_text_width(lang_ftp[language][1], 0.45f, 0.45f)) / 2), 100, 0.45f, 0.45f, RGBA8(BottomScreen_text_colour.r, BottomScreen_text_colour.g , BottomScreen_text_colour.b, 255), lang_ftp[language][1]); + Draw_Text(((320 - Draw_GetTextWidth(0.48f, "File browser cannot be accesed at this time.")) / 2), 100, 0.48f, WHITE, "File browser cannot be accesed at this time."); - if (strlen(ftp_file_transfer) != 0) - pp2d_draw_text(((320 - pp2d_get_text_width(ftp_file_transfer, 0.45f, 0.45f)) / 2), 150, 0.45f, 0.45f, RGBA8(BottomScreen_text_colour.r, BottomScreen_text_colour.g , BottomScreen_text_colour.b, 255), ftp_file_transfer); + if (strlen(ftp_file_transfer) != 0) + Draw_Text(((320 - Draw_GetTextWidth(0.45f, ftp_file_transfer)) / 2), 150, 0.45f, WHITE, ftp_file_transfer); - if (isTransfering) - { - pp2d_draw_rectangle(50, 140, 220, 3, RGBA8(BottomScreen_bar_colour.r, BottomScreen_bar_colour.g, BottomScreen_bar_colour.b, 255)); - pp2d_draw_rectangle(pBar, 140, 40, 3, RGBA8(BottomScreen_text_colour.r, BottomScreen_text_colour.g , BottomScreen_text_colour.b, 255)); + if (isTransfering) + { + Draw_Rect(50, 140, 220, 3, config_dark_theme? STATUS_BAR_DARK : STATUS_BAR_LIGHT); + Draw_Rect(pBar, 140, 40, 3, WHITE); - // Boundary stuff - pp2d_draw_rectangle(0, 140, 50, 3, RGBA8(BottomScreen_colour.r, BottomScreen_colour.g, BottomScreen_colour.b, 255)); - pp2d_draw_rectangle(270, 140, 50, 3, RGBA8(BottomScreen_colour.r, BottomScreen_colour.g, BottomScreen_colour.b, 255)); - pBar += 4; - - if (pBar >= xlim) - pBar = 34; - } + // Boundary stuff + Draw_Rect(0, 140, 50, 3, config_dark_theme? MENU_BAR_DARK : MENU_BAR_LIGHT); + Draw_Rect(270, 140, 50, 3, config_dark_theme? MENU_BAR_DARK : MENU_BAR_LIGHT); + pBar += 4; + + if (pBar >= xlim) + pBar = 34; } + } - pp2d_draw_text(((320 - pp2d_get_text_width(lang_ftp[language][2], 0.45f, 0.45f)) / 2), 120, 0.45f, 0.45f, RGBA8(BottomScreen_text_colour.r, BottomScreen_text_colour.g , BottomScreen_text_colour.b, 255), lang_ftp[language][2]); + Draw_Text(((320 - Draw_GetTextWidth(0.48f, "Tap the FTP icon to disable the FTP connection.")) / 2), 120, 0.48f, WHITE, "Tap the FTP icon to disable the FTP connection."); - pp2d_end_draw(); + Draw_EndFrame(); hidScanInput(); u32 kDown = hidKeysDown(); @@ -95,6 +93,5 @@ void Menu_DisplayFTP(void) memset(ftp_file_transfer, 0, 50); // Empty transfer status ftp_exit(); MENU_DEFAULT_STATE = MENU_STATE_HOME; - Dirlist_PopulateFiles(true); - Dirlist_DisplayFiles(); + Dirbrowse_PopulateFiles(true); } \ No newline at end of file diff --git a/source/menus/menu_gallery.c b/source/menus/menu_gallery.c index 6a3417a..60a596e 100644 --- a/source/menus/menu_gallery.c +++ b/source/menus/menu_gallery.c @@ -1,142 +1,391 @@ -#include -#include +#include +#include <3ds.h> +#include +#include + +#include "C2D_helper.h" #include "common.h" -#include "dir_list.h" +#include "config.h" +#include "fs.h" #include "menu_gallery.h" -#include "pp2d.h" -#include "screenshot.h" -#include "textures.h" +#include "touch.h" #include "utils.h" -#define DIMENSION_DEFAULT 0 -#define DIMENSION_NINTENDO_SCREENSHOT 1 -#define DIMENSION_NINTENDO_PICTURE 2 -#define DIMENSION_3DSHELL_SCREENSHOT 3 -#define DIMENSION_OTHER 4 +#define EXTENSION_SIZE 4 + +#define PNG_SIG_SIZE 8 -#define GALLERY_BAR_Y_BOUNDARY -35 -#define GALLERY_NAME_Y_BOUNDARY -11 +static char album[512][512]; +static int count = 0, selection = 0; +C2D_Image *image; -void Gallery_DisplayImage(char * path) +// Thanks to LiquidFenrir +static u32 Gallery_GetNextPowerOf2(u32 v) // from pp2d { - File * file = Dirlist_GetFileIndex(position); + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v >= 64 ? v : 64; +} - if (strncasecmp(file->ext, "png", 3) == 0) - pp2d_load_texture_png(TEXTURE_GALLERY_IMAGE, path); - else if (strncasecmp(file->ext, "bmp", 3) == 0) - pp2d_load_texture_bmp(TEXTURE_GALLERY_IMAGE, path); - else if (strncasecmp(file->ext, "jpg", 3) == 0) - pp2d_load_texture_jpg(TEXTURE_GALLERY_IMAGE, path); - else if ((strncasecmp(file->ext, "gif", 3) == 0) || (strncasecmp(file->ext, "tga", 3) == 0)) - pp2d_load_texture_other(TEXTURE_GALLERY_IMAGE, path); +// Thanks to LiquidFenrir +static C2D_Image *Gallery_LoadImage(const char *path) +{ + u32* outBuf = NULL; + int width = 0, height = 0; - int galleryBarY = 0, nameY = 11; + char extension[EXTENSION_SIZE+1] = {0}; + strncpy(extension, &path[strlen(path)-EXTENSION_SIZE], EXTENSION_SIZE); - u64 start = osGetTime(); + if (!strncmp(extension, ".png", EXTENSION_SIZE)) + { + FILE* fh = fopen(path, "rb"); + if (!fh) + return NULL; - float width = pp2d_get_texture_width(TEXTURE_GALLERY_IMAGE); - float height = pp2d_get_texture_height(TEXTURE_GALLERY_IMAGE); + u8 sig[PNG_SIG_SIZE] = {0}; + size_t size = fread(sig, sizeof(u8), PNG_SIG_SIZE, fh); + fseek(fh, 0, SEEK_SET); - int dimensions = 0; + if (size < PNG_SIG_SIZE || png_sig_cmp(sig, 0, PNG_SIG_SIZE)) + { + fclose(fh); + return NULL; + } - while (aptMainLoop()) - { - hidScanInput(); + png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - u32 kDown = hidKeysDown(); - u32 kHeld = hidKeysHeld(); + png_infop info = png_create_info_struct(png); - pp2d_begin_draw(GFX_BOTTOM, GFX_LEFT); + if (setjmp(png_jmpbuf(png))) + { + png_destroy_read_struct(&png, &info, NULL); + fclose(fh); + return NULL; + } - pp2d_draw_rectangle(0, 0, 320, 240, RGBA8(33, 39, 43, 255)); + png_init_io(png, fh); + png_read_info(png, info); - switch (dimensions) - { - case DIMENSION_NINTENDO_SCREENSHOT: - pp2d_draw_texture_part(TEXTURE_GALLERY_IMAGE, 0, 0, 56, 272, 320, 240); - break; + width = png_get_image_width(png, info); + height = png_get_image_height(png, info); - case DIMENSION_3DSHELL_SCREENSHOT: - pp2d_draw_texture_part(TEXTURE_GALLERY_IMAGE, 0, 0, 40, 240, 320, 240); - break; - } - - pp2d_end_draw(); + png_byte color_type = png_get_color_type(png, info); + png_byte bit_depth = png_get_bit_depth(png, info); - pp2d_begin_draw(GFX_TOP, GFX_LEFT); + // Read any color_type into 8bit depth, ABGR format. + // See http://www.libpng.org/pub/png/libpng-manual.txt - pp2d_draw_rectangle(0, 0, 400, 240, RGBA8(33, 39, 43, 255)); + if (bit_depth == 16) + png_set_strip_16(png); - if ((width <= 400.0) && (height <= 240.0)) - DIMENSION_DEFAULT; - else if ((width == 432) && (height == 528)) // Nintnedo's screenshot (both screens) dimensions. - dimensions = DIMENSION_NINTENDO_SCREENSHOT; - else if ((width == 640) && (height == 480)) // Nintnedo's CAM dimensions. - dimensions = DIMENSION_NINTENDO_PICTURE; - else if ((width == 400) && (height == 480)) - dimensions = DIMENSION_3DSHELL_SCREENSHOT; - else if ((width > 400.0) && (height > 240.0)) - dimensions = DIMENSION_OTHER; + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png); - switch (dimensions) - { - case DIMENSION_DEFAULT: - pp2d_draw_texture(TEXTURE_GALLERY_IMAGE, ((400.0 - width) / 2.0), ((240.0 - height) / 2.0)); - break; + // PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth. + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_expand_gray_1_2_4_to_8(png); - case DIMENSION_NINTENDO_SCREENSHOT: - pp2d_draw_texture_part(TEXTURE_GALLERY_IMAGE, 0, 0, 16, 16, 400, 240); - break; + if (png_get_valid(png, info, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(png); - case DIMENSION_NINTENDO_PICTURE: - pp2d_draw_texture_scale(TEXTURE_GALLERY_IMAGE, 40, 0, 0.5, 0.5); - break; + // These color_type don't have an alpha channel then fill it with 0xff. + if (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_PALETTE) + png_set_filler(png, 0xFF, PNG_FILLER_AFTER); - case DIMENSION_3DSHELL_SCREENSHOT: - pp2d_draw_texture_part(TEXTURE_GALLERY_IMAGE, 0, 0, 0, 0, 400, 240); - break; + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png); - case DIMENSION_OTHER: - pp2d_draw_texture_scale(TEXTURE_GALLERY_IMAGE, 0, 0, 400.0/width, 240.0/height); - break; - } + //output ABGR + png_set_bgr(png); + png_set_swap_alpha(png); - if (galleryDisplay) - { - if (osGetTime() - start >= (1500)) - { - nameY -= 4; - galleryBarY -= 4; - } + png_read_update_info(png, info); + + png_bytep* row_pointers = malloc(height*sizeof(png_bytep)); + if (row_pointers == NULL) + { + png_destroy_read_struct(&png, &info, NULL); + return NULL; + } + + for(int y = 0; y < height; y++) + row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(png,info)); + + png_read_image(png, row_pointers); + + fclose(fh); + png_destroy_read_struct(&png, &info, NULL); + + outBuf = malloc(width*height*sizeof(u32)); + for(int j = 0; j < height; j++) + { + png_bytep row = row_pointers[j]; + for(int i = 0; i < width; i++) + { + png_bytep px = &(row[i * 4]); + memcpy(&outBuf[j*width +i], px, sizeof(u32)); + } + free(row_pointers[j]); // free the completed row, to avoid having to loop over the whole thing again + } + free(row_pointers); + } + else if (!strncmp(extension, ".jpg", EXTENSION_SIZE) || !strncmp(extension, ".joeg", EXTENSION_SIZE)) + { + FILE* fh = fopen(path, "rb"); + if (fh == NULL) + return NULL; + + fseek(fh, 0, SEEK_END); + long int jpegSize = ftell(fh); + fseek(fh, 0, SEEK_SET); + + unsigned char* jpegBuf = (unsigned char*)malloc(jpegSize); + if (jpegBuf == NULL) + { + fclose(fh); + return NULL; + } - if (galleryBarY == -35) - galleryBarY = GALLERY_BAR_Y_BOUNDARY; - if (nameY == -11) - nameY = GALLERY_NAME_Y_BOUNDARY; + fread(jpegBuf, jpegSize, sizeof(unsigned char), fh); + fclose(fh); - pp2d_draw_texture(TEXTURE_GALLERY_BAR, 0, galleryBarY); - pp2d_draw_textf(30, nameY, 0.45f, 0.45f, RGBA8(255, 255, 255, 255), "%.60s", fileName); + tjhandle handle = tjInitDecompress(); + if (handle == NULL) + { + free(jpegBuf); + return NULL; + } - if (kDown & KEY_TOUCH) + if (tjDecompressHeader(handle, jpegBuf, jpegSize, &width, &height) == -1) + { + free(jpegBuf); + tjDestroy(handle); + return NULL; + } + + outBuf = malloc(width*height*sizeof(u32)); + + if (tjDecompress2(handle, jpegBuf, jpegSize, (unsigned char*)outBuf, width, 0, height, TJPF_ABGR, TJFLAG_ACCURATEDCT) == -1) + { + free(outBuf); + free(jpegBuf); + tjDestroy(handle); + return NULL; + } + + free(jpegBuf); + tjDestroy(handle); + } + else + return NULL; + + if (outBuf) + { + C2D_Image* image = malloc(sizeof(C2D_Image)); + if (image == NULL) + { + free(outBuf); + return NULL; + } + + C3D_Tex* tex = malloc(sizeof(C3D_Tex)); + if (tex == NULL) + { + free(image); + free(outBuf); + return NULL; + } + image->tex = tex; + + u32 w_pow2 = Gallery_GetNextPowerOf2((u32)width); + u32 h_pow2 = Gallery_GetNextPowerOf2((u32)height); + + Tex3DS_SubTexture* subt3x = malloc(sizeof(Tex3DS_SubTexture)); + if (subt3x == NULL) + { + free(tex); + free(image); + free(outBuf); + return NULL; + } + subt3x->width = width; + subt3x->height = height; + subt3x->left = 0.0f; + subt3x->top = 1.0f; + subt3x->right = width/(float)w_pow2; + subt3x->bottom = 1.0-(height/(float)h_pow2); + image->subtex = subt3x; + + C3D_TexInit(image->tex, w_pow2, h_pow2, GPU_RGBA8); + + memset(image->tex->data, 0, image->tex->size); + + for(int j = 0; j < height; j++) + { + for(int i = 0; i < width; i++) + { + u32 dst = ((((j >> 3) * (w_pow2 >> 3) + (i >> 3)) << 6) + ((i & 1) | ((j & 1) << 1) | ((i & 2) << 1) | ((j & 2) << 2) | ((i & 4) << 2) | ((j & 4) << 3))) * 4; + memcpy(((u8*)(image->tex->data)) + dst, &outBuf[j*width +i], sizeof(u32)); + } + } + + free(outBuf); + + C3D_TexSetFilter(image->tex, GPU_LINEAR, GPU_LINEAR); + image->tex->border = 0xFFFFFFFF; + C3D_TexSetWrap(image->tex, GPU_CLAMP_TO_BORDER, GPU_CLAMP_TO_BORDER); + + return image; + } + + return NULL; +} + +// Thanks to LiquidFenrir +static void Gallery_FreeImage(C2D_Image *image) +{ + if (image) + { + C3D_TexDelete(image->tex); + free(image->tex); + free((Tex3DS_SubTexture *)image->subtex); + free(image); + } +} + +static Result Gallery_GetImageList(void) +{ + Handle dir; + Result ret = 0; + + if (R_SUCCEEDED(ret = FSUSER_OpenDirectory(&dir, archive, fsMakePath(PATH_ASCII, cwd)))) + { + u32 entryCount = 0; + FS_DirectoryEntry* entries = (FS_DirectoryEntry*) calloc(MAX_FILES, sizeof(FS_DirectoryEntry)); + + if (R_SUCCEEDED(ret = FSDIR_Read(dir, &entryCount, MAX_FILES, entries))) + { + qsort(entries, entryCount, sizeof(FS_DirectoryEntry), Utils_Alphasort); + u8 name[256] = {'\0'}; + + for (u32 i = 0; i < entryCount; i++) + { + Utils_U16_To_U8(&name[0], entries[i].name, 255); + int length = strlen(name); + + if ((strncasecmp(entries[i].shortExt, "png", 3) == 0) || (strncasecmp(entries[i].shortExt, "jpg", 3) == 0) + || (strncasecmp(entries[i].shortExt, "bmp", 3) == 0) || (strncasecmp(entries[i].shortExt, "gif", 3) == 0)) { - galleryBarY = 0; - nameY = 11; - start = osGetTime(); + strcpy(album[count], cwd); + strcpy(album[count] + strlen(album[count]), name); + count++; } } + } + else + { + free(entries); + return ret; + } + + free(entries); - pp2d_end_draw(); + if (R_FAILED(ret = FSDIR_Close(dir))) // Close directory + return ret; + } + else + return ret; +} - if (kDown & KEY_B) +static int Gallery_GetCurrentIndex(char *path) +{ + for(int i = 0; i < count; ++i) + { + if (!strcmp(album[i], path)) + return i; + } +} + +static void Gallery_HandleNext(bool forward) +{ + if (forward) + selection++; + else + selection--; + + Utils_SetMax(&selection, 0, (count - 1)); + Utils_SetMin(&selection, (count - 1), 0); + + Gallery_FreeImage(image); + + Gallery_GetImageList(); + selection = Gallery_GetCurrentIndex(album[selection]); + + image = Gallery_LoadImage(album[selection]); +} + +void Gallery_DisplayImage(char *path) +{ + image = Gallery_LoadImage(path); + + Gallery_GetImageList(); + selection = Gallery_GetCurrentIndex(path); + + while(aptMainLoop()) + { + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + C2D_TargetClear(RENDER_TOP, C2D_Color32(33, 39, 43, 255)); + C2D_TargetClear(RENDER_BOTTOM, C2D_Color32(33, 39, 43, 255)); + C2D_SceneBegin(RENDER_TOP); + + Draw_Image(*image, ((400 - (image->subtex->width)) / 2), (240 - (image->subtex->height)) / 2); + + hidScanInput(); + u32 kDown = hidKeysDown(); + + if ((kDown & KEY_LEFT) || (kDown & KEY_L)) { - wait(10); - break; + wait(1); + Gallery_HandleNext(false); } + else if ((kDown & KEY_RIGHT) || (kDown & KEY_R)) + { + wait(1); + Gallery_HandleNext(true); + } + + /*if (touchInfo.state == TouchEnded && touchInfo.tapType != TapNone) + { + if (tapped_inside(touchInfo, 0, 0, 120, 240)) + { + wait(1); + Gallery_HandleNext(false); + } + else if (tapped_inside(touchInfo, 1160, 0, 400, 240)) + { + wait(1); + Gallery_HandleNext(true); + } + }*/ - if (((kHeld & KEY_L) && (kDown & KEY_R)) || ((kHeld & KEY_R) && (kDown & KEY_L))) - Screenshot_Capture(); + C2D_SceneBegin(RENDER_BOTTOM); + Draw_EndFrame(); + + if (kDown & KEY_B) + break; } - pp2d_free_texture(TEXTURE_GALLERY_IMAGE); // delete image + Gallery_FreeImage(image); + memset(album, 0, sizeof(album[0][0]) * 512 * 512); + count = 0; + MENU_DEFAULT_STATE = MENU_STATE_HOME; } diff --git a/source/menus/menu_home.c b/source/menus/menu_home.c deleted file mode 100644 index 659d37a..0000000 --- a/source/menus/menu_home.c +++ /dev/null @@ -1,6 +0,0 @@ -#include <3ds.h> - -#include "../dir_list.h" -#include "menu_home.h" -#include "../pp2d/pp2d.h" -#include "textures.h" \ No newline at end of file diff --git a/source/menus/menu_main.c b/source/menus/menu_main.c index 8f4fb1f..9d5ac66 100644 --- a/source/menus/menu_main.c +++ b/source/menus/menu_main.c @@ -1,353 +1,178 @@ #include <3ds.h> +#include "C2D_helper.h" #include "common.h" -#include "dir_list.h" -#include "fs.h" -#include "keyboard.h" -#include "menu_delete.h" -#include "menu_file_options.h" +#include "config.h" +#include "dirbrowse.h" #include "menu_ftp.h" #include "menu_main.h" -#include "menu_properties.h" -#include "menu_settings.h" -#include "menu_sort.h" -#include "menu_update.h" -#include "pp2d.h" -#include "screenshot.h" #include "status_bar.h" #include "textures.h" -#include "theme.h" -#include "touch.h" +#include "utils.h" -struct colour BottomScreen_colour; -struct colour BottomScreen_bar_colour; -struct colour BottomScreen_text_colour; +#define MENUBAR_X_BOUNDARY 0 +static int menubar_x = -125; +static char multi_select_dir_old[256]; -void Menu_Draw_MenuBar(void) +static void Menu_ControlMenuBar(u32 input) { - float options_x = pp2d_get_texture_width(TEXTURE_HOME_ICON) + 5; - float settings_x = pp2d_get_texture_width(TEXTURE_HOME_ICON) + pp2d_get_texture_width(TEXTURE_OPTIONS_ICON) + 15; - float ftp_x = pp2d_get_texture_width(TEXTURE_HOME_ICON) + pp2d_get_texture_width(TEXTURE_OPTIONS_ICON) + - pp2d_get_texture_width(TEXTURE_SETTINGS_ICON) + 25; - float update_x = pp2d_get_texture_width(TEXTURE_HOME_ICON) + pp2d_get_texture_width(TEXTURE_OPTIONS_ICON) + - pp2d_get_texture_width(TEXTURE_SETTINGS_ICON) + pp2d_get_texture_width(TEXTURE_FTP_ICON) + 35; - - if (MENU_DEFAULT_STATE == MENU_STATE_HOME) - pp2d_draw_texture(TEXTURE_HOME_ICON_SELECTED, 0, -2); - else - pp2d_draw_texture(TEXTURE_HOME_ICON, 0, -2); - - if ((MENU_DEFAULT_STATE == MENU_STATE_OPTIONS) || (MENU_DEFAULT_STATE == MENU_STATE_PROPERTIES)) - pp2d_draw_texture(TEXTURE_OPTIONS_ICON_SELECTED, options_x, 0); - else - pp2d_draw_texture(TEXTURE_OPTIONS_ICON, options_x, 0); - - if ((MENU_DEFAULT_STATE == MENU_STATE_SETTINGS) || (MENU_DEFAULT_STATE == MENU_STATE_SORT) || (MENU_DEFAULT_STATE == MENU_STATE_THEMES)) - pp2d_draw_texture(TEXTURE_SETTINGS_ICON_SELECTED, settings_x, 1); - else - pp2d_draw_texture(TEXTURE_SETTINGS_ICON, settings_x, 1); - - if (MENU_DEFAULT_STATE == MENU_STATE_FTP) - pp2d_draw_texture(TEXTURE_FTP_ICON_SELECTED, ftp_x, 0); - else - pp2d_draw_texture(TEXTURE_FTP_ICON, ftp_x, 0); - - if ((MENU_DEFAULT_STATE == MENU_STATE_UPDATE) || (MENU_DEFAULT_STATE == MENU_STATE_UPDATE_2)) - pp2d_draw_texture(TEXTURE_UPDATE_ICON_SELECTED, update_x, 0); - else - pp2d_draw_texture(TEXTURE_UPDATE_ICON, update_x, 0); - - if (BROWSE_STATE == STATE_SD) - pp2d_draw_texture(TEXTURE_SD_ICON_SELECTED, (320 - pp2d_get_texture_width(TEXTURE_SD_ICON_SELECTED)) - 55, 0); - else - pp2d_draw_texture(TEXTURE_SD_ICON, (320 - pp2d_get_texture_width(TEXTURE_SD_ICON)) - 55, 0); - - if (BROWSE_STATE == STATE_NAND) - pp2d_draw_texture(TEXTURE_NAND_ICON_SELECTED, (320 - pp2d_get_texture_width(TEXTURE_NAND_ICON_SELECTED)) - 30, 0); - else - pp2d_draw_texture(TEXTURE_NAND_ICON, (320 - pp2d_get_texture_width(TEXTURE_NAND_ICON)) - 30, 0); + if (input & KEY_A) + MENU_DEFAULT_STATE = MENU_STATE_SETTINGS; - pp2d_draw_texture(TEXTURE_SEARCH_ICON, (320 - pp2d_get_texture_width(TEXTURE_SEARCH_ICON)), -2); + if ((input & KEY_SELECT) || (input & KEY_B)) + MENU_DEFAULT_STATE = MENU_STATE_HOME; } -static void Menu_Main_Controls(void) +static void Menu_HandleMultiSelect(void) { - u32 kDown = hidKeysDown(); - u32 kHeld = hidKeysHeld(); - - if (((kHeld & KEY_L) && (kDown & KEY_R)) || ((kHeld & KEY_R) && (kDown & KEY_L))) - Screenshot_Capture(); - - if ((kDown & KEY_TOUCH) && (touchInRect((320 - pp2d_get_texture_width(TEXTURE_SD_ICON)) - 55, 0, - ((320 - pp2d_get_texture_width(TEXTURE_SD_ICON)) - 55) + 20, 20))) // SD - { - wait(1); - FS_Write(archive, "/3ds/3DShell/lastdir.txt", START_PATH); - strcpy(cwd, START_PATH); - - BROWSE_STATE = STATE_SD; + // multi_select_dir can only hold one dir + strcpy(multi_select_dir_old, cwd); + //if (strcmp(multi_select_dir_old, multi_select_dir) != 0) + // FileOptions_ResetClipboard(); + + char path[256]; + File *file = Dirbrowse_GetFileIndex(position); + strcpy(path, cwd); + strcpy(path + strlen(path), file->name); + strcpy(multi_select_dir, cwd); - FS_CloseArchive(archive); - FS_OpenArchive(&archive, ARCHIVE_SDMC); - - Dirlist_PopulateFiles(true); - Dirlist_DisplayFiles(); + if (!multi_select[position]) + { + multi_select[position] = true; + multi_select_indices[position] = multi_select_index; // Store the index in the position + Utils_AppendArr(multi_select_paths[multi_select_index], path, multi_select_index); + multi_select_index += 1; } - else if ((kDown & KEY_TOUCH) && (touchInRect((320 - pp2d_get_texture_width(TEXTURE_NAND_ICON)) - 30, 0, - ((320 - pp2d_get_texture_width(TEXTURE_NAND_ICON)) - 30) + 20, 20))) // CTR NAND + else { - wait(1); - strcpy(cwd, START_PATH); - - BROWSE_STATE = STATE_NAND; + multi_select[position] = false; + strcpy(multi_select_paths[multi_select_indices[position]], ""); + multi_select_indices[position] = -1; + } - FS_CloseArchive(archive); - FS_OpenArchive(&archive, ARCHIVE_NAND_CTR_FS); + Utils_SetMax(&multi_select_index, 0, 50); + Utils_SetMin(&multi_select_index, 50, 0); +} - Dirlist_PopulateFiles(true); - Dirlist_DisplayFiles(); - } +static void Menu_ControlHome(u32 input) +{ + if (input & KEY_START) + longjmp(exitJmp, 1); - if ((kDown & KEY_TOUCH) && (touchInRect((320 - pp2d_get_texture_width(TEXTURE_SEARCH_ICON)), 0, 320, 20))) + if (input & KEY_SELECT) { - char *path = (char *)malloc(256); - strcpy(path, keyboard_3ds_get(256, "/", "Enter path")); - - if (FS_DirExists(archive, path)) + if (MENU_DEFAULT_STATE == MENU_STATE_MENUBAR) + MENU_DEFAULT_STATE = MENU_STATE_HOME; + else { - strcpy(cwd, path); - Dirlist_PopulateFiles(true); - Dirlist_DisplayFiles(); + menubar_x = -125; + MENU_DEFAULT_STATE = MENU_STATE_MENUBAR; } - else - Dirlist_DisplayFiles(); - - free(path); } - if ((kDown & KEY_TOUCH) && (touchInRect(0, 0, 22, 20))) - { - wait(1); - MENU_DEFAULT_STATE = MENU_STATE_HOME; - } - else if ((kDown & KEY_TOUCH) && (touchInRect(23, 0, 48, 20))) - { - wait(1); - MENU_DEFAULT_STATE = MENU_STATE_OPTIONS; - } - else if ((kDown & KEY_TOUCH) && (touchInRect(49, 0, 74, 20))) - { - wait(1); - MENU_DEFAULT_STATE = MENU_STATE_SETTINGS; - } - else if (((kDown & KEY_TOUCH) && (touchInRect(75, 0, 100, 20))) || (kDown & KEY_SELECT)) + if (input & KEY_SELECT) { wait(1); MENU_DEFAULT_STATE = MENU_STATE_FTP; Menu_DisplayFTP(); } - else if ((kDown & KEY_TOUCH) && (touchInRect(101, 0, 126, 20))) - { - wait(1); - MENU_DEFAULT_STATE = MENU_STATE_UPDATE; - } - if (MENU_DEFAULT_STATE == MENU_STATE_OPTIONS) - Menu_ControlFileOptions(kDown); - else if (MENU_DEFAULT_STATE == MENU_STATE_PROPERTIES) - Menu_ControlProperties(kDown); - else if (MENU_DEFAULT_STATE == MENU_STATE_SETTINGS) - Menu_ControlSettings(kDown); - else if (MENU_DEFAULT_STATE == MENU_STATE_SORT) - Menu_ControlSort(kDown); - else if (MENU_DEFAULT_STATE == MENU_STATE_DIALOG) - Menu_ControlDeleteDialog(kDown); - else if (MENU_DEFAULT_STATE == MENU_STATE_UPDATE) - Menu_ControlUpdate(kDown); - else if (MENU_DEFAULT_STATE == MENU_STATE_UPDATE_2) - Menu_ControlUpdate2(kDown); - else if (MENU_DEFAULT_STATE == MENU_STATE_THEMES) + if (fileCount > 0) { - if (kDown & KEY_A) - { - File * file = Dirlist_GetFileIndex(position); - - strcpy(fileName, file->name); - - if ((strncmp(fileName, "default", 7) == 0)) - { - strcpy(theme_dir, "romfs:/res/drawable"); - strcpy(colour_dir, "/3ds/3DShell/themes/default"); - - Theme_SaveConfig(theme_dir, colour_dir); + if (input & KEY_DUP) + position--; + else if (input & KEY_DDOWN) + position++; - wait(1); + Utils_SetMax(&position, 0, (fileCount - 1)); + Utils_SetMin(&position, (fileCount - 1), 0); - Theme_Load(); - Theme_Reload(); - } - else if ((strncmp(fileName, "..", 2) != 0) && (file->isDir)) - { - strcpy(theme_dir, cwd); - strcpy(colour_dir, cwd); - - strcat(theme_dir, fileName); - strcat(colour_dir, fileName); + if (input & KEY_LEFT) + position = 0; + else if (input & KEY_RIGHT) + position = fileCount - 1; - Theme_SaveConfig(theme_dir, colour_dir); - - wait(1); - - Theme_Load(); - Theme_Reload(); - } - } - if (kDown & KEY_B) + // Open options + if (input & KEY_X) { - char buf[250]; - - FILE * read = fopen("/3ds/3DShell/lastdir.txt", "r"); - fscanf(read, "%s", buf); - fclose(read); - - if (FS_DirExists(archive, buf)) // Incase a directory previously visited had been deleted, set start path to sdmc:/ to avoid errors. - strcpy(cwd, buf); + if (MENU_DEFAULT_STATE == MENU_STATE_OPTIONS) + MENU_DEFAULT_STATE = MENU_STATE_HOME; else - strcpy(cwd, START_PATH); - - wait(1); - - MENU_DEFAULT_STATE = MENU_STATE_SETTINGS; + MENU_DEFAULT_STATE = MENU_STATE_OPTIONS; } - } - - if (fileCount > 0) - { - // Position Decrement - if (kDown & KEY_DUP) - { - // Decrease Position - if (position > 0) - position--; - // Rewind Pointer - else - position = fileCount - 1; - } + if (input & KEY_Y) + Menu_HandleMultiSelect(); - // Position Increment - else if (kDown & KEY_DDOWN) - { - // Increase Position - if (position < (fileCount - 1)) - position++; - - // Rewind Pointer - else - position = 0; - } - - if (kHeld & KEY_CPAD_UP) + if (input & KEY_A) { wait(5); - - //scroll_x = 395; - //scroll_time = osGetTime(); - - if (position > 0) - position--; - - else position = fileCount - 1; + Dirbrowse_OpenFile(); } - - else if (kHeld & KEY_CPAD_DOWN) + else if ((strcmp(cwd, ROOT_PATH) != 0) && (input & KEY_B)) { wait(5); - - //scroll_x = 395; - //scroll_time = osGetTime(); - - if (position < (fileCount - 1)) - position++; - - else position = 0; - } - - else if (kDown & KEY_A) - { - if (MENU_DEFAULT_STATE != MENU_STATE_THEMES) - { - wait(1); - Dirlist_OpenFile(); // Open file/dir - } - } - - else if (kDown & KEY_Y) - { - snprintf(multi_select_dir, 255, cwd); - - if (!multi_select[position]) - multi_select[position] = true; - else - multi_select[position] = false; - } - - else if ((strcmp(cwd, ROOT_PATH) != 0) && (kDown & KEY_B)) - { - if (MENU_DEFAULT_STATE != MENU_STATE_THEMES) - { - wait(1); - Dirlist_Navigate(-1); - Dirlist_PopulateFiles(true); - Dirlist_DisplayFiles(); - } + Dirbrowse_Navigate(-1); + Dirbrowse_PopulateFiles(true); } } } void Menu_Main(void) { - Dirlist_PopulateFiles(true); + //TouchInfo touchInfo; + //Touch_Init(&touchInfo); + Dirbrowse_PopulateFiles(false); memset(multi_select, 0, sizeof(multi_select)); // Reset all multi selected items - while (aptMainLoop()) + while(aptMainLoop()) { - pp2d_begin_draw(GFX_TOP, GFX_LEFT); - pp2d_draw_texture(TEXTURE_BACKGROUND, 0, 0); - StatusBar_DisplayBar(); - Dirlist_DisplayFiles(); - pp2d_end_draw(); - - pp2d_begin_draw(GFX_BOTTOM, GFX_LEFT); - pp2d_draw_rectangle(0, 0, 320, 240, RGBA8(BottomScreen_colour.r, BottomScreen_colour.g, BottomScreen_colour.b, 255)); - pp2d_draw_rectangle(0, 0, 320, 20, RGBA8(BottomScreen_bar_colour.r, BottomScreen_bar_colour.g, BottomScreen_bar_colour.b, 255)); - - if (MENU_DEFAULT_STATE == MENU_STATE_HOME) - pp2d_draw_textf(2, 225, 0.45f, 0.45f, RGBA8(BottomScreen_text_colour.r, BottomScreen_text_colour.g , BottomScreen_text_colour.b, 255), "3DShell %d.%d.%d - %s", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO, GITVERSION); - else if (MENU_DEFAULT_STATE == MENU_STATE_OPTIONS) - Menu_DisplayFileOptions(); - else if (MENU_DEFAULT_STATE == MENU_STATE_PROPERTIES) - Menu_DisplayProperties(); - else if (MENU_DEFAULT_STATE == MENU_STATE_SETTINGS) - Menu_DisplaySettings(); - else if (MENU_DEFAULT_STATE == MENU_STATE_SORT) - Menu_DisplaySort(); - else if (MENU_DEFAULT_STATE == MENU_STATE_DIALOG) - Menu_DisplayDeleteDialog(); - else if (MENU_DEFAULT_STATE == MENU_STATE_UPDATE) - Menu_DisplayUpdate(); - else if (MENU_DEFAULT_STATE == MENU_STATE_UPDATE_2) - Menu_DisplayUpdate2(); - - Menu_Draw_MenuBar(); - pp2d_end_draw(); + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + C2D_TargetClear(RENDER_TOP, config_dark_theme? BLACK_BG : WHITE); + C2D_TargetClear(RENDER_BOTTOM, config_dark_theme? BLACK_BG : WHITE); + C2D_SceneBegin(RENDER_TOP); + + Draw_Rect(0, 0, 400, 18, config_dark_theme? STATUS_BAR_DARK : STATUS_BAR_LIGHT); // Status bar + Draw_Rect(0, 18, 400, 34, config_dark_theme? MENU_BAR_DARK : MENU_BAR_LIGHT); // Menu bar + + StatusBar_DisplayTime(); + Dirbrowse_DisplayFiles(); + + C2D_SceneBegin(RENDER_BOTTOM); + Draw_Rect(0, 0, 320, 20, config_dark_theme? STATUS_BAR_DARK : STATUS_BAR_LIGHT); // Status bar + Draw_Rect(0, 20, 320, 220, config_dark_theme? MENU_BAR_DARK : MENU_BAR_LIGHT); // Menu bar + + Draw_Image(config_dark_theme? icon_home_dark : icon_home, 0, 0); + Draw_Image(config_dark_theme? icon_options_dark : icon_options, 25, 0); + Draw_Image(config_dark_theme? icon_settings_dark : icon_settings, 50, 0); + Draw_Image(config_dark_theme? icon_ftp_dark : icon_ftp, 75, 0); + + Draw_Image(config_dark_theme? icon_sd_dark : icon_sd, 250, 0); + Draw_Image(config_dark_theme? icon_secure_dark : icon_secure, 275, 0); + Draw_Image(icon_search, 300, 0); + + /*else if (MENU_DEFAULT_STATE == MENU_STATE_OPTIONS) + Menu_DisplayFileOptions(); + else if (MENU_DEFAULT_STATE == MENU_STATE_PROPERTIES) + Menu_DisplayProperties(); + else if (MENU_DEFAULT_STATE == MENU_STATE_SETTINGS) + Menu_DisplaySettings(); + else if (MENU_DEFAULT_STATE == MENU_STATE_SORT) + Menu_DisplaySort(); + else if (MENU_DEFAULT_STATE == MENU_STATE_DIALOG) + Menu_DisplayDeleteDialog();*/ + + Draw_EndFrame(); hidScanInput(); - Menu_Main_Controls(); - + //Touch_Process(&touchInfo); u32 kDown = hidKeysDown(); - if (kDown & KEY_START) - break; + + if (MENU_DEFAULT_STATE == MENU_STATE_HOME) + { + Menu_ControlHome(kDown); + //Menu_TouchHome(touchInfo); + } } } \ No newline at end of file diff --git a/source/menus/menu_music.c b/source/menus/menu_music.c index ba2157d..393e558 100644 --- a/source/menus/menu_music.c +++ b/source/menus/menu_music.c @@ -8,10 +8,9 @@ #include "wav.h" #include "common.h" -#include "dir_list.h" +#include "dirbrowse.h" #include "fs.h" #include "menu_music.h" -#include "pp2d.h" #include "screenshot.h" #include "status_bar.h" #include "textures.h" @@ -20,29 +19,17 @@ static volatile bool stop = true; -/** - * Stops current playback. Playback thread should exit as a result. - */ static void Music_StopPlayback(void) { stop = true; } -/** - * Returns whether music is playing or paused. - */ bool Music_IsPlaying(void) { return !stop; } -/** - * Obtains audio file type. Lifted from ctrmus with permission. - * - * \param file File location. - * \return file_types enum or 0 on unsupported file or error. - */ -enum file_types Music_GetMusicFileType(const char * file) +enum file_types Music_GetMusicFileType(const char *file) { Handle handle; @@ -101,19 +88,12 @@ enum file_types Music_GetMusicFileType(const char * file) return file_type; } -/** - * Should only be called from a new thread only, and have only one playback - * thread at time. This function has not been written for more than one - * playback thread in mind. - * - * \param pathIn File location. - */ -static void Music_PlayFile(void * pathIn) +static void Music_PlayFile(void *pathIn) { struct decoder_fn decoder; - const char * file = pathIn; - s16 * buffer1 = NULL; - s16 * buffer2 = NULL; + const char *file = pathIn; + s16 *buffer1 = NULL; + s16 *buffer2 = NULL; ndspWaveBuf waveBuf[2]; bool lastbuf = false; Result ret = -1; @@ -149,8 +129,8 @@ static void Music_PlayFile(void * pathIn) if ((*decoder.channels)() > 2 || (*decoder.channels)() < 1) goto out; - buffer1 = linearAlloc(decoder.buffSize * sizeof(s16)); - buffer2 = linearAlloc(decoder.buffSize * sizeof(s16)); + buffer1 = linearAlloc(decoder.buffSize *sizeof(s16)); + buffer2 = linearAlloc(decoder.buffSize *sizeof(s16)); ndspChnReset(SFX); ndspChnWaveBufClear(SFX); @@ -169,14 +149,14 @@ static void Music_PlayFile(void * pathIn) ndspChnWaveBufAdd(SFX, &waveBuf[1]); /** - * There may be a chance that the music has not started by the time we get - * to the while loop. So we ensure that music has started here. + *There may be a chance that the music has not started by the time we get + *to the while loop. So we ensure that music has started here. */ while(ndspChnIsPlaying(SFX) == false); while(stop == false) { - svcSleepThread(100 * 1000); + svcSleepThread(100 *1000); /* When the last buffer has finished playing, break. */ if ((lastbuf == true) && (waveBuf[0].status == NDSP_WBUF_DONE) && (waveBuf[1].status == NDSP_WBUF_DONE)) @@ -215,9 +195,9 @@ static void Music_PlayFile(void * pathIn) ndspChnWaveBufAdd(SFX, &waveBuf[1]); } - if (R_FAILED(DSP_FlushDataCache(buffer1, decoder.buffSize * sizeof(s16)))) + if (R_FAILED(DSP_FlushDataCache(buffer1, decoder.buffSize *sizeof(s16)))) return; - if (R_FAILED(DSP_FlushDataCache(buffer2, decoder.buffSize * sizeof(s16)))) + if (R_FAILED(DSP_FlushDataCache(buffer2, decoder.buffSize *sizeof(s16)))) return; } @@ -232,12 +212,7 @@ static void Music_PlayFile(void * pathIn) return; } -/** - * Play an audio file. - * - * \param path File path. - */ -void Music_Player(char * path) +void Music_Player(char *path) { s32 prio; static Thread thread = NULL; @@ -246,9 +221,9 @@ void Music_Player(char * path) stop = false; svcGetThreadPriority(&prio, CUR_THREAD_HANDLE); - thread = threadCreate(Music_PlayFile, path, 32 * 1024, prio - 1, -2, false); + thread = threadCreate(Music_PlayFile, path, 32 *1024, prio - 1, -2, false); - File * file = Dirlist_GetFileIndex(position); + File *file = Dirbrowse_GetFileIndex(position); bool isMP3 = (strncasecmp(file->ext, "mp3", 3) == 0); while (Music_IsPlaying()) @@ -258,43 +233,43 @@ void Music_Player(char * path) u32 kDown = hidKeysDown(); u32 kHeld = hidKeysHeld(); - if ((kDown & KEY_A) || ((touchInRect(114, 76, 204, 164)) && (kDown & KEY_TOUCH))) + if ((kDown & KEY_A) || ((TouchInRect(114, 76, 204, 164)) && (kDown & KEY_TOUCH))) Audio_TogglePlayback(SFX); - pp2d_begin_draw(GFX_BOTTOM, GFX_LEFT); + //pp2d_begin_draw(GFX_BOTTOM, GFX_LEFT); - pp2d_draw_texture(TEXTURE_MUSIC_BOTTOM_BG, 0, 0); + //pp2d_draw_texture(TEXTURE_MUSIC_BOTTOM_BG, 0, 0); - if (!(Audio_IsPaused(SFX))) - pp2d_draw_texture(TEXTURE_MUSIC_PAUSE, ((320.0 - pp2d_get_texture_width(TEXTURE_MUSIC_PAUSE)) / 2.0) - 2, - ((240.0 - pp2d_get_texture_height(TEXTURE_MUSIC_PAUSE)) / 2.0)); + /*if (!(Audio_IsPaused(SFX))) + //pp2d_draw_texture(TEXTURE_MUSIC_PAUSE, ((320.0 - //pp2d_get_texture_width(TEXTURE_MUSIC_PAUSE)) / 2.0) - 2, + ((240.0 - //pp2d_get_texture_height(TEXTURE_MUSIC_PAUSE)) / 2.0)); else - pp2d_draw_texture(TEXTURE_MUSIC_PLAY, ((320.0 - pp2d_get_texture_width(TEXTURE_MUSIC_PLAY)) / 2.0), - ((240.0 - pp2d_get_texture_height(TEXTURE_MUSIC_PLAY)) / 2.0)); + //pp2d_draw_texture(TEXTURE_MUSIC_PLAY, ((320.0 - //pp2d_get_texture_width(TEXTURE_MUSIC_PLAY)) / 2.0), + ((240.0 - //pp2d_get_texture_height(TEXTURE_MUSIC_PLAY)) / 2.0));*/ - pp2d_end_draw(); + //pp2d_end_draw(); - pp2d_begin_draw(GFX_TOP, GFX_LEFT); + //pp2d_begin_draw(GFX_TOP, GFX_LEFT); - pp2d_draw_texture(TEXTURE_MUSIC_TOP_BG, 0, 0); + //pp2d_draw_texture(TEXTURE_MUSIC_TOP_BG, 0, 0); - StatusBar_DisplayBar(); + StatusBar_DisplayTime(); if (isMP3) // Only print out ID3 tag info for MP3 { - pp2d_draw_textf(5, 20, 0.5f, 0.5f, RGBA8(255, 255, 255, 255), "%s", fileName); - pp2d_draw_textf(5, 36, 0.45f, 0.45f, RGBA8(255, 255, 255, 255), "%s", ID3.artist); + //pp2d_draw_textf(5, 20, 0.5f, 0.5f, RGBA8(255, 255, 255, 255), "%s", fileName); + //pp2d_draw_textf(5, 36, 0.45f, 0.45f, RGBA8(255, 255, 255, 255), "%s", ID3.artist); - pp2d_draw_textf(184, 64, 0.5f, 0.5f, RGBA8(255, 255, 255, 255), "%.30s", ID3.title); - pp2d_draw_textf(184, 84, 0.5f, 0.5f, RGBA8(255, 255, 255, 255), "%.30s", ID3.album); - pp2d_draw_textf(184, 104, 0.5f, 0.5f, RGBA8(255, 255, 255, 255), "%.30s", ID3.year); - pp2d_draw_textf(184, 124, 0.5f, 0.5f, RGBA8(255, 255, 255, 255), "%.30s", ID3.genre); + //pp2d_draw_textf(184, 64, 0.5f, 0.5f, RGBA8(255, 255, 255, 255), "%.30s", ID3.title); + //pp2d_draw_textf(184, 84, 0.5f, 0.5f, RGBA8(255, 255, 255, 255), "%.30s", ID3.album); + //pp2d_draw_textf(184, 104, 0.5f, 0.5f, RGBA8(255, 255, 255, 255), "%.30s", ID3.year); + //pp2d_draw_textf(184, 124, 0.5f, 0.5f, RGBA8(255, 255, 255, 255), "%.30s", ID3.genre); } else - pp2d_draw_textf(5, 25, 0.5f, 0.5f, RGBA8(255, 255, 255, 255), "%s", fileName); + //pp2d_draw_textf(5, 25, 0.5f, 0.5f, RGBA8(255, 255, 255, 255), "%s", fileName); - pp2d_end_draw(); + //pp2d_end_draw(); if (kDown & KEY_B) { diff --git a/source/menus/menu_properties.c b/source/menus/menu_properties.c deleted file mode 100644 index d818f09..0000000 --- a/source/menus/menu_properties.c +++ /dev/null @@ -1,82 +0,0 @@ -#include "common.h" -#include "dir_list.h" -#include "language.h" -#include "menu_properties.h" -#include "pp2d.h" -#include "textures.h" -#include "theme.h" -#include "touch.h" -#include "utils.h" - -struct colour Options_select_colour; -struct colour Options_text_colour; -struct colour Options_title_text_colour; -struct colour Settings_title_text_colour; - -void Menu_DisplayProperties(void) -{ - File * file = Dirlist_GetFileIndex(position); - - char path[255], fullPath[500]; - - strcpy(fileName, file->name); - - strcpy(path, cwd); - - char fileSize[16]; - if (!(file->isDir)) - Utils_GetSizeString(fileSize, file->size); - - u32 totalSize = 0, directories = 0, files = 0; - - pp2d_draw_texture(TEXTURE_PROPERTIES, 36, 20); - - pp2d_draw_text(41, 33, 0.45f, 0.45f, RGBA8(Settings_title_text_colour.r, Settings_title_text_colour.g, Settings_title_text_colour.b, 255), lang_properties[language][0]); - - pp2d_draw_text(247, 201, 0.45f, 0.45f, RGBA8(Settings_title_text_colour.r, Settings_title_text_colour.g, Settings_title_text_colour.b, 255), lang_properties[language][6]); - - pp2d_draw_text(((320 - pp2d_get_text_width(lang_properties[language][1], 0.45f, 0.45f)) / 2), 50, 0.45f, 0.45f, RGBA8(Options_title_text_colour.r, Options_title_text_colour.g, Options_title_text_colour.b, 255), lang_properties[language][1]); - - pp2d_draw_text(42, 74, 0.45f, 0.45f, RGBA8(Options_title_text_colour.r, Options_title_text_colour.g, Options_title_text_colour.b, 255), lang_properties[language][2]); - pp2d_draw_textf(100, 74, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), "%.28s", fileName); - pp2d_draw_text(42, 94, 0.45f, 0.45f, RGBA8(Options_title_text_colour.r, Options_title_text_colour.g, Options_title_text_colour.b, 255), lang_properties[language][3]); - pp2d_draw_textf(100, 94, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), "%.28s", path); - - pp2d_draw_text(42, 114, 0.45f, 0.45f, RGBA8(Options_title_text_colour.r, Options_title_text_colour.g, Options_title_text_colour.b, 255), lang_properties[language][4]); - - if (file->isDir) - pp2d_draw_text(100, 114, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), lang_files[language][1]); - else if ((strncmp(file->ext, "CIA", 3) == 0) || (strncmp(file->ext, "cia", 3) == 0) || (strncmp(file->ext, "3DS", 3) == 0) || (strncmp(file->ext, "3ds", 3) == 0)) - pp2d_draw_text(100, 114, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), lang_files[language][2]); - else if ((strncmp(file->ext, "bin", 3) == 0) || (strncmp(file->ext, "BIN", 3) == 0) || (strncmp(file->ext, "fir", 3) == 0) || (strncmp(file->ext, "FIR", 3) == 0)) - pp2d_draw_text(100, 114, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), lang_files[language][3]); - else if ((strncmp(file->ext, "zip", 3) == 0) || (strncmp(file->ext, "ZIP", 3) == 0)) - pp2d_draw_text(100, 114, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), lang_files[language][4]); - else if ((strncmp(file->ext, "rar", 3) == 0) || (strncmp(file->ext, "RAR", 3) == 0)) - pp2d_draw_text(100, 114, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), lang_files[language][5]); - else if ((strncmp(file->ext, "PNG", 3) == 0) || (strncmp(file->ext, "png", 3) == 0)) - pp2d_draw_text(100, 114, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), lang_files[language][6]); - else if ((strncmp(file->ext, "JPG", 3) == 0) || (strncmp(file->ext, "jpg", 3) == 0)) - pp2d_draw_text(100, 114, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), lang_files[language][7]); - else if ((strncmp(file->ext, "MP3", 3) == 0) || (strncmp(file->ext, "mp3", 3) == 0)) - pp2d_draw_text(100, 114, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), lang_files[language][8]); - else if ((strncmp(file->ext, "txt", 3) == 0) || (strncmp(file->ext, "TXT", 3) == 0) || (strncmp(file->ext, "XML", 3) == 0) || (strncmp(file->ext, "xml", 3) == 0)) - pp2d_draw_text(100, 114, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), lang_files[language][9]); - else - pp2d_draw_text(100, 114, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), lang_files[language][10]); - - if (!(file->isDir)) - { - pp2d_draw_text(42, 134, 0.45f, 0.45f, RGBA8(Options_title_text_colour.r, Options_title_text_colour.g, Options_title_text_colour.b, 255), lang_properties[language][5]); - pp2d_draw_textf(100, 134, 0.45f, 0.45f, RGBA8(Options_text_colour.r, Options_text_colour.g, Options_text_colour.b, 255), "%.28s", fileSize); - } -} - -void Menu_ControlProperties(u32 input) -{ - if ((input & KEY_B) || (input & KEY_A) || (touchInRect(36, 192, 284, 220))) - { - wait(1); - MENU_DEFAULT_STATE = MENU_STATE_OPTIONS; - } -} \ No newline at end of file diff --git a/source/menus/menu_settings.c b/source/menus/menu_settings.c deleted file mode 100644 index cb6b17a..0000000 --- a/source/menus/menu_settings.c +++ /dev/null @@ -1,95 +0,0 @@ -#include "common.h" -#include "dir_list.h" -#include "language.h" -#include "menu_settings.h" -#include "pp2d.h" -#include "textures.h" -#include "theme.h" -#include "touch.h" -#include "utils.h" - -struct colour Settings_colour; -struct colour Settings_title_text_colour; -struct colour Settings_text_colour; -struct colour Settings_text_min_colour; - -void Menu_DisplaySettings(void) -{ - pp2d_draw_rectangle(0, 20, 320, 220, RGBA8(Settings_colour.r, Settings_colour.g, Settings_colour.b, 255)); - - pp2d_draw_text(10, 30, 0.45f, 0.45f, RGBA8(Settings_title_text_colour.r, Settings_title_text_colour.g, Settings_title_text_colour.b, 255), lang_settings[language][0]); - - pp2d_draw_text(10, 50, 0.45f, 0.45f, RGBA8(Settings_text_colour.r, Settings_text_colour.g, Settings_text_colour.b, 255), "Sort by"); - pp2d_draw_text(10, 62, 0.45f, 0.45f, RGBA8(Settings_text_min_colour.r, Settings_text_min_colour.g, Settings_text_min_colour.b, 255), "Select from a list of sorting options."); - - pp2d_draw_text(10, 85, 0.45f, 0.45f, RGBA8(Settings_text_colour.r, Settings_text_colour.g, Settings_text_colour.b, 255), lang_settings[language][5]); - pp2d_draw_text(10, 97, 0.45f, 0.45f, RGBA8(Settings_text_min_colour.r, Settings_text_min_colour.g, Settings_text_min_colour.b, 255), lang_settings[language][6]); - - pp2d_draw_text(10, 120, 0.45f, 0.45f, RGBA8(Settings_text_colour.r, Settings_text_colour.g, Settings_text_colour.b, 255), lang_settings[language][1]); - pp2d_draw_text(10, 132, 0.45f, 0.45f, RGBA8(Settings_text_min_colour.r, Settings_text_min_colour.g, Settings_text_min_colour.b, 255), lang_settings[language][2]); - - pp2d_draw_text(10, 155, 0.45f, 0.45f, RGBA8(Settings_text_colour.r, Settings_text_colour.g, Settings_text_colour.b, 255), lang_settings[language][3]); - pp2d_draw_textf(10, 167, 0.45f, 0.45f, RGBA8(Settings_text_min_colour.r, Settings_text_min_colour.g, Settings_text_min_colour.b, 255), "%s %s", lang_settings[language][4], theme_dir); - - pp2d_draw_text(10, 190, 0.45f, 0.45f, RGBA8(Settings_text_colour.r, Settings_text_colour.g, Settings_text_colour.b, 255), lang_settings[language][7]); - pp2d_draw_textf(10, 202, 0.45f, 0.45f, RGBA8(Settings_text_min_colour.r, Settings_text_min_colour.g, Settings_text_min_colour.b, 255), "%s", lang_settings[language][8]); - - if (recycleBin) - pp2d_draw_texture(TEXTURE_TOGGLE_ON, 280, 90); - else - pp2d_draw_texture(TEXTURE_TOGGLE_OFF, 280, 90); - - if (galleryDisplay) - pp2d_draw_texture(TEXTURE_TOGGLE_ON, 280, 125); - else - pp2d_draw_texture(TEXTURE_TOGGLE_OFF, 280, 125); - - if (isHiddenEnabled) - pp2d_draw_texture(TEXTURE_TOGGLE_ON, 280, 195); - else - pp2d_draw_texture(TEXTURE_TOGGLE_OFF, 280, 195); - - pp2d_draw_texture(TEXTURE_THEMES_LAUNCHER, 283, 155); -} - -void Menu_ControlSettings(u32 input) -{ - if ((input & KEY_TOUCH) && (touchInRect(0, 50, 320, 72))) - { - wait(1); - MENU_DEFAULT_STATE = MENU_STATE_SORT; - } - - else if ((input & KEY_TOUCH) && (touchInRect(280, 90, 320, 110))) - { - wait(1); - recycleBin = !recycleBin; - Utils_SaveConfig(sortBy, recycleBin, galleryDisplay, isHiddenEnabled); - } - - else if ((input & KEY_TOUCH) && (touchInRect(280, 125, 320, 145))) - { - wait(1); - galleryDisplay = !galleryDisplay; - Utils_SaveConfig(sortBy, recycleBin, galleryDisplay, isHiddenEnabled); - } - - else if ((input & KEY_TOUCH) && (touchInRect(283, 155, 303, 175))) - { - wait(1); - MENU_DEFAULT_STATE = MENU_STATE_THEMES; - strcpy(cwd, "/3ds/3DShell/themes/"); - Dirlist_PopulateFiles(true); - Dirlist_DisplayFiles(); - } - - else if ((input & KEY_TOUCH) && (touchInRect(280, 195, 320, 215))) - { - wait(1); - isHiddenEnabled = !isHiddenEnabled; - Utils_SaveConfig(sortBy, recycleBin, galleryDisplay, isHiddenEnabled); - - Dirlist_PopulateFiles(true); - Dirlist_DisplayFiles(); - } -} \ No newline at end of file diff --git a/source/menus/menu_sort.c b/source/menus/menu_sort.c deleted file mode 100644 index c9c3570..0000000 --- a/source/menus/menu_sort.c +++ /dev/null @@ -1,96 +0,0 @@ -#include "common.h" -#include "dir_list.h" -#include "language.h" -#include "menu_sort.h" -#include "pp2d.h" -#include "textures.h" -#include "theme.h" -#include "touch.h" -#include "utils.h" - -struct colour Settings_colour; -struct colour Settings_title_text_colour; -struct colour Settings_text_colour; -struct colour Settings_text_min_colour; - -void Menu_DisplaySort(void) -{ - pp2d_draw_rectangle(0, 20, 320, 220, RGBA8(Settings_colour.r, Settings_colour.g, Settings_colour.b, 255)); - - pp2d_draw_text(10, 30, 0.45f, 0.45f, RGBA8(Settings_title_text_colour.r, Settings_title_text_colour.g, Settings_title_text_colour.b, 255), "Sorting options"); - - pp2d_draw_text(10, 50, 0.45f, 0.45f, RGBA8(Settings_text_colour.r, Settings_text_colour.g, Settings_text_colour.b, 255), "None"); - pp2d_draw_text(10, 62, 0.45f, 0.45f, RGBA8(Settings_text_min_colour.r, Settings_text_min_colour.g, Settings_text_min_colour.b, 255), "No sorting method is applied."); - - pp2d_draw_text(10, 85, 0.45f, 0.45f, RGBA8(Settings_text_colour.r, Settings_text_colour.g, Settings_text_colour.b, 255), "Alphabetical \uE01B"); - pp2d_draw_text(10, 97, 0.45f, 0.45f, RGBA8(Settings_text_min_colour.r, Settings_text_min_colour.g, Settings_text_min_colour.b, 255), "Sort alphabetically in ascending order."); - - pp2d_draw_text(10, 120, 0.45f, 0.45f, RGBA8(Settings_text_colour.r, Settings_text_colour.g, Settings_text_colour.b, 255), "Alphabetical \uE01C"); - pp2d_draw_text(10, 132, 0.45f, 0.45f, RGBA8(Settings_text_min_colour.r, Settings_text_min_colour.g, Settings_text_min_colour.b, 255), "Sort alphabetically in descending order."); - - pp2d_draw_text(10, 155, 0.45f, 0.45f, RGBA8(Settings_text_colour.r, Settings_text_colour.g, Settings_text_colour.b, 255), "Size \uE01B"); - pp2d_draw_text(10, 167, 0.45f, 0.45f, RGBA8(Settings_text_min_colour.r, Settings_text_min_colour.g, Settings_text_min_colour.b, 255), "Sort by size (largest first)."); - - pp2d_draw_text(10, 190, 0.45f, 0.45f, RGBA8(Settings_text_colour.r, Settings_text_colour.g, Settings_text_colour.b, 255), "Size \uE01C"); - pp2d_draw_text(10, 202, 0.45f, 0.45f, RGBA8(Settings_text_min_colour.r, Settings_text_min_colour.g, Settings_text_min_colour.b, 255), "Sort by size (smallest first)."); - - pp2d_draw_texture(sortBy == 0? TEXTURE_RADIO_ON : TEXTURE_RADIO_OFF, 280, 55); - pp2d_draw_texture(sortBy == 1? TEXTURE_RADIO_ON : TEXTURE_RADIO_OFF, 280, 90); - pp2d_draw_texture(sortBy == 2? TEXTURE_RADIO_ON : TEXTURE_RADIO_OFF, 280, 125); - pp2d_draw_texture(sortBy == 3? TEXTURE_RADIO_ON : TEXTURE_RADIO_OFF, 280, 160); - pp2d_draw_texture(sortBy == 4? TEXTURE_RADIO_ON : TEXTURE_RADIO_OFF, 280, 195); -} - -void Menu_ControlSort(u32 input) -{ - if ((input & KEY_TOUCH) && (touchInRect(280, 55, 320, 75))) - { - wait(1); - sortBy = 0; - Utils_SaveConfig(sortBy, recycleBin, galleryDisplay, isHiddenEnabled); - Dirlist_PopulateFiles(true); - Dirlist_DisplayFiles(); - } - - else if ((input & KEY_TOUCH) && (touchInRect(280, 90, 320, 110))) - { - wait(1); - sortBy = 1; - Utils_SaveConfig(sortBy, recycleBin, galleryDisplay, isHiddenEnabled); - Dirlist_PopulateFiles(true); - Dirlist_DisplayFiles(); - } - - else if ((input & KEY_TOUCH) && (touchInRect(280, 125, 320, 145))) - { - wait(1); - sortBy = 2; - Utils_SaveConfig(sortBy, recycleBin, galleryDisplay, isHiddenEnabled); - Dirlist_PopulateFiles(true); - Dirlist_DisplayFiles(); - } - - else if ((input & KEY_TOUCH) && (touchInRect(280, 160, 320, 180))) - { - wait(1); - sortBy = 3; - Utils_SaveConfig(sortBy, recycleBin, galleryDisplay, isHiddenEnabled); - Dirlist_PopulateFiles(true); - Dirlist_DisplayFiles(); - } - - else if ((input & KEY_TOUCH) && (touchInRect(280, 195, 320, 215))) - { - wait(1); - sortBy = 4; - Utils_SaveConfig(sortBy, recycleBin, galleryDisplay, isHiddenEnabled); - Dirlist_PopulateFiles(true); - Dirlist_DisplayFiles(); - } - - if (input & KEY_B) - { - wait(1); - MENU_DEFAULT_STATE = MENU_STATE_SETTINGS; - } -} \ No newline at end of file diff --git a/source/menus/menu_textviewer.c b/source/menus/menu_textviewer.c deleted file mode 100644 index 42b1692..0000000 --- a/source/menus/menu_textviewer.c +++ /dev/null @@ -1,273 +0,0 @@ -#include -#include -#include - -#include "common.h" -#include "dir_list.h" -#include "fs.h" -#include "menu_textviewer.h" -#include "pp2d.h" -#include "screenshot.h" -#include "status_bar.h" -#include "textures.h" -#include "utils.h" - -struct TextViewer -{ - Handle handle; - struct Text *text; - char *path; - int opened; - u64 lines; -}; - -struct Text -{ - int line; - char *contents; -}; - -static Result TextViewer_ReadData(struct TextViewer *textViewer, char **contents) -{ - Result ret = 0; - char *temp; - u32 bytesRead = 0; - u64 offset = 1; - - u64 fizeSize = FS_GetFileSize(archive, textViewer->path); - - temp = (char*)malloc(sizeof(char) * (fizeSize + 10)); - *contents = (char*)malloc(sizeof(char) * (fizeSize + 10)); - - if (temp == NULL) - return -1; - - if (R_FAILED(ret = FSFILE_Read(textViewer->handle, &bytesRead, offset, temp, fizeSize))) - { - FSFILE_Close(textViewer->handle); - return ret; - } - - offset += bytesRead; - - if (R_FAILED(ret = FSFILE_Close(textViewer->handle))) - return ret; - - strncpy(*contents, temp, fizeSize); - free(temp); - - return 0; -} - -static u64 TextViewer_CountLines(char *path) -{ - FILE *file = fopen(path, "r"); - int ch = 0; - u64 count = 0; - - do { - ch = fgetc(file); - if (ch == '\n') - count++; - } while(ch != EOF); - - fclose(file); - return count; -} - -static u64 TextViewer_ApplyContents(struct TextViewer *textViewer, char *contents) -{ - if (textViewer == NULL) - return -1; - - char *token; - int line_index = 0; - - token = strtok(contents, "\n"); - printf("token %s\n", token); - - if (token == NULL) - return -1; - - if (token != NULL) - { - textViewer->lines = TextViewer_CountLines(textViewer->path); - textViewer->text = (struct Text*)malloc(sizeof(struct Text) * textViewer->lines); - textViewer->text[line_index].contents = malloc(sizeof(char) * strlen(contents) + 1); - textViewer->text[line_index].line = line_index; - strcpy(textViewer->text[line_index].contents, token); - line_index += 1; - } - - token = strtok(NULL, "\n"); - - while(token != NULL) - { - textViewer->text[line_index].contents = malloc(sizeof(char*) * (strlen(token) + 1)); - strcpy(textViewer->text[line_index].contents, token); - textViewer->text[line_index].line = line_index; - line_index += 1; - token = strtok(NULL, "\n"); - } - - return 0; -} - -static Result TextViewer_Init(struct TextViewer *textViewer, const char path[]) -{ - Result ret = 0; - - if (textViewer == NULL) - return -1; - - if (R_FAILED(ret = FS_Open(&textViewer->handle, archive, path, FS_OPEN_READ))) - { - FSFILE_Close(textViewer->handle); - return ret; - } - - textViewer->path = (char*)malloc(sizeof(char) * (strlen(path) + 3)); - strcpy(textViewer->path, path); - - return 0; -} - -static int TextViewer_GetLineIndex(struct TextViewer *textViewer, int index, char **buf, int *line_number) -{ - *buf = (char*)malloc(sizeof(char) * (strlen(textViewer->text[index].contents) + 3)); - if (*buf == NULL) - return -1; - - strcpy(*buf, textViewer->text[index].contents); - *line_number = textViewer->text[index].line; - - return 0; -} - -static int TextViewer_Update(struct TextViewer *textViewer) -{ - if (textViewer == NULL) - return -1; - - /* must free */ - char *contents; - char **bak_contents = &contents; - - if (TextViewer_ReadData(textViewer, &contents) == -1) - { - printf("Failed to read file\n"); - return -1; - } - - if (TextViewer_ApplyContents(textViewer, contents) == -1) - { - printf("Failed to apply\n"); - return -1; - } - - if (*bak_contents) - free(*bak_contents); - - return 0; -} - -static void TextViewer_DrawUI(int current_line, struct TextViewer *textViewer) -{ - int textline_index = 0; - char *linebuf; - int line_number; - char *line_number_buf; - - line_number_buf = (char*)malloc(sizeof(char) * 100); - - pp2d_draw_texture(TEXTURE_TEXTVIEWER_BG, 0, 0); - - StatusBar_DisplayBar(); - - if (line_number_buf == NULL) - { - printf("Memory error\n"); - return ; - } - - pp2d_draw_text(36, 28, 0.45f, 0.45f, RGBA8(255, 255, 255, 255), fileName); - - for (textline_index = current_line; textline_index < textViewer->lines; textline_index++) - { - if ((textline_index-current_line) == 30) - break; - - if ((current_line+30) > textViewer->lines) - break; - - if (TextViewer_GetLineIndex(textViewer, textline_index, &linebuf, &line_number) == 0) - { - memset(line_number_buf, '\0', 100); - snprintf(line_number_buf, 10, "%d", line_number+1); - - pp2d_draw_text(10, 58 + ((textline_index - current_line) * 14), 0.45f, 0.45f, RGBA8(0, 0, 0, 255), linebuf); - - if (linebuf) - { - free(linebuf); - linebuf = NULL; - } - } - } - - if (line_number_buf) - free(line_number_buf); -} - -void TextViewer_DisplayText(char * path) -{ - int current_line = 0; - struct TextViewer textViewer; - - if (TextViewer_Init(&textViewer, path) == -1) - { - printf("failed to open\n"); - return; - } - - if (TextViewer_Update(&textViewer) == -1) - { - printf("failed to run\n"); - return; - } - - while (aptMainLoop()) - { - hidScanInput(); - - u32 kDown = hidKeysDown(); - u32 kHeld = hidKeysHeld(); - - pp2d_begin_draw(GFX_TOP, GFX_LEFT); - - TextViewer_DrawUI(current_line, &textViewer); - - if (kDown & KEY_DUP) - { - if (current_line > 0) - current_line -= 1; - } - - else if (kDown & KEY_DDOWN) - { - if (current_line + 30 <= textViewer.lines - 1) - current_line += 1; - } - - pp2d_end_draw(); - - if (kDown & KEY_B) - { - wait(10); - break; - } - - if (((kHeld & KEY_L) && (kDown & KEY_R)) || ((kHeld & KEY_R) && (kDown & KEY_L))) - Screenshot_Capture(); - } -} \ No newline at end of file diff --git a/source/menus/menu_update.c b/source/menus/menu_update.c deleted file mode 100644 index bf44446..0000000 --- a/source/menus/menu_update.c +++ /dev/null @@ -1,153 +0,0 @@ -#include "cia.h" -#include "common.h" -#include "fs.h" -#include "language.h" -#include "menu_update.h" -#include "net.h" -#include "pp2d.h" -#include "textures.h" -#include "theme.h" -#include "touch.h" -#include "utils.h" - -struct colour Settings_colour; -struct colour Settings_title_text_colour; -struct colour Settings_text_colour; -struct colour Settings_text_min_colour; - -struct colour BottomScreen_colour; -struct colour Options_title_text_colour; - -static bool err = false; -char ver[10]; - -bool Menu_ValidateUpdate(bool nighlty) -{ - if (nighlty) - { - if (FS_FileExists(archive, "/3ds/3DShell/UPDATE_NIGHTLY.txt")) - { - u64 size64 = 0; - u32 size = 0; - - size64 = FS_GetFileSize(archive, "/3ds/3DShell/UPDATE_NIGHTLY.txt"); - size = (u32)size64; - char * buf = (char *)malloc(size + 1); - - if (R_FAILED(FS_Read(archive, "/3ds/3DShell/UPDATE_NIGHTLY.txt", size, buf))) - { - free(buf); - return false; - } - - buf[size] = '\0'; - sscanf(buf, "%s", ver); - free(buf); - - if (strcmp(ver, GITVERSION) != 0) - return true; - - return false; - } - } -} - -void Menu_DisplayUpdate(void) -{ - float err_width = ((320 - pp2d_get_text_height("No updates available.", 0.45f, 0.45f)) / 2); - - pp2d_draw_rectangle(0, 20, 320, 220, RGBA8(Settings_colour.r, Settings_colour.g, Settings_colour.b, 255)); - - pp2d_draw_text(10, 30, 0.45f, 0.45f, RGBA8(Settings_title_text_colour.r, Settings_title_text_colour.g, Settings_title_text_colour.b, 255), "Update Center"); - - if (err) - pp2d_draw_text(err_width, 60, 0.45f, 0.45f, RGBA8(Settings_text_min_colour.r, Settings_text_min_colour.g, Settings_text_min_colour.b, 255), "No updates available."); - - pp2d_draw_textf(122, 85, 0.45f, 0.45f, RGBA8(Settings_text_colour.r, Settings_text_colour.g, Settings_text_colour.b, 255), "Nightly builds"); - pp2d_draw_textf(52, 97, 0.45f, 0.45f, RGBA8(Settings_text_min_colour.r, Settings_text_min_colour.g, Settings_text_min_colour.b, 255), "Untested builds that may contain bugs."); - - pp2d_draw_rectangle(106, 117, (pp2d_get_text_width("Check for updates", 0.45f, 0.45f) + 10), 20, - RGBA8(Settings_title_text_colour.r, Settings_title_text_colour.g, Settings_title_text_colour.b, 255)); - pp2d_draw_rectangle(110 - 3, 118, (pp2d_get_text_width("Check for updates", 0.45f, 0.45f) + 8), 18, - RGBA8(Settings_colour.r, Settings_colour.g, Settings_colour.b, 255)); - pp2d_draw_rectangle(110 - 2, 119, (pp2d_get_text_width("Check for updates", 0.45f, 0.45f) + 6), 16, - RGBA8(Settings_title_text_colour.r, Settings_title_text_colour.g, Settings_title_text_colour.b, 255)); - pp2d_draw_text(110, 120, 0.45f, 0.45f, RGBA8(Settings_colour.r, Settings_colour.g, Settings_colour.b, 255), "Check for updates"); - - pp2d_draw_textf(115, 155, 0.45f, 0.45f, RGBA8(Settings_text_colour.r, Settings_text_colour.g, Settings_text_colour.b, 255), "Milestone builds"); - pp2d_draw_textf(102, 167, 0.45f, 0.45f, RGBA8(Settings_text_min_colour.r, Settings_text_min_colour.g, Settings_text_min_colour.b, 255), "Official release builds."); - - pp2d_draw_rectangle(106, 187, (pp2d_get_text_width("Check for updates", 0.45f, 0.45f) + 10), 20, - RGBA8(Settings_title_text_colour.r, Settings_title_text_colour.g, Settings_title_text_colour.b, 255)); - pp2d_draw_rectangle(110 - 3, 188, (pp2d_get_text_width("Check for updates", 0.45f, 0.45f) + 8), 18, - RGBA8(Settings_colour.r, Settings_colour.g, Settings_colour.b, 255)); - pp2d_draw_rectangle(110 - 2, 189, (pp2d_get_text_width("Check for updates", 0.45f, 0.45f) + 6), 16, - RGBA8(Settings_title_text_colour.r, Settings_title_text_colour.g, Settings_title_text_colour.b, 255)); - pp2d_draw_text(110, 190, 0.45f, 0.45f, RGBA8(Settings_colour.r, Settings_colour.g, Settings_colour.b, 255), "Check for updates"); -} - -void Menu_DisplayUpdate2(void) -{ - pp2d_draw_rectangle(0, 0, 320, 240, RGBA8(BottomScreen_colour.r, BottomScreen_colour.g, BottomScreen_colour.b, 255)); - - pp2d_draw_texture(TEXTURE_DIALOG, 20, 55); - - pp2d_draw_text(27, 72, 0.45f, 0.45f, RGBA8(Settings_title_text_colour.r, Settings_title_text_colour.g, Settings_title_text_colour.b, 255), "Confirm update"); - - pp2d_draw_text(206, 159, 0.45f, 0.45f, RGBA8(Settings_title_text_colour.r, Settings_title_text_colour.g, Settings_title_text_colour.b, 255), "NO"); - pp2d_draw_text(255, 159, 0.45f, 0.45f, RGBA8(Settings_title_text_colour.r, Settings_title_text_colour.g, Settings_title_text_colour.b, 255), "YES"); - - pp2d_draw_text(((320 - pp2d_get_text_width(lang_deletion[language][1], 0.45f, 0.45f)) / 2), 100, 0.45f, 0.45f, RGBA8(Options_title_text_colour.r, Options_title_text_colour.g, Options_title_text_colour.b, 255), "This action cannot be undone."); - pp2d_draw_text(((320 - pp2d_get_text_width(lang_deletion[language][2], 0.45f, 0.45f)) / 2), 115, 0.45f, 0.45f, RGBA8(Options_title_text_colour.r, Options_title_text_colour.g, Options_title_text_colour.b, 255), "Do you wish to update?"); -} - -void Menu_ControlUpdate(u32 input) -{ - if ((input & KEY_TOUCH) && (touchInRect(106, 117, (106) + (pp2d_get_text_width("Check for updates", 0.45f, 0.45f) + 10), 137))) - { - wait(1); - Net_DownloadFile("https://github.com/joel16/3DShell/raw/gh-pages/UPDATE_NIGHTLY.txt", "/3ds/3DShell/UPDATE_NIGHTLY.txt"); - if (Menu_ValidateUpdate(true)) - MENU_DEFAULT_STATE = MENU_STATE_UPDATE_2; - else - err = true; - } - - else if ((input & KEY_TOUCH) && (touchInRect(106, 187, (106) + (pp2d_get_text_width("Check for updates", 0.45f, 0.45f) + 10), 207))) - { - wait(1); - Net_DownloadFile("https://github.com/joel16/3DShell/raw/gh-pages/UPDATE_MILESTONE.txt", "/3ds/3DShell/UPDATE_MILESTONE.txt"); - if (Menu_ValidateUpdate(false)) - MENU_DEFAULT_STATE = MENU_STATE_UPDATE_2; - else - err = true; - } -} - -void Menu_ControlUpdate2(u32 input) -{ - if ((input & KEY_A) || (touchInRect(240, 142, 320, 185))) - { - if(envIsHomebrew()) - { - if (FS_FileExists(archive, "/3ds/3DShell/3DShell.3dsx")) - FS_Remove(archive, "/3ds/3DShell/3DShell.3dsx"); - Net_DownloadFile("https://github.com/joel16/3DShell/raw/gh-pages/3DShell.3dsx", "/3ds/3DShell/3DShell.3dsx"); - longjmp(exitJmp, 1); - } - else - { - Net_DownloadFile("https://github.com/joel16/3DShell/raw/gh-pages/3DShell.cia", "/3ds/3DShell/3DShell.cia"); - CIA_InstallTitle("/3ds/3DShell/3DShell.cia", MEDIATYPE_SD, true); - } - - wait(1); - MENU_DEFAULT_STATE = MENU_STATE_HOME; - } - - else if ((input & KEY_B) || (touchInRect(136, 142, 239, 185))) - { - wait(1); - MENU_DEFAULT_STATE = MENU_STATE_UPDATE; - } -} \ No newline at end of file diff --git a/source/misc/battery.c b/source/misc/battery.c deleted file mode 100644 index bbac68c..0000000 --- a/source/misc/battery.c +++ /dev/null @@ -1,52 +0,0 @@ -#include <3ds.h> - -#include "battery.h" -#include "pp2d.h" -#include "textures.h" - -static u8 Battery_GetBatteryPercent(void) -{ - u8 batteryPercent = 0; - - if (R_SUCCEEDED(MCUHWC_GetBatteryLevel(&batteryPercent))) - return batteryPercent; - - return 0; -} - -char * Battery_GetBatteryPercentString(void) -{ - static char str[5]; - snprintf(str, 5, "%d%%", Battery_GetBatteryPercent()); - return str; -} - -void Battery_DrawIcons(int x, int y) -{ - u8 batteryPercent = Battery_GetBatteryPercent(); - - if (batteryPercent == 0) - pp2d_draw_texture(TEXTURE_BATTERY_0, x, y); - else if (batteryPercent > 0 && batteryPercent <= 15) - pp2d_draw_texture(TEXTURE_BATTERY_15, x, y); - else if (batteryPercent > 15 && batteryPercent <= 28) - pp2d_draw_texture(TEXTURE_BATTERY_28, x, y); - else if (batteryPercent > 28 && batteryPercent <= 43) - pp2d_draw_texture(TEXTURE_BATTERY_43, x, y); - else if (batteryPercent > 43 && batteryPercent <= 57) - pp2d_draw_texture(TEXTURE_BATTERY_57, x, y); - else if (batteryPercent > 57 && batteryPercent <= 71) - pp2d_draw_texture(TEXTURE_BATTERY_71, x, y); - else if (batteryPercent > 71 && batteryPercent <= 99) - pp2d_draw_texture(TEXTURE_BATTERY_85, x, y); - else if (batteryPercent == 100) - pp2d_draw_texture(TEXTURE_BATTERY_100, x, y); - - u8 batteryState = false; // boolean that represnets charging state - - if (R_SUCCEEDED(PTMU_GetBatteryChargeState(&batteryState))) - { - if (batteryState) - pp2d_draw_texture(TEXTURE_BATTERY_CHARGE, x + 2, y + 2); - } -} \ No newline at end of file diff --git a/source/misc/clock.c b/source/misc/clock.c deleted file mode 100644 index f140b7b..0000000 --- a/source/misc/clock.c +++ /dev/null @@ -1,67 +0,0 @@ -#include -#include - -#include "clock.h" -#include "language.h" - -char * Clock_GetCurrentTime(bool _12hour) -{ - static char buffer[10]; - - time_t unix_time = time(0); - struct tm* time_struct = gmtime((const time_t*)&unix_time); - int hours = time_struct->tm_hour; - int minutes = time_struct->tm_min; - int amOrPm = 0; - - if (_12hour) - { - if (hours < 12) - amOrPm = 1; - if (hours == 0) - hours = 12; - else if (hours > 12) - hours = hours - 12; - - if ((hours >= 1) && (hours < 10)) - snprintf(buffer, 10, "%2i:%02i %s", hours, minutes, amOrPm ? "AM" : "PM"); - else - snprintf(buffer, 10, "%2i:%02i %s", hours, minutes, amOrPm ? "AM" : "PM"); - } - - return buffer; -} - -char * Clock_GetDayOfWeek(bool brief) -{ - time_t unixTime = time(NULL); - struct tm* timeStruct = gmtime((const time_t *)&unixTime); - - static char buffer[16]; - sprintf(buffer, "%s", lang_days[language][timeStruct->tm_wday]); - - if (brief) - buffer[3] = 0; - - return buffer; -} - -char * Clock_GetMonthOfYear(int style) -{ - time_t unixTime = time(NULL); - struct tm* timeStruct = gmtime((const time_t *)&unixTime); - int day = timeStruct->tm_mday; - int year = timeStruct->tm_year + 1900; - - static char buffer[16]; - - if (style == 0) - sprintf(buffer, "%d %s, %d", day, lang_months[language][timeStruct->tm_mon], year); - else - sprintf(buffer, "%s", lang_months[language][timeStruct->tm_mon]); - - if (style == 1) - buffer[3] = 0; - - return buffer; -} diff --git a/source/misc/status_bar.c b/source/misc/status_bar.c deleted file mode 100644 index 4779f8a..0000000 --- a/source/misc/status_bar.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "battery.h" -#include "clock.h" -#include "pp2d.h" -#include "status_bar.h" -#include "textures.h" -#include "wifi.h" - -void StatusBar_DisplayBar(void) -{ - float time_x = 395 - pp2d_get_text_width(Clock_GetCurrentTime(true), 0.45f, 0.45f); - float battery_texture_x = time_x - pp2d_get_texture_width(TEXTURE_BATTERY_100) - 5; - float battery_string_x = battery_texture_x - pp2d_get_text_width(Battery_GetBatteryPercentString(), 0.45f, 0.45f) - 5; - float wifi_x = battery_string_x - pp2d_get_texture_width(TEXTURE_WIFI_3) - 10; - - pp2d_draw_text(time_x, 1, 0.45f, 0.45f, RGBA8(255, 255, 255, 255), Clock_GetCurrentTime(true)); - - Battery_DrawIcons(battery_texture_x, 1); - - pp2d_draw_text(battery_string_x, 1, 0.45f, 0.45f, RGBA8(255, 255, 255, 255), Battery_GetBatteryPercentString()); - - WiFi_DrawIcons(wifi_x, 1); -} \ No newline at end of file diff --git a/source/misc/wifi.c b/source/misc/wifi.c deleted file mode 100644 index a82b5fb..0000000 --- a/source/misc/wifi.c +++ /dev/null @@ -1,30 +0,0 @@ -#include <3ds.h> - -#include "pp2d.h" -#include "textures.h" -#include "wifi.h" - -void WiFi_DrawIcons(int x, int y) -{ - switch (osGetWifiStrength()) - { - case 0: - pp2d_draw_texture(TEXTURE_WIFI_0, x, y); - break; - - case 1: - pp2d_draw_texture(TEXTURE_WIFI_1, x, y); - break; - - case 2: - pp2d_draw_texture(TEXTURE_WIFI_2, x, y); - break; - - case 3: - pp2d_draw_texture(TEXTURE_WIFI_3, x, y); - break; - - default: - pp2d_draw_texture(TEXTURE_WIFI_NULL, x, y); - } -} \ No newline at end of file diff --git a/source/net.c b/source/net.c deleted file mode 100644 index e4bec13..0000000 --- a/source/net.c +++ /dev/null @@ -1,161 +0,0 @@ -#include -#include -#include - -#include "fs.h" -#include "progress_bar.h" -#include "net.h" -#include "utils.h" - -#define USER_AGENT APP_TITLE - -static char* result_buf = NULL; -static size_t result_sz = 0; -static size_t result_written = 0; - -// following function is from -// https://github.com/angelsl/libctrfgh/blob/master/curl_test/src/main.c -static size_t Net_HandleData(char* ptr, size_t size, size_t nmemb, void* userdata) -{ - (void) userdata; - const size_t bsz = size*nmemb; - - if (result_sz == 0 || !result_buf) - { - result_sz = 0x1000; - result_buf = (char*)malloc(result_sz); - } - - bool need_realloc = false; - while (result_written + bsz > result_sz) - { - result_sz <<= 1; - need_realloc = true; - } - - if (need_realloc) - { - char *new_buf = (char*)realloc(result_buf, result_sz); - if (!new_buf) - { - return 0; - } - result_buf = new_buf; - } - - if (!result_buf) - { - return 0; - } - - memcpy(result_buf + result_written, ptr, bsz); - result_written += bsz; - return bsz; -} - -// From MultiUpdater -static Result Net_SetupContext(CURL *hnd, const char * url) -{ - curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, 102400L); - curl_easy_setopt(hnd, CURLOPT_URL, url); - curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L); - curl_easy_setopt(hnd, CURLOPT_USERAGENT, USER_AGENT); - curl_easy_setopt(hnd, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L); - curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_2TLS); - curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, Net_HandleData); - curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L); - curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(hnd, CURLOPT_STDERR, stdout); - - return 0; -} - -// From MultiUpdater -Result Net_DownloadFile(const char *url, const char *path) -{ - Result ret = 0; - printf("Downloading from:\n%s\nto:\n%s\n", url, path); - - void *socubuf = memalign(0x1000, 0x100000); - if (!socubuf) - return -1; - - ret = socInit((u32*)socubuf, 0x100000); - if (R_FAILED(ret)) - { - free(socubuf); - return ret; - } - - CURL *hnd = curl_easy_init(); - ret = Net_SetupContext(hnd, url); - - if (ret != 0) - { - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_sz = 0; - result_written = 0; - return ret; - } - - Handle fileHandle; - u64 offset = 0; - u32 bytesWritten = 0; - - //ret = openFile(&fileHandle, path.c_str(), true); - ret = FS_Open(&fileHandle, archive, path, (FS_OPEN_WRITE | FS_OPEN_CREATE)); - - if (R_FAILED(ret)) - { - printf("Error: couldn't open file to write.\n"); - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_sz = 0; - result_written = 0; - return -1; - } - - u64 startTime = osGetTime(); - - CURLcode cres = curl_easy_perform(hnd); - curl_easy_cleanup(hnd); - - if (cres != CURLE_OK) - { - printf("Error in:\ncurl\n"); - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_sz = 0; - result_written = 0; - return -1; - } - - FSFILE_Write(fileHandle, &bytesWritten, offset, result_buf, result_written, 0); - - ProgressBar_DisplayProgress("Downloading", Utils_Basename(path), 0, 0); - - u64 endTime = osGetTime(); - u64 totalTime = endTime - startTime; - printf("Download took %llu milliseconds.\n", totalTime); - - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_sz = 0; - result_written = 0; - FSFILE_Close(fileHandle); - return 0; -} - - - - diff --git a/source/pp2d/LICENSE b/source/pp2d/LICENSE deleted file mode 100644 index 51ef706..0000000 --- a/source/pp2d/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2017 Bernardo Giordano - -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. \ No newline at end of file diff --git a/source/pp2d/README.md b/source/pp2d/README.md deleted file mode 100644 index c1682cc..0000000 --- a/source/pp2d/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# [pp2d](https://discord.gg/bGKEyfY) - -Plug & Play 2D (unofficial) wrapper for Citro3D. - -## License - -pp2d is licensed under the MIT License. - -## Changes - -* depth parameter (Ryuzaki-MrL) -* standalone pp2d_free_texture (LiquidFenrir) -* 3d support (Robz8) -* load png from memory (ErmanSayin) -* BMP support (blujay) diff --git a/source/pp2d/libnsbmp.c b/source/pp2d/libnsbmp.c deleted file mode 100644 index 173581c..0000000 --- a/source/pp2d/libnsbmp.c +++ /dev/null @@ -1,1371 +0,0 @@ -/* - * Copyright 2006 Richard Wilson - * Copyright 2008 Sean Fox - * - * This file is part of NetSurf's libnsbmp, http://www.netsurf-browser.org/ - * Licenced under the MIT License, - * http://www.opensource.org/licenses/mit-license.php - */ - -/** - * \file - * BMP decoding implementation - * - * This library decode windows bitmaps and icons from their disc images. - * - * The image format is described in several documents: - * https://msdn.microsoft.com/en-us/library/dd183391(v=vs.85).aspx - * http://www.fileformat.info/format/bmp/egff.htm - * https://en.wikipedia.org/wiki/BMP_file_format - * - * Despite the format being clearly defined many bitmaps found on the web are - * not compliant and this implementation attempts to cope with as many issues - * as possible rather than simply failing. - */ - -#include -#include -#include -#include -#include -#include - -#include - -/* squashes unused variable compiler warnings */ -#define UNUSED(x) ((x)=(x)) - -/* BMP entry sizes */ -#define BMP_FILE_HEADER_SIZE 14 -#define ICO_FILE_HEADER_SIZE 6 -#define ICO_DIR_ENTRY_SIZE 16 - -/* the bitmap information header types (encoded as lengths) */ -#define BITMAPCOREHEADER 12 - -#ifdef WE_NEED_INT8_READING_NOW -static inline int8_t read_int8(uint8_t *data, unsigned int o) { - return (int8_t) data[o]; -} -#endif - -static inline uint8_t read_uint8(uint8_t *data, unsigned int o) { - return (uint8_t) data[o]; -} - -static inline int16_t read_int16(uint8_t *data, unsigned int o) { - return (int16_t) (data[o] | (data[o+1] << 8)); -} - -static inline uint16_t read_uint16(uint8_t *data, unsigned int o) { - return (uint16_t) (data[o] | (data[o+1] << 8)); -} - -static inline int32_t read_int32(uint8_t *data, unsigned int o) { - return (int32_t) (data[o] | (data[o+1] << 8) | (data[o+2] << 16) | (data[o+3] << 24)); -} - -static inline uint32_t read_uint32(uint8_t *data, unsigned int o) { - return (uint32_t) (data[o] | (data[o+1] << 8) | (data[o+2] << 16) | (data[o+3] << 24)); -} - - -/** - * Parse the bitmap info header - */ -static bmp_result bmp_info_header_parse(bmp_image *bmp, uint8_t *data) -{ - uint32_t header_size; - uint32_t i; - uint8_t j; - int32_t width, height; - uint8_t palette_size; - unsigned int flags = 0; - - /* must be at least enough data for a core header */ - if (bmp->buffer_size < (BMP_FILE_HEADER_SIZE + BITMAPCOREHEADER)) { - return BMP_INSUFFICIENT_DATA; - } - - header_size = read_uint32(data, 0); - - /* ensure there is enough data for the declared header size*/ - if ((bmp->buffer_size - BMP_FILE_HEADER_SIZE) < header_size) { - return BMP_INSUFFICIENT_DATA; - } - - /* a variety of different bitmap headers can follow, depending - * on the BMP variant. The header length field determines the type. - */ - if (header_size == BITMAPCOREHEADER) { - /* the following header is for os/2 and windows 2.x and consists of: - * - * +0 UINT32 size of this header (in bytes) - * +4 INT16 image width (in pixels) - * +6 INT16 image height (in pixels) - * +8 UINT16 number of colour planes (always 1) - * +10 UINT16 number of bits per pixel - */ - width = read_int16(data, 4); - height = read_int16(data, 6); - if ((width <= 0) || (height == 0)) - return BMP_DATA_ERROR; - if (height < 0) { - bmp->reversed = true; - height = -height; - } - /* ICOs only support 256*256 resolutions - * In the case of the ICO header, the height is actually the added - * height of XOR-Bitmap and AND-Bitmap (double the visible height) - * Technically we could remove this check and ICOs with bitmaps - * of any size could be processed; this is to conform to the spec. - */ - if (bmp->ico) { - if ((width > 256) || (height > 512)) { - return BMP_DATA_ERROR; - } else { - bmp->width = width; - bmp->height = height / 2; - } - } else { - bmp->width = width; - bmp->height = height; - } - if (read_uint16(data, 8) != 1) - return BMP_DATA_ERROR; - bmp->bpp = read_uint16(data, 10); - /** - * The bpp value should be in the range 1-32, but the only - * values considered legal are: - * RGB ENCODING: 1, 4, 8, 16, 24 and 32 - */ - if ((bmp->bpp != 1) && (bmp->bpp != 4) && - (bmp->bpp != 8) && - (bmp->bpp != 16) && - (bmp->bpp != 24) && - (bmp->bpp != 32)) - return BMP_DATA_ERROR; - bmp->colours = (1 << bmp->bpp); - palette_size = 3; - } else if (header_size < 40) { - return BMP_DATA_ERROR; - } else { - /* the following header is for windows 3.x and onwards. it is a - * minimum of 40 bytes and (as of Windows 95) a maximum of 108 bytes. - * - * +0 UINT32 size of this header (in bytes) - * +4 INT32 image width (in pixels) - * +8 INT32 image height (in pixels) - * +12 UINT16 number of colour planes (always 1) - * +14 UINT16 number of bits per pixel - * +16 UINT32 compression methods used - * +20 UINT32 size of bitmap (in bytes) - * +24 UINT32 horizontal resolution (in pixels per meter) - * +28 UINT32 vertical resolution (in pixels per meter) - * +32 UINT32 number of colours in the image - * +36 UINT32 number of important colours - * +40 UINT32 mask identifying bits of red component - * +44 UINT32 mask identifying bits of green component - * +48 UINT32 mask identifying bits of blue component - * +52 UINT32 mask identifying bits of alpha component - * +56 UINT32 color space type - * +60 UINT32 x coordinate of red endpoint - * +64 UINT32 y coordinate of red endpoint - * +68 UINT32 z coordinate of red endpoint - * +72 UINT32 x coordinate of green endpoint - * +76 UINT32 y coordinate of green endpoint - * +80 UINT32 z coordinate of green endpoint - * +84 UINT32 x coordinate of blue endpoint - * +88 UINT32 y coordinate of blue endpoint - * +92 UINT32 z coordinate of blue endpoint - * +96 UINT32 gamma red coordinate scale value - * +100 UINT32 gamma green coordinate scale value - * +104 UINT32 gamma blue coordinate scale value - */ - width = read_int32(data, 4); - height = read_int32(data, 8); - if ((width <= 0) || (height == 0)) - return BMP_DATA_ERROR; - if (height < 0) { - bmp->reversed = true; - if (height <= -INT32_MAX) { - height = INT32_MAX; - } else { - height = -height; - } - } - /* ICOs only support 256*256 resolutions - * In the case of the ICO header, the height is actually the added - * height of XOR-Bitmap and AND-Bitmap (double the visible height) - * Technically we could remove this check and ICOs with bitmaps - * of any size could be processed; this is to conform to the spec. - */ - if (bmp->ico) { - if ((width > 256) || (height > 512)) { - return BMP_DATA_ERROR; - } else { - bmp->width = width; - bmp->height = height / 2; - } - } else { - bmp->width = width; - bmp->height = height; - } - if (read_uint16(data, 12) != 1) - return BMP_DATA_ERROR; - bmp->bpp = read_uint16(data, 14); - if (bmp->bpp == 0) - bmp->bpp = 8; - bmp->encoding = read_uint32(data, 16); - /** - * The bpp value should be in the range 1-32, but the only - * values considered legal are: - * RGB ENCODING: 1, 4, 8, 16, 24 and 32 - * RLE4 ENCODING: 4 - * RLE8 ENCODING: 8 - * BITFIELD ENCODING: 16 and 32 - */ - switch (bmp->encoding) { - case BMP_ENCODING_RGB: - if ((bmp->bpp != 1) && (bmp->bpp != 4) && - (bmp->bpp != 8) && - (bmp->bpp != 16) && - (bmp->bpp != 24) && - (bmp->bpp != 32)) - return BMP_DATA_ERROR; - break; - case BMP_ENCODING_RLE8: - if (bmp->bpp != 8) - return BMP_DATA_ERROR; - break; - case BMP_ENCODING_RLE4: - if (bmp->bpp != 4) - return BMP_DATA_ERROR; - break; - case BMP_ENCODING_BITFIELDS: - if ((bmp->bpp != 16) && (bmp->bpp != 32)) - return BMP_DATA_ERROR; - break; - /* invalid encoding */ - default: - return BMP_DATA_ERROR; - break; - } - /* Bitfield encoding means we have red, green, blue, and alpha masks. - * Here we acquire the masks and determine the required bit shift to - * align them in our 24-bit color 8-bit alpha format. - */ - if (bmp->encoding == BMP_ENCODING_BITFIELDS) { - if (header_size == 40) { - header_size += 12; - if (bmp->buffer_size < (14 + header_size)) - return BMP_INSUFFICIENT_DATA; - for (i = 0; i < 3; i++) - bmp->mask[i] = read_uint32(data, 40 + (i << 2)); - } else { - for (i = 0; i < 4; i++) - bmp->mask[i] = read_uint32(data, 40 + (i << 2)); - } - for (i = 0; i < 4; i++) { - if (bmp->mask[i] == 0) - break; - for (j = 31; j > 0; j--) - if (bmp->mask[i] & (1 << j)) { - if ((j - 7) > 0) - bmp->mask[i] &= 0xff << (j - 7); - else - bmp->mask[i] &= 0xff >> (-(j - 7)); - bmp->shift[i] = (i << 3) - (j - 7); - break; - } - } - } - bmp->colours = read_uint32(data, 32); - if (bmp->colours == 0) - bmp->colours = (1 << bmp->bpp); - palette_size = 4; - } - data += header_size; - - /* if there's no alpha mask, flag the bmp opaque */ - if ((!bmp->ico) && (bmp->mask[3] == 0)) { - flags |= BMP_OPAQUE; - bmp->opaque = true; - } - - /* we only have a palette for <16bpp */ - if (bmp->bpp < 16) { - /* we now have a series of palette entries of the format: - * - * +0 BYTE blue - * +1 BYTE green - * +2 BYTE red - * - * if the palette is from an OS/2 or Win2.x file then the entries - * are padded with an extra byte. - */ - - /* boundary checking */ - if (bmp->buffer_size < (14 + header_size + ((uint64_t)4 * bmp->colours))) - return BMP_INSUFFICIENT_DATA; - - /* create the colour table */ - bmp->colour_table = (uint32_t *)malloc(bmp->colours * 4); - if (!bmp->colour_table) - return BMP_INSUFFICIENT_MEMORY; - for (i = 0; i < bmp->colours; i++) { - bmp->colour_table[i] = data[2] | (data[1] << 8) | (data[0] << 16); - if (bmp->opaque) - bmp->colour_table[i] |= (0xff << 24); - data += palette_size; - bmp->colour_table[i] = read_uint32((uint8_t *)&bmp->colour_table[i],0); - } - - /* some bitmaps have a bad offset if there is a pallete, work - * round this by fixing up the data offset to after the palette - * but only if there is data following the palette as some - * bitmaps encode data in the palette! - */ - if ((bmp->bitmap_offset < (uint32_t)(data - bmp->bmp_data)) && - ((bmp->buffer_size - (data - bmp->bmp_data)) > 0)) { - bmp->bitmap_offset = data - bmp->bmp_data; - } - } - - /* create our bitmap */ - flags |= BMP_NEW | BMP_CLEAR_MEMORY; - bmp->bitmap = bmp->bitmap_callbacks.bitmap_create(bmp->width, bmp->height, flags); - if (!bmp->bitmap) { - if (bmp->colour_table) - free(bmp->colour_table); - bmp->colour_table = NULL; - return BMP_INSUFFICIENT_MEMORY; - } - /* BMPs within ICOs don't have BMP file headers, so the image data should - * always be right after the colour table. - */ - if (bmp->ico) - bmp->bitmap_offset = (intptr_t)data - (intptr_t)bmp->bmp_data; - return BMP_OK; -} - - -/** - * Parse the bitmap file header - * - * \param bmp The bitmap. - * \param data The data for the file header - * \return BMP_OK on success or error code on faliure - */ -static bmp_result bmp_file_header_parse(bmp_image *bmp, uint8_t *data) -{ - /* standard 14-byte BMP file header is: - * - * +0 UINT16 File Type ('BM') - * +2 UINT32 Size of File (in bytes) - * +6 INT16 Reserved Field (1) - * +8 INT16 Reserved Field (2) - * +10 UINT32 Starting Position of Image Data (offset in bytes) - */ - if (bmp->buffer_size < BMP_FILE_HEADER_SIZE) - return BMP_INSUFFICIENT_DATA; - - if ((data[0] != (uint8_t)'B') || (data[1] != (uint8_t)'M')) - return BMP_DATA_ERROR; - - bmp->bitmap_offset = read_uint32(data, 10); - - /* check the offset to data lies within the file */ - if (bmp->bitmap_offset >= bmp->buffer_size) { - return BMP_INSUFFICIENT_DATA; - } - - return BMP_OK; -} - - -/** - * Allocates memory for the next BMP in an ICO collection - * - * Sets proper structure values - * - * \param ico the ICO collection to add the image to - * \param image a pointer to the ICO image to be initialised - */ -static bmp_result next_ico_image(ico_collection *ico, ico_image *image) { - bmp_create(&image->bmp, &ico->bitmap_callbacks); - image->next = ico->first; - ico->first = image; - return BMP_OK; -} - - -/** - * Parse the icon file header - * - * \param ico The icon collection. - * \param data The header data to parse. - * \return BMP_OK on successful parse else error code - */ -static bmp_result ico_header_parse(ico_collection *ico, uint8_t *data) -{ - uint16_t count, i; - bmp_result result; - int area, max_area = 0; - - /* 6-byte ICO file header is: - * - * +0 INT16 Reserved (should be 0) - * +2 UINT16 Type (1 for ICO, 2 for CUR) - * +4 UINT16 Number of BMPs to follow - */ - if (ico->buffer_size < ICO_FILE_HEADER_SIZE) - return BMP_INSUFFICIENT_DATA; - // if (read_int16(data, 2) != 0x0000) - // return BMP_DATA_ERROR; - if (read_uint16(data, 2) != 0x0001) - return BMP_DATA_ERROR; - count = read_uint16(data, 4); - if (count == 0) - return BMP_DATA_ERROR; - data += ICO_FILE_HEADER_SIZE; - - /* check if we have enough data for the directory */ - if (ico->buffer_size < (uint32_t)(ICO_FILE_HEADER_SIZE + (ICO_DIR_ENTRY_SIZE * count))) - return BMP_INSUFFICIENT_DATA; - - /* Decode the BMP files. - * - * 16-byte ICO directory entry is: - * - * +0 UINT8 Width (0 for 256 pixels) - * +1 UINT8 Height (0 for 256 pixels) - * +2 UINT8 Colour count (0 if more than 256 colours) - * +3 INT8 Reserved (should be 0, but may not be) - * +4 UINT16 Colour Planes (should be 0 or 1) - * +6 UINT16 Bits Per Pixel - * +8 UINT32 Size of BMP info header + bitmap data in bytes - * +12 UINT32 Offset (points to the BMP info header, not the bitmap data) - */ - for (i = 0; i < count; i++) { - ico_image *image; - image = calloc(1, sizeof(ico_image)); - if (!image) - return BMP_INSUFFICIENT_MEMORY; - result = next_ico_image(ico, image); - if (result != BMP_OK) - return result; - image->bmp.width = read_uint8(data, 0); - if (image->bmp.width == 0) - image->bmp.width = 256; - image->bmp.height = read_uint8(data, 1); - if (image->bmp.height == 0) - image->bmp.height = 256; - image->bmp.buffer_size = read_uint32(data, 8); - image->bmp.bmp_data = ico->ico_data + read_uint32(data, 12); - image->bmp.ico = true; - data += ICO_DIR_ENTRY_SIZE; - - /* Ensure that the bitmap data resides in the buffer */ - if (image->bmp.bmp_data - ico->ico_data >= 0 && - (uint32_t)(image->bmp.bmp_data - - ico->ico_data) >= ico->buffer_size) - return BMP_DATA_ERROR; - - /* Ensure that we have sufficient data to read the bitmap */ - if (image->bmp.buffer_size - ICO_DIR_ENTRY_SIZE >= - ico->buffer_size - (ico->ico_data - data)) - return BMP_INSUFFICIENT_DATA; - - result = bmp_info_header_parse(&image->bmp, - image->bmp.bmp_data); - if (result != BMP_OK) - return result; - - /* adjust the size based on the images available */ - area = image->bmp.width * image->bmp.height; - if (area > max_area) { - ico->width = image->bmp.width; - ico->height = image->bmp.height; - max_area = area; - } - } - return BMP_OK; -} - - -/** - * Decode BMP data stored in 32bpp colour. - * - * \param bmp the BMP image to decode - * \param start the data to decode, updated to last byte read on success - * \param bytes the number of bytes of data available - * \return BMP_OK on success - * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; - * in this case, the image may be partially viewable - */ -static bmp_result bmp_decode_rgb32(bmp_image *bmp, uint8_t **start, int bytes) -{ - uint8_t *top, *bottom, *end, *data; - uint32_t *scanline; - uint32_t x, y; - uint32_t swidth; - uint8_t i; - uint32_t word; - - assert(bmp->bpp == 32); - - data = *start; - swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; - top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); - if (!top) - return BMP_INSUFFICIENT_MEMORY; - bottom = top + (uint64_t)swidth * (bmp->height - 1); - end = data + bytes; - bmp->decoded = true; - - /* Determine transparent index */ - if (bmp->limited_trans) { - if ((data + 4) > end) - return BMP_INSUFFICIENT_DATA; - if (bmp->encoding == BMP_ENCODING_BITFIELDS) - bmp->transparent_index = read_uint32(data, 0); - else - bmp->transparent_index = data[2] | (data[1] << 8) | (data[0] << 16); - } - - for (y = 0; y < bmp->height; y++) { - if ((data + (4 * bmp->width)) > end) - return BMP_INSUFFICIENT_DATA; - if (bmp->reversed) - scanline = (void *)(top + (y * swidth)); - else - scanline = (void *)(bottom - (y * swidth)); - if (bmp->encoding == BMP_ENCODING_BITFIELDS) { - for (x = 0; x < bmp->width; x++) { - word = read_uint32(data, 0); - for (i = 0; i < 4; i++) - if (bmp->shift[i] > 0) - scanline[x] |= ((word & bmp->mask[i]) << bmp->shift[i]); - else - scanline[x] |= ((word & bmp->mask[i]) >> (-bmp->shift[i])); - /* 32-bit BMPs have alpha masks, but sometimes they're not utilized */ - if (bmp->opaque) - scanline[x] |= (0xff << 24); - data += 4; - scanline[x] = read_uint32((uint8_t *)&scanline[x],0); - } - } else { - for (x = 0; x < bmp->width; x++) { - scanline[x] = data[2] | (data[1] << 8) | (data[0] << 16); - if ((bmp->limited_trans) && (scanline[x] == bmp->transparent_index)) { - scanline[x] = bmp->trans_colour; - } - if (bmp->opaque) { - scanline[x] |= (0xff << 24); - } else { - scanline[x] |= data[3] << 24; - } - data += 4; - scanline[x] = read_uint32((uint8_t *)&scanline[x],0); - } - } - } - *start = data; - return BMP_OK; -} - - -/** - * Decode BMP data stored in 24bpp colour. - * - * \param bmp the BMP image to decode - * \param start the data to decode, updated to last byte read on success - * \param bytes the number of bytes of data available - * \return BMP_OK on success - * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; - * in this case, the image may be partially viewable - */ -static bmp_result bmp_decode_rgb24(bmp_image *bmp, uint8_t **start, int bytes) -{ - uint8_t *top, *bottom, *end, *data; - uint32_t *scanline; - uint32_t x, y; - uint32_t swidth; - intptr_t addr; - - assert(bmp->encoding == BMP_ENCODING_RGB); - assert(bmp->bpp == 24); - - data = *start; - swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; - top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); - if (!top) { - return BMP_INSUFFICIENT_MEMORY; - } - - bottom = top + (uint64_t)swidth * (bmp->height - 1); - end = data + bytes; - addr = ((intptr_t)data) & 3; - bmp->decoded = true; - - /* Determine transparent index */ - if (bmp->limited_trans) { - if ((data + 3) > end) { - return BMP_INSUFFICIENT_DATA; - } - - bmp->transparent_index = data[2] | (data[1] << 8) | (data[0] << 16); - } - - for (y = 0; y < bmp->height; y++) { - if ((data + (3 * bmp->width)) > end) { - return BMP_INSUFFICIENT_DATA; - } - - if (bmp->reversed) { - scanline = (void *)(top + (y * swidth)); - } else { - scanline = (void *)(bottom - (y * swidth)); - } - - for (x = 0; x < bmp->width; x++) { - scanline[x] = data[2] | (data[1] << 8) | (data[0] << 16); - if ((bmp->limited_trans) && (scanline[x] == bmp->transparent_index)) { - scanline[x] = bmp->trans_colour; - } else { - scanline[x] |= (0xff << 24); - } - data += 3; - scanline[x] = read_uint32((uint8_t *)&scanline[x],0); - } - - while (addr != (((intptr_t)data) & 3)) { - data++; - } - } - *start = data; - return BMP_OK; -} - - -/** - * Decode BMP data stored in 16bpp colour. - * - * \param bmp the BMP image to decode - * \param start the data to decode, updated to last byte read on success - * \param bytes the number of bytes of data available - * \return BMP_OK on success - * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; - * in this case, the image may be partially viewable - */ -static bmp_result bmp_decode_rgb16(bmp_image *bmp, uint8_t **start, int bytes) -{ - uint8_t *top, *bottom, *end, *data; - uint32_t *scanline; - uint32_t x, y, swidth; - intptr_t addr; - uint8_t i; - uint16_t word; - - data = *start; - swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; - top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); - if (!top) - return BMP_INSUFFICIENT_MEMORY; - bottom = top + (uint64_t)swidth * (bmp->height - 1); - end = data + bytes; - addr = ((intptr_t)data) & 3; - bmp->decoded = true; - - /* Determine transparent index */ - if (bmp->limited_trans) { - if ((data + 2) > end) - return BMP_INSUFFICIENT_DATA; - bmp->transparent_index = read_uint16(data, 0); - } - - for (y = 0; y < bmp->height; y++) { - if ((data + (2 * bmp->width)) > end) - return BMP_INSUFFICIENT_DATA; - if (bmp->reversed) - scanline = (void *)(top + (y * swidth)); - else - scanline = (void *)(bottom - (y * swidth)); - if (bmp->encoding == BMP_ENCODING_BITFIELDS) { - for (x = 0; x < bmp->width; x++) { - word = read_uint16(data, 0); - if ((bmp->limited_trans) && (word == bmp->transparent_index)) - scanline[x] = bmp->trans_colour; - else { - scanline[x] = 0; - for (i = 0; i < 4; i++) - if (bmp->shift[i] > 0) - scanline[x] |= ((word & bmp->mask[i]) << bmp->shift[i]); - else - scanline[x] |= ((word & bmp->mask[i]) >> (-bmp->shift[i])); - if (bmp->opaque) - scanline[x] |= (0xff << 24); - } - data += 2; - scanline[x] = read_uint32((uint8_t *)&scanline[x],0); - } - } else { - for (x = 0; x < bmp->width; x++) { - word = read_uint16(data, 0); - if ((bmp->limited_trans) && (word == bmp->transparent_index)) - scanline[x] = bmp->trans_colour; - else { - /* 16-bit RGB defaults to RGB555 */ - scanline[x] = ((word & (31 << 0)) << 19) | - ((word & (31 << 5)) << 6) | - ((word & (31 << 10)) >> 7); - } - if (bmp->opaque) - scanline[x] |= (0xff << 24); - data += 2; - scanline[x] = read_uint32((uint8_t *)&scanline[x],0); - } - } - while (addr != (((intptr_t)data) & 3)) - data += 2; - } - *start = data; - return BMP_OK; -} - - -/** - * Decode BMP data stored with a palette and in 8bpp colour or less. - * - * \param bmp the BMP image to decode - * \param start the data to decode, updated to last byte read on success - * \param bytes the number of bytes of data available - * \return BMP_OK on success - * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; - * in this case, the image may be partially viewable - */ -static bmp_result bmp_decode_rgb(bmp_image *bmp, uint8_t **start, int bytes) -{ - uint8_t *top, *bottom, *end, *data; - uint32_t *scanline; - intptr_t addr; - uint32_t x, y, swidth; - uint8_t bit_shifts[8]; - uint8_t ppb = 8 / bmp->bpp; - uint8_t bit_mask = (1 << bmp->bpp) - 1; - uint8_t cur_byte = 0, bit, i; - - for (i = 0; i < ppb; i++) - bit_shifts[i] = 8 - ((i + 1) * bmp->bpp); - - data = *start; - swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; - top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); - if (!top) - return BMP_INSUFFICIENT_MEMORY; - bottom = top + (uint64_t)swidth * (bmp->height - 1); - end = data + bytes; - addr = ((intptr_t)data) & 3; - bmp->decoded = true; - - /* Determine transparent index */ - if (bmp->limited_trans) { - uint32_t idx = (*data >> bit_shifts[0]) & bit_mask; - if (idx >= bmp->colours) - return BMP_DATA_ERROR; - bmp->transparent_index = bmp->colour_table[idx]; - } - - for (y = 0; y < bmp->height; y++) { - bit = 8; - if ((data + (bmp->width / ppb)) > end) - return BMP_INSUFFICIENT_DATA; - if (bmp->reversed) - scanline = (void *)(top + (y * swidth)); - else - scanline = (void *)(bottom - (y * swidth)); - for (x = 0; x < bmp->width; x++) { - uint32_t idx; - if (bit >= ppb) { - bit = 0; - cur_byte = *data++; - } - idx = (cur_byte >> bit_shifts[bit++]) & bit_mask; - if (idx < bmp->colours) { - /* ensure colour table index is in bounds */ - scanline[x] = bmp->colour_table[idx]; - if ((bmp->limited_trans) && - (scanline[x] == bmp->transparent_index)) { - scanline[x] = bmp->trans_colour; - } - } - } - while (addr != (((intptr_t)data) & 3)) - data++; - } - *start = data; - return BMP_OK; -} - - -/** - * Decode a 1bpp mask for an ICO - * - * \param bmp the BMP image to decode - * \param data the data to decode - * \param bytes the number of bytes of data available - * \return BMP_OK on success - */ -static bmp_result bmp_decode_mask(bmp_image *bmp, uint8_t *data, int bytes) -{ - uint8_t *top, *bottom, *end; - uint32_t *scanline; - intptr_t addr; - uint32_t x, y, swidth; - uint32_t cur_byte = 0; - - swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; - top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); - if (!top) - return BMP_INSUFFICIENT_MEMORY; - bottom = top + (uint64_t)swidth * (bmp->height - 1); - end = data + bytes; - - addr = ((intptr_t)data) & 3; - - for (y = 0; y < bmp->height; y++) { - if ((data + (bmp->width >> 3)) > end) - return BMP_INSUFFICIENT_DATA; - scanline = (void *)(bottom - (y * swidth)); - for (x = 0; x < bmp->width; x++) { - if ((x & 7) == 0) - cur_byte = *data++; - scanline[x] = read_uint32((uint8_t *)&scanline[x], 0); - if ((cur_byte & 128) == 0) { - scanline[x] |= (0xff << 24); - } else { - scanline[x] &= 0xffffff; - } - scanline[x] = read_uint32((uint8_t *)&scanline[x], 0); - cur_byte = cur_byte << 1; - } - while (addr != (((intptr_t)data) & 3)) - data++; - } - return BMP_OK; -} - - -/** - * Decode BMP data stored encoded in RLE8. - * - * \param bmp the BMP image to decode - * \param data the data to decode - * \param bytes the number of bytes of data available - * \return BMP_OK on success - * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; - * in this case, the image may be partially viewable - */ -static bmp_result -bmp_decode_rle8(bmp_image *bmp, uint8_t *data, int bytes) -{ - uint8_t *top, *bottom, *end; - uint32_t *scanline; - uint32_t swidth; - uint32_t i, length, pixels_left; - uint32_t x = 0, y = 0, last_y = 0; - uint32_t pixel = 0; - - if (bmp->ico) - return BMP_DATA_ERROR; - - swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; - top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); - if (!top) - return BMP_INSUFFICIENT_MEMORY; - bottom = top + (uint64_t)swidth * (bmp->height - 1); - end = data + bytes; - bmp->decoded = true; - - do { - if (data + 2 > end) - return BMP_INSUFFICIENT_DATA; - length = *data++; - if (length == 0) { - length = *data++; - switch (length) { - case 0: - /* 00 - 00 means end of scanline */ - x = 0; - if (last_y == y) { - y++; - if (y >= bmp->height) - return BMP_DATA_ERROR; - } - last_y = y; - break; - - case 1: - /* 00 - 01 means end of RLE data */ - return BMP_OK; - - case 2: - /* 00 - 02 - XX - YY means move cursor */ - if (data + 2 > end) - return BMP_INSUFFICIENT_DATA; - x += *data++; - if (x >= bmp->width) - return BMP_DATA_ERROR; - y += *data++; - if (y >= bmp->height) - return BMP_DATA_ERROR; - break; - - default: - /* 00 - NN means escape NN pixels */ - if (bmp->reversed) { - pixels_left = (bmp->height - y) * bmp->width - x; - scanline = (void *)(top + (y * swidth)); - } else { - pixels_left = (y + 1) * bmp->width - x; - scanline = (void *)(bottom - (y * swidth)); - } - if (length > pixels_left) - length = pixels_left; - if (data + length > end) - return BMP_INSUFFICIENT_DATA; - - /* the following code could be easily optimised - * by simply checking the bounds on entry and - * using some simple copying routines if so - */ - for (i = 0; i < length; i++) { - uint32_t idx = (uint32_t) *data++; - if (x >= bmp->width) { - x = 0; - y++; - if (y >= bmp->height) - return BMP_DATA_ERROR; - if (bmp->reversed) { - scanline += bmp->width; - } else { - scanline -= bmp->width; - } - } - if (idx >= bmp->colours) - return BMP_DATA_ERROR; - scanline[x++] = bmp->colour_table[idx]; - } - - if ((length & 1) && (*data++ != 0x00)) - return BMP_DATA_ERROR; - - break; - } - } else { - uint32_t idx; - - /* NN means perform RLE for NN pixels */ - if (bmp->reversed) { - pixels_left = (bmp->height - y) * bmp->width - x; - scanline = (void *)(top + (y * swidth)); - } else { - pixels_left = (y + 1) * bmp->width - x; - scanline = (void *)(bottom - (y * swidth)); - } - if (length > pixels_left) - length = pixels_left; - - /* boundary checking */ - if (data + 1 > end) - return BMP_INSUFFICIENT_DATA; - - /* the following code could be easily optimised by - * simply checking the bounds on entry and using some - * simply copying routines if so - */ - idx = (uint32_t) *data++; - if (idx >= bmp->colours) - return BMP_DATA_ERROR; - - pixel = bmp->colour_table[idx]; - for (i = 0; i < length; i++) { - if (x >= bmp->width) { - x = 0; - y++; - if (y >= bmp->height) - return BMP_DATA_ERROR; - if (bmp->reversed) { - scanline += bmp->width; - } else { - scanline -= bmp->width; - } - } - scanline[x++] = pixel; - } - } - } while (data < end); - - return BMP_OK; -} - - -/** - * Decode BMP data stored encoded in RLE4. - * - * \param bmp the BMP image to decode - * \param data the data to decode - * \param bytes the number of bytes of data available - * \return BMP_OK on success - * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; - * in this case, the image may be partially viewable - */ -static bmp_result -bmp_decode_rle4(bmp_image *bmp, uint8_t *data, int bytes) -{ - uint8_t *top, *bottom, *end; - uint32_t *scanline; - uint32_t swidth; - uint32_t i, length, pixels_left; - uint32_t x = 0, y = 0, last_y = 0; - uint32_t pixel = 0, pixel2; - - if (bmp->ico) - return BMP_DATA_ERROR; - - swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; - top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); - if (!top) - return BMP_INSUFFICIENT_MEMORY; - bottom = top + (uint64_t)swidth * (bmp->height - 1); - end = data + bytes; - bmp->decoded = true; - - do { - if (data + 2 > end) - return BMP_INSUFFICIENT_DATA; - length = *data++; - if (length == 0) { - length = *data++; - switch (length) { - case 0: - /* 00 - 00 means end of scanline */ - x = 0; - if (last_y == y) { - y++; - if (y >= bmp->height) - return BMP_DATA_ERROR; - } - last_y = y; - break; - - case 1: - /* 00 - 01 means end of RLE data */ - return BMP_OK; - - case 2: - /* 00 - 02 - XX - YY means move cursor */ - if (data + 2 > end) - return BMP_INSUFFICIENT_DATA; - x += *data++; - if (x >= bmp->width) - return BMP_DATA_ERROR; - y += *data++; - if (y >= bmp->height) - return BMP_DATA_ERROR; - break; - - default: - /* 00 - NN means escape NN pixels */ - if (bmp->reversed) { - pixels_left = (bmp->height - y) * bmp->width - x; - scanline = (void *)(top + (y * swidth)); - } else { - pixels_left = (y + 1) * bmp->width - x; - scanline = (void *)(bottom - (y * swidth)); - } - if (length > pixels_left) - length = pixels_left; - if (data + ((length + 1) / 2) > end) - return BMP_INSUFFICIENT_DATA; - - /* the following code could be easily optimised - * by simply checking the bounds on entry and - * using some simple copying routines - */ - - for (i = 0; i < length; i++) { - if (x >= bmp->width) { - x = 0; - y++; - if (y >= bmp->height) - return BMP_DATA_ERROR; - if (bmp->reversed) { - scanline += bmp->width; - } else { - scanline -= bmp->width; - } - - } - if ((i & 1) == 0) { - pixel = *data++; - if ((pixel >> 4) >= bmp->colours) - return BMP_DATA_ERROR; - scanline[x++] = bmp->colour_table - [pixel >> 4]; - } else { - if ((pixel & 0xf) >= bmp->colours) - return BMP_DATA_ERROR; - scanline[x++] = bmp->colour_table - [pixel & 0xf]; - } - } - length = (length + 1) >> 1; - - if ((length & 1) && (*data++ != 0x00)) - return BMP_DATA_ERROR; - - break; - } - } else { - /* NN means perform RLE for NN pixels */ - if (bmp->reversed) { - pixels_left = (bmp->height - y) * bmp->width - x; - scanline = (void *)(top + (y * swidth)); - } else { - pixels_left = (y + 1) * bmp->width - x; - scanline = (void *)(bottom - (y * swidth)); - } - if (length > pixels_left) - length = pixels_left; - - /* boundary checking */ - if (data + 1 > end) - return BMP_INSUFFICIENT_DATA; - - /* the following code could be easily optimised by - * simply checking the bounds on entry and using some - * simple copying routines - */ - - pixel2 = *data++; - if ((pixel2 >> 4) >= bmp->colours || - (pixel2 & 0xf) >= bmp->colours) - return BMP_DATA_ERROR; - pixel = bmp->colour_table[pixel2 >> 4]; - pixel2 = bmp->colour_table[pixel2 & 0xf]; - for (i = 0; i < length; i++) { - if (x >= bmp->width) { - x = 0; - y++; - if (y >= bmp->height) - return BMP_DATA_ERROR; - if (bmp->reversed) { - scanline += bmp->width; - } else { - scanline -= bmp->width; - } - } - if ((i & 1) == 0) - scanline[x++] = pixel; - else - scanline[x++] = pixel2; - } - - } - } while (data < end); - - return BMP_OK; -} - - -/* exported interface documented in libnsbmp.h */ -bmp_result -bmp_create(bmp_image *bmp, - bmp_bitmap_callback_vt *bitmap_callbacks) -{ - memset(bmp, 0, sizeof(bmp_image)); - bmp->bitmap_callbacks = *bitmap_callbacks; - - return BMP_OK; -} - - -/* exported interface documented in libnsbmp.h */ -bmp_result -ico_collection_create(ico_collection *ico, - bmp_bitmap_callback_vt *bitmap_callbacks) -{ - - memset(ico, 0, sizeof(ico_collection)); - ico->bitmap_callbacks = *bitmap_callbacks; - - return BMP_OK; -} - - -/* exported interface documented in libnsbmp.h */ -bmp_result bmp_analyse(bmp_image *bmp, size_t size, uint8_t *data) -{ - bmp_result res; - - /* ensure we aren't already initialised */ - if (bmp->bitmap) { - return BMP_OK; - } - - /* initialize source data values */ - bmp->buffer_size = size; - bmp->bmp_data = data; - - res = bmp_file_header_parse(bmp, data); - if (res == BMP_OK) { - res = bmp_info_header_parse(bmp, data + BMP_FILE_HEADER_SIZE); - } - return res; -} - - -/* exported interface documented in libnsbmp.h */ -bmp_result ico_analyse(ico_collection *ico, size_t size, uint8_t *data) -{ - /* ensure we aren't already initialised */ - if (ico->first) - return BMP_OK; - - /* initialize values */ - ico->buffer_size = size; - ico->ico_data = data; - - return ico_header_parse(ico, data); -} - - -/* exported interface documented in libnsbmp.h */ -bmp_result bmp_decode(bmp_image *bmp) -{ - uint8_t *data; - uint32_t bytes; - bmp_result result = BMP_OK; - - assert(bmp->bitmap); - - data = bmp->bmp_data + bmp->bitmap_offset; - bytes = bmp->buffer_size - bmp->bitmap_offset; - - switch (bmp->encoding) { - case BMP_ENCODING_RGB: - switch (bmp->bpp) { - case 32: - result = bmp_decode_rgb32(bmp, &data, bytes); - break; - - case 24: - result = bmp_decode_rgb24(bmp, &data, bytes); - break; - - case 16: - result = bmp_decode_rgb16(bmp, &data, bytes); - break; - - default: - result = bmp_decode_rgb(bmp, &data, bytes); - break; - } - break; - - case BMP_ENCODING_RLE8: - result = bmp_decode_rle8(bmp, data, bytes); - break; - - case BMP_ENCODING_RLE4: - result = bmp_decode_rle4(bmp, data, bytes); - break; - - case BMP_ENCODING_BITFIELDS: - switch (bmp->bpp) { - case 32: - result = bmp_decode_rgb32(bmp, &data, bytes); - break; - - case 16: - result = bmp_decode_rgb16(bmp, &data, bytes); - break; - - default: - result = BMP_DATA_ERROR; - break; - } - break; - } - - /* icons with less than 32bpp have a 1bpp alpha mask */ - if ((result == BMP_OK) && (bmp->ico) && (bmp->bpp != 32)) { - bytes = (uintptr_t)bmp->bmp_data + bmp->buffer_size - (uintptr_t)data; - result = bmp_decode_mask(bmp, data, bytes); - } - return result; -} - - -/* exported interface documented in libnsbmp.h */ -bmp_result bmp_decode_trans(bmp_image *bmp, uint32_t colour) -{ - bmp->limited_trans = true; - bmp->trans_colour = colour; - return bmp_decode(bmp); -} - - -/* exported interface documented in libnsbmp.h */ -bmp_image *ico_find(ico_collection *ico, uint16_t width, uint16_t height) -{ - bmp_image *bmp = NULL; - ico_image *image; - int x, y, cur, distance = (1 << 24); - - if (width == 0) - width = ico->width; - if (height == 0) - height = ico->height; - for (image = ico->first; image; image = image->next) { - if ((image->bmp.width == width) && (image->bmp.height == height)) - return &image->bmp; - x = image->bmp.width - width; - y = image->bmp.height - height; - cur = (x * x) + (y * y); - if (cur < distance) { - distance = cur; - bmp = &image->bmp; - } - } - return bmp; -} - - -/* exported interface documented in libnsbmp.h */ -void bmp_finalise(bmp_image *bmp) -{ - if (bmp->bitmap) - bmp->bitmap_callbacks.bitmap_destroy(bmp->bitmap); - bmp->bitmap = NULL; - if (bmp->colour_table) - free(bmp->colour_table); - bmp->colour_table = NULL; -} - - -/* exported interface documented in libnsbmp.h */ -void ico_finalise(ico_collection *ico) -{ - ico_image *image; - - for (image = ico->first; image; image = image->next) - bmp_finalise(&image->bmp); - while (ico->first) { - image = ico->first; - ico->first = image->next; - free(image); - } -} diff --git a/source/pp2d/lodepng.c b/source/pp2d/lodepng.c deleted file mode 100644 index 98a8376..0000000 --- a/source/pp2d/lodepng.c +++ /dev/null @@ -1,6245 +0,0 @@ -/* -LodePNG version 20180114 - -Copyright (c) 2005-2018 Lode Vandevenne - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*/ - -/* -The manual and changelog are in the header file "lodepng.h" -Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for C. -*/ - -#include "lodepng.h" - -#include -#include -#include - -#if defined(_MSC_VER) && (_MSC_VER >= 1310) /*Visual Studio: A few warning types are not desired here.*/ -#pragma warning( disable : 4244 ) /*implicit conversions: not warned by gcc -Wall -Wextra and requires too much casts*/ -#pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/ -#endif /*_MSC_VER */ - -const char* LODEPNG_VERSION_STRING = "20180114"; - -/* -This source file is built up in the following large parts. The code sections -with the "LODEPNG_COMPILE_" #defines divide this up further in an intermixed way. --Tools for C and common code for PNG and Zlib --C Code for Zlib (huffman, deflate, ...) --C Code for PNG (file format chunks, adam7, PNG filters, color conversions, ...) --The C++ wrapper around all of the above -*/ - -/*The malloc, realloc and free functions defined here with "lodepng_" in front -of the name, so that you can easily change them to others related to your -platform if needed. Everything else in the code calls these. Pass --DLODEPNG_NO_COMPILE_ALLOCATORS to the compiler, or comment out -#define LODEPNG_COMPILE_ALLOCATORS in the header, to disable the ones here and -define them in your own project's source files without needing to change -lodepng source code. Don't forget to remove "static" if you copypaste them -from here.*/ - -#ifdef LODEPNG_COMPILE_ALLOCATORS -static void* lodepng_malloc(size_t size) -{ - return malloc(size); -} - -static void* lodepng_realloc(void* ptr, size_t new_size) -{ - return realloc(ptr, new_size); -} - -static void lodepng_free(void* ptr) -{ - free(ptr); -} -#else /*LODEPNG_COMPILE_ALLOCATORS*/ -void* lodepng_malloc(size_t size); -void* lodepng_realloc(void* ptr, size_t new_size); -void lodepng_free(void* ptr); -#endif /*LODEPNG_COMPILE_ALLOCATORS*/ - -/* ////////////////////////////////////////////////////////////////////////// */ -/* ////////////////////////////////////////////////////////////////////////// */ -/* // Tools for C, and common code for PNG and Zlib. // */ -/* ////////////////////////////////////////////////////////////////////////// */ -/* ////////////////////////////////////////////////////////////////////////// */ - -/* -Often in case of an error a value is assigned to a variable and then it breaks -out of a loop (to go to the cleanup phase of a function). This macro does that. -It makes the error handling code shorter and more readable. - -Example: if(!uivector_resizev(&frequencies_ll, 286, 0)) ERROR_BREAK(83); -*/ -#define CERROR_BREAK(errorvar, code)\ -{\ - errorvar = code;\ - break;\ -} - -/*version of CERROR_BREAK that assumes the common case where the error variable is named "error"*/ -#define ERROR_BREAK(code) CERROR_BREAK(error, code) - -/*Set error var to the error code, and return it.*/ -#define CERROR_RETURN_ERROR(errorvar, code)\ -{\ - errorvar = code;\ - return code;\ -} - -/*Try the code, if it returns error, also return the error.*/ -#define CERROR_TRY_RETURN(call)\ -{\ - unsigned error = call;\ - if(error) return error;\ -} - -/*Set error var to the error code, and return from the void function.*/ -#define CERROR_RETURN(errorvar, code)\ -{\ - errorvar = code;\ - return;\ -} - -/* -About uivector, ucvector and string: --All of them wrap dynamic arrays or text strings in a similar way. --LodePNG was originally written in C++. The vectors replace the std::vectors that were used in the C++ version. --The string tools are made to avoid problems with compilers that declare things like strncat as deprecated. --They're not used in the interface, only internally in this file as static functions. --As with many other structs in this file, the init and cleanup functions serve as ctor and dtor. -*/ - -#ifdef LODEPNG_COMPILE_ZLIB -/*dynamic vector of unsigned ints*/ -typedef struct uivector -{ - unsigned* data; - size_t size; /*size in number of unsigned longs*/ - size_t allocsize; /*allocated size in bytes*/ -} uivector; - -static void uivector_cleanup(void* p) -{ - ((uivector*)p)->size = ((uivector*)p)->allocsize = 0; - lodepng_free(((uivector*)p)->data); - ((uivector*)p)->data = NULL; -} - -/*returns 1 if success, 0 if failure ==> nothing done*/ -static unsigned uivector_reserve(uivector* p, size_t allocsize) -{ - if(allocsize > p->allocsize) - { - size_t newsize = (allocsize > p->allocsize * 2) ? allocsize : (allocsize * 3 / 2); - void* data = lodepng_realloc(p->data, newsize); - if(data) - { - p->allocsize = newsize; - p->data = (unsigned*)data; - } - else return 0; /*error: not enough memory*/ - } - return 1; -} - -/*returns 1 if success, 0 if failure ==> nothing done*/ -static unsigned uivector_resize(uivector* p, size_t size) -{ - if(!uivector_reserve(p, size * sizeof(unsigned))) return 0; - p->size = size; - return 1; /*success*/ -} - -/*resize and give all new elements the value*/ -static unsigned uivector_resizev(uivector* p, size_t size, unsigned value) -{ - size_t oldsize = p->size, i; - if(!uivector_resize(p, size)) return 0; - for(i = oldsize; i < size; ++i) p->data[i] = value; - return 1; -} - -static void uivector_init(uivector* p) -{ - p->data = NULL; - p->size = p->allocsize = 0; -} - -#ifdef LODEPNG_COMPILE_ENCODER -/*returns 1 if success, 0 if failure ==> nothing done*/ -static unsigned uivector_push_back(uivector* p, unsigned c) -{ - if(!uivector_resize(p, p->size + 1)) return 0; - p->data[p->size - 1] = c; - return 1; -} -#endif /*LODEPNG_COMPILE_ENCODER*/ -#endif /*LODEPNG_COMPILE_ZLIB*/ - -/* /////////////////////////////////////////////////////////////////////////// */ - -/*dynamic vector of unsigned chars*/ -typedef struct ucvector -{ - unsigned char* data; - size_t size; /*used size*/ - size_t allocsize; /*allocated size*/ -} ucvector; - -/*returns 1 if success, 0 if failure ==> nothing done*/ -static unsigned ucvector_reserve(ucvector* p, size_t allocsize) -{ - if(allocsize > p->allocsize) - { - size_t newsize = (allocsize > p->allocsize * 2) ? allocsize : (allocsize * 3 / 2); - void* data = lodepng_realloc(p->data, newsize); - if(data) - { - p->allocsize = newsize; - p->data = (unsigned char*)data; - } - else return 0; /*error: not enough memory*/ - } - return 1; -} - -/*returns 1 if success, 0 if failure ==> nothing done*/ -static unsigned ucvector_resize(ucvector* p, size_t size) -{ - if(!ucvector_reserve(p, size * sizeof(unsigned char))) return 0; - p->size = size; - return 1; /*success*/ -} - -#ifdef LODEPNG_COMPILE_PNG - -static void ucvector_cleanup(void* p) -{ - ((ucvector*)p)->size = ((ucvector*)p)->allocsize = 0; - lodepng_free(((ucvector*)p)->data); - ((ucvector*)p)->data = NULL; -} - -static void ucvector_init(ucvector* p) -{ - p->data = NULL; - p->size = p->allocsize = 0; -} -#endif /*LODEPNG_COMPILE_PNG*/ - -#ifdef LODEPNG_COMPILE_ZLIB -/*you can both convert from vector to buffer&size and vica versa. If you use -init_buffer to take over a buffer and size, it is not needed to use cleanup*/ -static void ucvector_init_buffer(ucvector* p, unsigned char* buffer, size_t size) -{ - p->data = buffer; - p->allocsize = p->size = size; -} -#endif /*LODEPNG_COMPILE_ZLIB*/ - -#if (defined(LODEPNG_COMPILE_PNG) && defined(LODEPNG_COMPILE_ANCILLARY_CHUNKS)) || defined(LODEPNG_COMPILE_ENCODER) -/*returns 1 if success, 0 if failure ==> nothing done*/ -static unsigned ucvector_push_back(ucvector* p, unsigned char c) -{ - if(!ucvector_resize(p, p->size + 1)) return 0; - p->data[p->size - 1] = c; - return 1; -} -#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/ - - -/* ////////////////////////////////////////////////////////////////////////// */ - -#ifdef LODEPNG_COMPILE_PNG -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS -/*returns 1 if success, 0 if failure ==> nothing done*/ -static unsigned string_resize(char** out, size_t size) -{ - char* data = (char*)lodepng_realloc(*out, size + 1); - if(data) - { - data[size] = 0; /*null termination char*/ - *out = data; - } - return data != 0; -} - -/*init a {char*, size_t} pair for use as string*/ -static void string_init(char** out) -{ - *out = NULL; - string_resize(out, 0); -} - -/*free the above pair again*/ -static void string_cleanup(char** out) -{ - lodepng_free(*out); - *out = NULL; -} - -static void string_set(char** out, const char* in) -{ - size_t insize = strlen(in), i; - if(string_resize(out, insize)) - { - for(i = 0; i != insize; ++i) - { - (*out)[i] = in[i]; - } - } -} -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ -#endif /*LODEPNG_COMPILE_PNG*/ - -/* ////////////////////////////////////////////////////////////////////////// */ - -unsigned lodepng_read32bitInt(const unsigned char* buffer) -{ - return (unsigned)((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]); -} - -#if defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER) -/*buffer must have at least 4 allocated bytes available*/ -static void lodepng_set32bitInt(unsigned char* buffer, unsigned value) -{ - buffer[0] = (unsigned char)((value >> 24) & 0xff); - buffer[1] = (unsigned char)((value >> 16) & 0xff); - buffer[2] = (unsigned char)((value >> 8) & 0xff); - buffer[3] = (unsigned char)((value ) & 0xff); -} -#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/ - -#ifdef LODEPNG_COMPILE_ENCODER -static void lodepng_add32bitInt(ucvector* buffer, unsigned value) -{ - ucvector_resize(buffer, buffer->size + 4); /*todo: give error if resize failed*/ - lodepng_set32bitInt(&buffer->data[buffer->size - 4], value); -} -#endif /*LODEPNG_COMPILE_ENCODER*/ - -/* ////////////////////////////////////////////////////////////////////////// */ -/* / File IO / */ -/* ////////////////////////////////////////////////////////////////////////// */ - -#ifdef LODEPNG_COMPILE_DISK - -/* returns negative value on error. This should be pure C compatible, so no fstat. */ -static long lodepng_filesize(const char* filename) -{ - FILE* file; - long size; - file = fopen(filename, "rb"); - if(!file) return -1; - - if(fseek(file, 0, SEEK_END) != 0) - { - fclose(file); - return -1; - } - - size = ftell(file); - /* It may give LONG_MAX as directory size, this is invalid for us. */ - if(size == LONG_MAX) size = -1; - - fclose(file); - return size; -} - -/* load file into buffer that already has the correct allocated size. Returns error code.*/ -static unsigned lodepng_buffer_file(unsigned char* out, size_t size, const char* filename) -{ - FILE* file; - size_t readsize; - file = fopen(filename, "rb"); - if(!file) return 78; - - readsize = fread(out, 1, size, file); - fclose(file); - - if (readsize != size) return 78; - return 0; -} - -unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename) -{ - long size = lodepng_filesize(filename); - if (size < 0) return 78; - *outsize = (size_t)size; - - *out = (unsigned char*)lodepng_malloc((size_t)size); - if(!(*out) && size > 0) return 83; /*the above malloc failed*/ - - return lodepng_buffer_file(*out, (size_t)size, filename); -} - -/*write given buffer to the file, overwriting the file, it doesn't append to it.*/ -unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename) -{ - FILE* file; - file = fopen(filename, "wb" ); - if(!file) return 79; - fwrite((char*)buffer , 1 , buffersize, file); - fclose(file); - return 0; -} - -#endif /*LODEPNG_COMPILE_DISK*/ - -/* ////////////////////////////////////////////////////////////////////////// */ -/* ////////////////////////////////////////////////////////////////////////// */ -/* // End of common code and tools. Begin of Zlib related code. // */ -/* ////////////////////////////////////////////////////////////////////////// */ -/* ////////////////////////////////////////////////////////////////////////// */ - -#ifdef LODEPNG_COMPILE_ZLIB -#ifdef LODEPNG_COMPILE_ENCODER -/*TODO: this ignores potential out of memory errors*/ -#define addBitToStream(/*size_t**/ bitpointer, /*ucvector**/ bitstream, /*unsigned char*/ bit)\ -{\ - /*add a new byte at the end*/\ - if(((*bitpointer) & 7) == 0) ucvector_push_back(bitstream, (unsigned char)0);\ - /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/\ - (bitstream->data[bitstream->size - 1]) |= (bit << ((*bitpointer) & 0x7));\ - ++(*bitpointer);\ -} - -static void addBitsToStream(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits) -{ - size_t i; - for(i = 0; i != nbits; ++i) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> i) & 1)); -} - -static void addBitsToStreamReversed(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits) -{ - size_t i; - for(i = 0; i != nbits; ++i) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> (nbits - 1 - i)) & 1)); -} -#endif /*LODEPNG_COMPILE_ENCODER*/ - -#ifdef LODEPNG_COMPILE_DECODER - -#define READBIT(bitpointer, bitstream) ((bitstream[bitpointer >> 3] >> (bitpointer & 0x7)) & (unsigned char)1) - -static unsigned char readBitFromStream(size_t* bitpointer, const unsigned char* bitstream) -{ - unsigned char result = (unsigned char)(READBIT(*bitpointer, bitstream)); - ++(*bitpointer); - return result; -} - -static unsigned readBitsFromStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) -{ - unsigned result = 0, i; - for(i = 0; i != nbits; ++i) - { - result += ((unsigned)READBIT(*bitpointer, bitstream)) << i; - ++(*bitpointer); - } - return result; -} -#endif /*LODEPNG_COMPILE_DECODER*/ - -/* ////////////////////////////////////////////////////////////////////////// */ -/* / Deflate - Huffman / */ -/* ////////////////////////////////////////////////////////////////////////// */ - -#define FIRST_LENGTH_CODE_INDEX 257 -#define LAST_LENGTH_CODE_INDEX 285 -/*256 literals, the end code, some length codes, and 2 unused codes*/ -#define NUM_DEFLATE_CODE_SYMBOLS 288 -/*the distance codes have their own symbols, 30 used, 2 unused*/ -#define NUM_DISTANCE_SYMBOLS 32 -/*the code length codes. 0-15: code lengths, 16: copy previous 3-6 times, 17: 3-10 zeros, 18: 11-138 zeros*/ -#define NUM_CODE_LENGTH_CODES 19 - -/*the base lengths represented by codes 257-285*/ -static const unsigned LENGTHBASE[29] - = {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}; - -/*the extra bits used by codes 257-285 (added to base length)*/ -static const unsigned LENGTHEXTRA[29] - = {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}; - -/*the base backwards distances (the bits of distance codes appear after length codes and use their own huffman tree)*/ -static const unsigned DISTANCEBASE[30] - = {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}; - -/*the extra bits of backwards distances (added to base)*/ -static const unsigned DISTANCEEXTRA[30] - = {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}; - -/*the order in which "code length alphabet code lengths" are stored, out of this -the huffman tree of the dynamic huffman tree lengths is generated*/ -static const unsigned CLCL_ORDER[NUM_CODE_LENGTH_CODES] - = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; - -/* ////////////////////////////////////////////////////////////////////////// */ - -/* -Huffman tree struct, containing multiple representations of the tree -*/ -typedef struct HuffmanTree -{ - unsigned* tree2d; - unsigned* tree1d; - unsigned* lengths; /*the lengths of the codes of the 1d-tree*/ - unsigned maxbitlen; /*maximum number of bits a single code can get*/ - unsigned numcodes; /*number of symbols in the alphabet = number of codes*/ -} HuffmanTree; - -/*function used for debug purposes to draw the tree in ascii art with C++*/ -/* -static void HuffmanTree_draw(HuffmanTree* tree) -{ - std::cout << "tree. length: " << tree->numcodes << " maxbitlen: " << tree->maxbitlen << std::endl; - for(size_t i = 0; i != tree->tree1d.size; ++i) - { - if(tree->lengths.data[i]) - std::cout << i << " " << tree->tree1d.data[i] << " " << tree->lengths.data[i] << std::endl; - } - std::cout << std::endl; -}*/ - -static void HuffmanTree_init(HuffmanTree* tree) -{ - tree->tree2d = 0; - tree->tree1d = 0; - tree->lengths = 0; -} - -static void HuffmanTree_cleanup(HuffmanTree* tree) -{ - lodepng_free(tree->tree2d); - lodepng_free(tree->tree1d); - lodepng_free(tree->lengths); -} - -/*the tree representation used by the decoder. return value is error*/ -static unsigned HuffmanTree_make2DTree(HuffmanTree* tree) -{ - unsigned nodefilled = 0; /*up to which node it is filled*/ - unsigned treepos = 0; /*position in the tree (1 of the numcodes columns)*/ - unsigned n, i; - - tree->tree2d = (unsigned*)lodepng_malloc(tree->numcodes * 2 * sizeof(unsigned)); - if(!tree->tree2d) return 83; /*alloc fail*/ - - /* - convert tree1d[] to tree2d[][]. In the 2D array, a value of 32767 means - uninited, a value >= numcodes is an address to another bit, a value < numcodes - is a code. The 2 rows are the 2 possible bit values (0 or 1), there are as - many columns as codes - 1. - A good huffman tree has N * 2 - 1 nodes, of which N - 1 are internal nodes. - Here, the internal nodes are stored (what their 0 and 1 option point to). - There is only memory for such good tree currently, if there are more nodes - (due to too long length codes), error 55 will happen - */ - for(n = 0; n < tree->numcodes * 2; ++n) - { - tree->tree2d[n] = 32767; /*32767 here means the tree2d isn't filled there yet*/ - } - - for(n = 0; n < tree->numcodes; ++n) /*the codes*/ - { - for(i = 0; i != tree->lengths[n]; ++i) /*the bits for this code*/ - { - unsigned char bit = (unsigned char)((tree->tree1d[n] >> (tree->lengths[n] - i - 1)) & 1); - /*oversubscribed, see comment in lodepng_error_text*/ - if(treepos > 2147483647 || treepos + 2 > tree->numcodes) return 55; - if(tree->tree2d[2 * treepos + bit] == 32767) /*not yet filled in*/ - { - if(i + 1 == tree->lengths[n]) /*last bit*/ - { - tree->tree2d[2 * treepos + bit] = n; /*put the current code in it*/ - treepos = 0; - } - else - { - /*put address of the next step in here, first that address has to be found of course - (it's just nodefilled + 1)...*/ - ++nodefilled; - /*addresses encoded with numcodes added to it*/ - tree->tree2d[2 * treepos + bit] = nodefilled + tree->numcodes; - treepos = nodefilled; - } - } - else treepos = tree->tree2d[2 * treepos + bit] - tree->numcodes; - } - } - - for(n = 0; n < tree->numcodes * 2; ++n) - { - if(tree->tree2d[n] == 32767) tree->tree2d[n] = 0; /*remove possible remaining 32767's*/ - } - - return 0; -} - -/* -Second step for the ...makeFromLengths and ...makeFromFrequencies functions. -numcodes, lengths and maxbitlen must already be filled in correctly. return -value is error. -*/ -static unsigned HuffmanTree_makeFromLengths2(HuffmanTree* tree) -{ - uivector blcount; - uivector nextcode; - unsigned error = 0; - unsigned bits, n; - - uivector_init(&blcount); - uivector_init(&nextcode); - - tree->tree1d = (unsigned*)lodepng_malloc(tree->numcodes * sizeof(unsigned)); - if(!tree->tree1d) error = 83; /*alloc fail*/ - - if(!uivector_resizev(&blcount, tree->maxbitlen + 1, 0) - || !uivector_resizev(&nextcode, tree->maxbitlen + 1, 0)) - error = 83; /*alloc fail*/ - - if(!error) - { - /*step 1: count number of instances of each code length*/ - for(bits = 0; bits != tree->numcodes; ++bits) ++blcount.data[tree->lengths[bits]]; - /*step 2: generate the nextcode values*/ - for(bits = 1; bits <= tree->maxbitlen; ++bits) - { - nextcode.data[bits] = (nextcode.data[bits - 1] + blcount.data[bits - 1]) << 1; - } - /*step 3: generate all the codes*/ - for(n = 0; n != tree->numcodes; ++n) - { - if(tree->lengths[n] != 0) tree->tree1d[n] = nextcode.data[tree->lengths[n]]++; - } - } - - uivector_cleanup(&blcount); - uivector_cleanup(&nextcode); - - if(!error) return HuffmanTree_make2DTree(tree); - else return error; -} - -/* -given the code lengths (as stored in the PNG file), generate the tree as defined -by Deflate. maxbitlen is the maximum bits that a code in the tree can have. -return value is error. -*/ -static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* bitlen, - size_t numcodes, unsigned maxbitlen) -{ - unsigned i; - tree->lengths = (unsigned*)lodepng_malloc(numcodes * sizeof(unsigned)); - if(!tree->lengths) return 83; /*alloc fail*/ - for(i = 0; i != numcodes; ++i) tree->lengths[i] = bitlen[i]; - tree->numcodes = (unsigned)numcodes; /*number of symbols*/ - tree->maxbitlen = maxbitlen; - return HuffmanTree_makeFromLengths2(tree); -} - -#ifdef LODEPNG_COMPILE_ENCODER - -/*BPM: Boundary Package Merge, see "A Fast and Space-Economical Algorithm for Length-Limited Coding", -Jyrki Katajainen, Alistair Moffat, Andrew Turpin, 1995.*/ - -/*chain node for boundary package merge*/ -typedef struct BPMNode -{ - int weight; /*the sum of all weights in this chain*/ - unsigned index; /*index of this leaf node (called "count" in the paper)*/ - struct BPMNode* tail; /*the next nodes in this chain (null if last)*/ - int in_use; -} BPMNode; - -/*lists of chains*/ -typedef struct BPMLists -{ - /*memory pool*/ - unsigned memsize; - BPMNode* memory; - unsigned numfree; - unsigned nextfree; - BPMNode** freelist; - /*two heads of lookahead chains per list*/ - unsigned listsize; - BPMNode** chains0; - BPMNode** chains1; -} BPMLists; - -/*creates a new chain node with the given parameters, from the memory in the lists */ -static BPMNode* bpmnode_create(BPMLists* lists, int weight, unsigned index, BPMNode* tail) -{ - unsigned i; - BPMNode* result; - - /*memory full, so garbage collect*/ - if(lists->nextfree >= lists->numfree) - { - /*mark only those that are in use*/ - for(i = 0; i != lists->memsize; ++i) lists->memory[i].in_use = 0; - for(i = 0; i != lists->listsize; ++i) - { - BPMNode* node; - for(node = lists->chains0[i]; node != 0; node = node->tail) node->in_use = 1; - for(node = lists->chains1[i]; node != 0; node = node->tail) node->in_use = 1; - } - /*collect those that are free*/ - lists->numfree = 0; - for(i = 0; i != lists->memsize; ++i) - { - if(!lists->memory[i].in_use) lists->freelist[lists->numfree++] = &lists->memory[i]; - } - lists->nextfree = 0; - } - - result = lists->freelist[lists->nextfree++]; - result->weight = weight; - result->index = index; - result->tail = tail; - return result; -} - -/*sort the leaves with stable mergesort*/ -static void bpmnode_sort(BPMNode* leaves, size_t num) -{ - BPMNode* mem = (BPMNode*)lodepng_malloc(sizeof(*leaves) * num); - size_t width, counter = 0; - for(width = 1; width < num; width *= 2) - { - BPMNode* a = (counter & 1) ? mem : leaves; - BPMNode* b = (counter & 1) ? leaves : mem; - size_t p; - for(p = 0; p < num; p += 2 * width) - { - size_t q = (p + width > num) ? num : (p + width); - size_t r = (p + 2 * width > num) ? num : (p + 2 * width); - size_t i = p, j = q, k; - for(k = p; k < r; k++) - { - if(i < q && (j >= r || a[i].weight <= a[j].weight)) b[k] = a[i++]; - else b[k] = a[j++]; - } - } - counter++; - } - if(counter & 1) memcpy(leaves, mem, sizeof(*leaves) * num); - lodepng_free(mem); -} - -/*Boundary Package Merge step, numpresent is the amount of leaves, and c is the current chain.*/ -static void boundaryPM(BPMLists* lists, BPMNode* leaves, size_t numpresent, int c, int num) -{ - unsigned lastindex = lists->chains1[c]->index; - - if(c == 0) - { - if(lastindex >= numpresent) return; - lists->chains0[c] = lists->chains1[c]; - lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, 0); - } - else - { - /*sum of the weights of the head nodes of the previous lookahead chains.*/ - int sum = lists->chains0[c - 1]->weight + lists->chains1[c - 1]->weight; - lists->chains0[c] = lists->chains1[c]; - if(lastindex < numpresent && sum > leaves[lastindex].weight) - { - lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, lists->chains1[c]->tail); - return; - } - lists->chains1[c] = bpmnode_create(lists, sum, lastindex, lists->chains1[c - 1]); - /*in the end we are only interested in the chain of the last list, so no - need to recurse if we're at the last one (this gives measurable speedup)*/ - if(num + 1 < (int)(2 * numpresent - 2)) - { - boundaryPM(lists, leaves, numpresent, c - 1, num); - boundaryPM(lists, leaves, numpresent, c - 1, num); - } - } -} - -unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies, - size_t numcodes, unsigned maxbitlen) -{ - unsigned error = 0; - unsigned i; - size_t numpresent = 0; /*number of symbols with non-zero frequency*/ - BPMNode* leaves; /*the symbols, only those with > 0 frequency*/ - - if(numcodes == 0) return 80; /*error: a tree of 0 symbols is not supposed to be made*/ - if((1u << maxbitlen) < (unsigned)numcodes) return 80; /*error: represent all symbols*/ - - leaves = (BPMNode*)lodepng_malloc(numcodes * sizeof(*leaves)); - if(!leaves) return 83; /*alloc fail*/ - - for(i = 0; i != numcodes; ++i) - { - if(frequencies[i] > 0) - { - leaves[numpresent].weight = (int)frequencies[i]; - leaves[numpresent].index = i; - ++numpresent; - } - } - - for(i = 0; i != numcodes; ++i) lengths[i] = 0; - - /*ensure at least two present symbols. There should be at least one symbol - according to RFC 1951 section 3.2.7. Some decoders incorrectly require two. To - make these work as well ensure there are at least two symbols. The - Package-Merge code below also doesn't work correctly if there's only one - symbol, it'd give it the theoritical 0 bits but in practice zlib wants 1 bit*/ - if(numpresent == 0) - { - lengths[0] = lengths[1] = 1; /*note that for RFC 1951 section 3.2.7, only lengths[0] = 1 is needed*/ - } - else if(numpresent == 1) - { - lengths[leaves[0].index] = 1; - lengths[leaves[0].index == 0 ? 1 : 0] = 1; - } - else - { - BPMLists lists; - BPMNode* node; - - bpmnode_sort(leaves, numpresent); - - lists.listsize = maxbitlen; - lists.memsize = 2 * maxbitlen * (maxbitlen + 1); - lists.nextfree = 0; - lists.numfree = lists.memsize; - lists.memory = (BPMNode*)lodepng_malloc(lists.memsize * sizeof(*lists.memory)); - lists.freelist = (BPMNode**)lodepng_malloc(lists.memsize * sizeof(BPMNode*)); - lists.chains0 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); - lists.chains1 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); - if(!lists.memory || !lists.freelist || !lists.chains0 || !lists.chains1) error = 83; /*alloc fail*/ - - if(!error) - { - for(i = 0; i != lists.memsize; ++i) lists.freelist[i] = &lists.memory[i]; - - bpmnode_create(&lists, leaves[0].weight, 1, 0); - bpmnode_create(&lists, leaves[1].weight, 2, 0); - - for(i = 0; i != lists.listsize; ++i) - { - lists.chains0[i] = &lists.memory[0]; - lists.chains1[i] = &lists.memory[1]; - } - - /*each boundaryPM call adds one chain to the last list, and we need 2 * numpresent - 2 chains.*/ - for(i = 2; i != 2 * numpresent - 2; ++i) boundaryPM(&lists, leaves, numpresent, (int)maxbitlen - 1, (int)i); - - for(node = lists.chains1[maxbitlen - 1]; node; node = node->tail) - { - for(i = 0; i != node->index; ++i) ++lengths[leaves[i].index]; - } - } - - lodepng_free(lists.memory); - lodepng_free(lists.freelist); - lodepng_free(lists.chains0); - lodepng_free(lists.chains1); - } - - lodepng_free(leaves); - return error; -} - -/*Create the Huffman tree given the symbol frequencies*/ -static unsigned HuffmanTree_makeFromFrequencies(HuffmanTree* tree, const unsigned* frequencies, - size_t mincodes, size_t numcodes, unsigned maxbitlen) -{ - unsigned error = 0; - while(!frequencies[numcodes - 1] && numcodes > mincodes) --numcodes; /*trim zeroes*/ - tree->maxbitlen = maxbitlen; - tree->numcodes = (unsigned)numcodes; /*number of symbols*/ - tree->lengths = (unsigned*)lodepng_realloc(tree->lengths, numcodes * sizeof(unsigned)); - if(!tree->lengths) return 83; /*alloc fail*/ - /*initialize all lengths to 0*/ - memset(tree->lengths, 0, numcodes * sizeof(unsigned)); - - error = lodepng_huffman_code_lengths(tree->lengths, frequencies, numcodes, maxbitlen); - if(!error) error = HuffmanTree_makeFromLengths2(tree); - return error; -} - -static unsigned HuffmanTree_getCode(const HuffmanTree* tree, unsigned index) -{ - return tree->tree1d[index]; -} - -static unsigned HuffmanTree_getLength(const HuffmanTree* tree, unsigned index) -{ - return tree->lengths[index]; -} -#endif /*LODEPNG_COMPILE_ENCODER*/ - -/*get the literal and length code tree of a deflated block with fixed tree, as per the deflate specification*/ -static unsigned generateFixedLitLenTree(HuffmanTree* tree) -{ - unsigned i, error = 0; - unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); - if(!bitlen) return 83; /*alloc fail*/ - - /*288 possible codes: 0-255=literals, 256=endcode, 257-285=lengthcodes, 286-287=unused*/ - for(i = 0; i <= 143; ++i) bitlen[i] = 8; - for(i = 144; i <= 255; ++i) bitlen[i] = 9; - for(i = 256; i <= 279; ++i) bitlen[i] = 7; - for(i = 280; i <= 287; ++i) bitlen[i] = 8; - - error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15); - - lodepng_free(bitlen); - return error; -} - -/*get the distance code tree of a deflated block with fixed tree, as specified in the deflate specification*/ -static unsigned generateFixedDistanceTree(HuffmanTree* tree) -{ - unsigned i, error = 0; - unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); - if(!bitlen) return 83; /*alloc fail*/ - - /*there are 32 distance codes, but 30-31 are unused*/ - for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen[i] = 5; - error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DISTANCE_SYMBOLS, 15); - - lodepng_free(bitlen); - return error; -} - -#ifdef LODEPNG_COMPILE_DECODER - -/* -returns the code, or (unsigned)(-1) if error happened -inbitlength is the length of the complete buffer, in bits (so its byte length times 8) -*/ -static unsigned huffmanDecodeSymbol(const unsigned char* in, size_t* bp, - const HuffmanTree* codetree, size_t inbitlength) -{ - unsigned treepos = 0, ct; - for(;;) - { - if(*bp >= inbitlength) return (unsigned)(-1); /*error: end of input memory reached without endcode*/ - /* - decode the symbol from the tree. The "readBitFromStream" code is inlined in - the expression below because this is the biggest bottleneck while decoding - */ - ct = codetree->tree2d[(treepos << 1) + READBIT(*bp, in)]; - ++(*bp); - if(ct < codetree->numcodes) return ct; /*the symbol is decoded, return it*/ - else treepos = ct - codetree->numcodes; /*symbol not yet decoded, instead move tree position*/ - - if(treepos >= codetree->numcodes) return (unsigned)(-1); /*error: it appeared outside the codetree*/ - } -} -#endif /*LODEPNG_COMPILE_DECODER*/ - -#ifdef LODEPNG_COMPILE_DECODER - -/* ////////////////////////////////////////////////////////////////////////// */ -/* / Inflator (Decompressor) / */ -/* ////////////////////////////////////////////////////////////////////////// */ - -/*get the tree of a deflated block with fixed tree, as specified in the deflate specification*/ -static void getTreeInflateFixed(HuffmanTree* tree_ll, HuffmanTree* tree_d) -{ - /*TODO: check for out of memory errors*/ - generateFixedLitLenTree(tree_ll); - generateFixedDistanceTree(tree_d); -} - -/*get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/ -static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d, - const unsigned char* in, size_t* bp, size_t inlength) -{ - /*make sure that length values that aren't filled in will be 0, or a wrong tree will be generated*/ - unsigned error = 0; - unsigned n, HLIT, HDIST, HCLEN, i; - size_t inbitlength = inlength * 8; - - /*see comments in deflateDynamic for explanation of the context and these variables, it is analogous*/ - unsigned* bitlen_ll = 0; /*lit,len code lengths*/ - unsigned* bitlen_d = 0; /*dist code lengths*/ - /*code length code lengths ("clcl"), the bit lengths of the huffman tree used to compress bitlen_ll and bitlen_d*/ - unsigned* bitlen_cl = 0; - HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/ - - if((*bp) + 14 > (inlength << 3)) return 49; /*error: the bit pointer is or will go past the memory*/ - - /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/ - HLIT = readBitsFromStream(bp, in, 5) + 257; - /*number of distance codes. Unlike the spec, the value 1 is added to it here already*/ - HDIST = readBitsFromStream(bp, in, 5) + 1; - /*number of code length codes. Unlike the spec, the value 4 is added to it here already*/ - HCLEN = readBitsFromStream(bp, in, 4) + 4; - - if((*bp) + HCLEN * 3 > (inlength << 3)) return 50; /*error: the bit pointer is or will go past the memory*/ - - HuffmanTree_init(&tree_cl); - - while(!error) - { - /*read the code length codes out of 3 * (amount of code length codes) bits*/ - - bitlen_cl = (unsigned*)lodepng_malloc(NUM_CODE_LENGTH_CODES * sizeof(unsigned)); - if(!bitlen_cl) ERROR_BREAK(83 /*alloc fail*/); - - for(i = 0; i != NUM_CODE_LENGTH_CODES; ++i) - { - if(i < HCLEN) bitlen_cl[CLCL_ORDER[i]] = readBitsFromStream(bp, in, 3); - else bitlen_cl[CLCL_ORDER[i]] = 0; /*if not, it must stay 0*/ - } - - error = HuffmanTree_makeFromLengths(&tree_cl, bitlen_cl, NUM_CODE_LENGTH_CODES, 7); - if(error) break; - - /*now we can use this tree to read the lengths for the tree that this function will return*/ - bitlen_ll = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); - bitlen_d = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); - if(!bitlen_ll || !bitlen_d) ERROR_BREAK(83 /*alloc fail*/); - for(i = 0; i != NUM_DEFLATE_CODE_SYMBOLS; ++i) bitlen_ll[i] = 0; - for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen_d[i] = 0; - - /*i is the current symbol we're reading in the part that contains the code lengths of lit/len and dist codes*/ - i = 0; - while(i < HLIT + HDIST) - { - unsigned code = huffmanDecodeSymbol(in, bp, &tree_cl, inbitlength); - if(code <= 15) /*a length code*/ - { - if(i < HLIT) bitlen_ll[i] = code; - else bitlen_d[i - HLIT] = code; - ++i; - } - else if(code == 16) /*repeat previous*/ - { - unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/ - unsigned value; /*set value to the previous code*/ - - if(i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/ - - if((*bp + 2) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ - replength += readBitsFromStream(bp, in, 2); - - if(i < HLIT + 1) value = bitlen_ll[i - 1]; - else value = bitlen_d[i - HLIT - 1]; - /*repeat this value in the next lengths*/ - for(n = 0; n < replength; ++n) - { - if(i >= HLIT + HDIST) ERROR_BREAK(13); /*error: i is larger than the amount of codes*/ - if(i < HLIT) bitlen_ll[i] = value; - else bitlen_d[i - HLIT] = value; - ++i; - } - } - else if(code == 17) /*repeat "0" 3-10 times*/ - { - unsigned replength = 3; /*read in the bits that indicate repeat length*/ - if((*bp + 3) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ - replength += readBitsFromStream(bp, in, 3); - - /*repeat this value in the next lengths*/ - for(n = 0; n < replength; ++n) - { - if(i >= HLIT + HDIST) ERROR_BREAK(14); /*error: i is larger than the amount of codes*/ - - if(i < HLIT) bitlen_ll[i] = 0; - else bitlen_d[i - HLIT] = 0; - ++i; - } - } - else if(code == 18) /*repeat "0" 11-138 times*/ - { - unsigned replength = 11; /*read in the bits that indicate repeat length*/ - if((*bp + 7) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ - replength += readBitsFromStream(bp, in, 7); - - /*repeat this value in the next lengths*/ - for(n = 0; n < replength; ++n) - { - if(i >= HLIT + HDIST) ERROR_BREAK(15); /*error: i is larger than the amount of codes*/ - - if(i < HLIT) bitlen_ll[i] = 0; - else bitlen_d[i - HLIT] = 0; - ++i; - } - } - else /*if(code == (unsigned)(-1))*/ /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ - { - if(code == (unsigned)(-1)) - { - /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol - (10=no endcode, 11=wrong jump outside of tree)*/ - error = (*bp) > inbitlength ? 10 : 11; - } - else error = 16; /*unexisting code, this can never happen*/ - break; - } - } - if(error) break; - - if(bitlen_ll[256] == 0) ERROR_BREAK(64); /*the length of the end code 256 must be larger than 0*/ - - /*now we've finally got HLIT and HDIST, so generate the code trees, and the function is done*/ - error = HuffmanTree_makeFromLengths(tree_ll, bitlen_ll, NUM_DEFLATE_CODE_SYMBOLS, 15); - if(error) break; - error = HuffmanTree_makeFromLengths(tree_d, bitlen_d, NUM_DISTANCE_SYMBOLS, 15); - - break; /*end of error-while*/ - } - - lodepng_free(bitlen_cl); - lodepng_free(bitlen_ll); - lodepng_free(bitlen_d); - HuffmanTree_cleanup(&tree_cl); - - return error; -} - -/*inflate a block with dynamic of fixed Huffman tree*/ -static unsigned inflateHuffmanBlock(ucvector* out, const unsigned char* in, size_t* bp, - size_t* pos, size_t inlength, unsigned btype) -{ - unsigned error = 0; - HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/ - HuffmanTree tree_d; /*the huffman tree for distance codes*/ - size_t inbitlength = inlength * 8; - - HuffmanTree_init(&tree_ll); - HuffmanTree_init(&tree_d); - - if(btype == 1) getTreeInflateFixed(&tree_ll, &tree_d); - else if(btype == 2) error = getTreeInflateDynamic(&tree_ll, &tree_d, in, bp, inlength); - - while(!error) /*decode all symbols until end reached, breaks at end code*/ - { - /*code_ll is literal, length or end code*/ - unsigned code_ll = huffmanDecodeSymbol(in, bp, &tree_ll, inbitlength); - if(code_ll <= 255) /*literal symbol*/ - { - /*ucvector_push_back would do the same, but for some reason the two lines below run 10% faster*/ - if(!ucvector_resize(out, (*pos) + 1)) ERROR_BREAK(83 /*alloc fail*/); - out->data[*pos] = (unsigned char)code_ll; - ++(*pos); - } - else if(code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ - { - unsigned code_d, distance; - unsigned numextrabits_l, numextrabits_d; /*extra bits for length and distance*/ - size_t start, forward, backward, length; - - /*part 1: get length base*/ - length = LENGTHBASE[code_ll - FIRST_LENGTH_CODE_INDEX]; - - /*part 2: get extra bits and add the value of that to length*/ - numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX]; - if((*bp + numextrabits_l) > inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ - length += readBitsFromStream(bp, in, numextrabits_l); - - /*part 3: get distance code*/ - code_d = huffmanDecodeSymbol(in, bp, &tree_d, inbitlength); - if(code_d > 29) - { - if(code_d == (unsigned)(-1)) /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ - { - /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol - (10=no endcode, 11=wrong jump outside of tree)*/ - error = (*bp) > inlength * 8 ? 10 : 11; - } - else error = 18; /*error: invalid distance code (30-31 are never used)*/ - break; - } - distance = DISTANCEBASE[code_d]; - - /*part 4: get extra bits from distance*/ - numextrabits_d = DISTANCEEXTRA[code_d]; - if((*bp + numextrabits_d) > inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ - distance += readBitsFromStream(bp, in, numextrabits_d); - - /*part 5: fill in all the out[n] values based on the length and dist*/ - start = (*pos); - if(distance > start) ERROR_BREAK(52); /*too long backward distance*/ - backward = start - distance; - - if(!ucvector_resize(out, (*pos) + length)) ERROR_BREAK(83 /*alloc fail*/); - if (distance < length) { - for(forward = 0; forward < length; ++forward) - { - out->data[(*pos)++] = out->data[backward++]; - } - } else { - memcpy(out->data + *pos, out->data + backward, length); - *pos += length; - } - } - else if(code_ll == 256) - { - break; /*end code, break the loop*/ - } - else /*if(code == (unsigned)(-1))*/ /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ - { - /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol - (10=no endcode, 11=wrong jump outside of tree)*/ - error = ((*bp) > inlength * 8) ? 10 : 11; - break; - } - } - - HuffmanTree_cleanup(&tree_ll); - HuffmanTree_cleanup(&tree_d); - - return error; -} - -static unsigned inflateNoCompression(ucvector* out, const unsigned char* in, size_t* bp, size_t* pos, size_t inlength) -{ - size_t p; - unsigned LEN, NLEN, n, error = 0; - - /*go to first boundary of byte*/ - while(((*bp) & 0x7) != 0) ++(*bp); - p = (*bp) / 8; /*byte position*/ - - /*read LEN (2 bytes) and NLEN (2 bytes)*/ - if(p + 4 >= inlength) return 52; /*error, bit pointer will jump past memory*/ - LEN = in[p] + 256u * in[p + 1]; p += 2; - NLEN = in[p] + 256u * in[p + 1]; p += 2; - - /*check if 16-bit NLEN is really the one's complement of LEN*/ - if(LEN + NLEN != 65535) return 21; /*error: NLEN is not one's complement of LEN*/ - - if(!ucvector_resize(out, (*pos) + LEN)) return 83; /*alloc fail*/ - - /*read the literal data: LEN bytes are now stored in the out buffer*/ - if(p + LEN > inlength) return 23; /*error: reading outside of in buffer*/ - for(n = 0; n < LEN; ++n) out->data[(*pos)++] = in[p++]; - - (*bp) = p * 8; - - return error; -} - -static unsigned lodepng_inflatev(ucvector* out, - const unsigned char* in, size_t insize, - const LodePNGDecompressSettings* settings) -{ - /*bit pointer in the "in" data, current byte is bp >> 3, current bit is bp & 0x7 (from lsb to msb of the byte)*/ - size_t bp = 0; - unsigned BFINAL = 0; - size_t pos = 0; /*byte position in the out buffer*/ - unsigned error = 0; - - (void)settings; - - while(!BFINAL) - { - unsigned BTYPE; - if(bp + 2 >= insize * 8) return 52; /*error, bit pointer will jump past memory*/ - BFINAL = readBitFromStream(&bp, in); - BTYPE = 1u * readBitFromStream(&bp, in); - BTYPE += 2u * readBitFromStream(&bp, in); - - if(BTYPE == 3) return 20; /*error: invalid BTYPE*/ - else if(BTYPE == 0) error = inflateNoCompression(out, in, &bp, &pos, insize); /*no compression*/ - else error = inflateHuffmanBlock(out, in, &bp, &pos, insize, BTYPE); /*compression, BTYPE 01 or 10*/ - - if(error) return error; - } - - return error; -} - -unsigned lodepng_inflate(unsigned char** out, size_t* outsize, - const unsigned char* in, size_t insize, - const LodePNGDecompressSettings* settings) -{ - unsigned error; - ucvector v; - ucvector_init_buffer(&v, *out, *outsize); - error = lodepng_inflatev(&v, in, insize, settings); - *out = v.data; - *outsize = v.size; - return error; -} - -static unsigned inflate(unsigned char** out, size_t* outsize, - const unsigned char* in, size_t insize, - const LodePNGDecompressSettings* settings) -{ - if(settings->custom_inflate) - { - return settings->custom_inflate(out, outsize, in, insize, settings); - } - else - { - return lodepng_inflate(out, outsize, in, insize, settings); - } -} - -#endif /*LODEPNG_COMPILE_DECODER*/ - -#ifdef LODEPNG_COMPILE_ENCODER - -/* ////////////////////////////////////////////////////////////////////////// */ -/* / Deflator (Compressor) / */ -/* ////////////////////////////////////////////////////////////////////////// */ - -static const size_t MAX_SUPPORTED_DEFLATE_LENGTH = 258; - -/*bitlen is the size in bits of the code*/ -static void addHuffmanSymbol(size_t* bp, ucvector* compressed, unsigned code, unsigned bitlen) -{ - addBitsToStreamReversed(bp, compressed, code, bitlen); -} - -/*search the index in the array, that has the largest value smaller than or equal to the given value, -given array must be sorted (if no value is smaller, it returns the size of the given array)*/ -static size_t searchCodeIndex(const unsigned* array, size_t array_size, size_t value) -{ - /*binary search (only small gain over linear). TODO: use CPU log2 instruction for getting symbols instead*/ - size_t left = 1; - size_t right = array_size - 1; - - while(left <= right) { - size_t mid = (left + right) >> 1; - if (array[mid] >= value) right = mid - 1; - else left = mid + 1; - } - if(left >= array_size || array[left] > value) left--; - return left; -} - -static void addLengthDistance(uivector* values, size_t length, size_t distance) -{ - /*values in encoded vector are those used by deflate: - 0-255: literal bytes - 256: end - 257-285: length/distance pair (length code, followed by extra length bits, distance code, extra distance bits) - 286-287: invalid*/ - - unsigned length_code = (unsigned)searchCodeIndex(LENGTHBASE, 29, length); - unsigned extra_length = (unsigned)(length - LENGTHBASE[length_code]); - unsigned dist_code = (unsigned)searchCodeIndex(DISTANCEBASE, 30, distance); - unsigned extra_distance = (unsigned)(distance - DISTANCEBASE[dist_code]); - - uivector_push_back(values, length_code + FIRST_LENGTH_CODE_INDEX); - uivector_push_back(values, extra_length); - uivector_push_back(values, dist_code); - uivector_push_back(values, extra_distance); -} - -/*3 bytes of data get encoded into two bytes. The hash cannot use more than 3 -bytes as input because 3 is the minimum match length for deflate*/ -static const unsigned HASH_NUM_VALUES = 65536; -static const unsigned HASH_BIT_MASK = 65535; /*HASH_NUM_VALUES - 1, but C90 does not like that as initializer*/ - -typedef struct Hash -{ - int* head; /*hash value to head circular pos - can be outdated if went around window*/ - /*circular pos to prev circular pos*/ - unsigned short* chain; - int* val; /*circular pos to hash value*/ - - /*TODO: do this not only for zeros but for any repeated byte. However for PNG - it's always going to be the zeros that dominate, so not important for PNG*/ - int* headz; /*similar to head, but for chainz*/ - unsigned short* chainz; /*those with same amount of zeros*/ - unsigned short* zeros; /*length of zeros streak, used as a second hash chain*/ -} Hash; - -static unsigned hash_init(Hash* hash, unsigned windowsize) -{ - unsigned i; - hash->head = (int*)lodepng_malloc(sizeof(int) * HASH_NUM_VALUES); - hash->val = (int*)lodepng_malloc(sizeof(int) * windowsize); - hash->chain = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); - - hash->zeros = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); - hash->headz = (int*)lodepng_malloc(sizeof(int) * (MAX_SUPPORTED_DEFLATE_LENGTH + 1)); - hash->chainz = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); - - if(!hash->head || !hash->chain || !hash->val || !hash->headz|| !hash->chainz || !hash->zeros) - { - return 83; /*alloc fail*/ - } - - /*initialize hash table*/ - for(i = 0; i != HASH_NUM_VALUES; ++i) hash->head[i] = -1; - for(i = 0; i != windowsize; ++i) hash->val[i] = -1; - for(i = 0; i != windowsize; ++i) hash->chain[i] = i; /*same value as index indicates uninitialized*/ - - for(i = 0; i <= MAX_SUPPORTED_DEFLATE_LENGTH; ++i) hash->headz[i] = -1; - for(i = 0; i != windowsize; ++i) hash->chainz[i] = i; /*same value as index indicates uninitialized*/ - - return 0; -} - -static void hash_cleanup(Hash* hash) -{ - lodepng_free(hash->head); - lodepng_free(hash->val); - lodepng_free(hash->chain); - - lodepng_free(hash->zeros); - lodepng_free(hash->headz); - lodepng_free(hash->chainz); -} - - - -static unsigned getHash(const unsigned char* data, size_t size, size_t pos) -{ - unsigned result = 0; - if(pos + 2 < size) - { - /*A simple shift and xor hash is used. Since the data of PNGs is dominated - by zeroes due to the filters, a better hash does not have a significant - effect on speed in traversing the chain, and causes more time spend on - calculating the hash.*/ - result ^= (unsigned)(data[pos + 0] << 0u); - result ^= (unsigned)(data[pos + 1] << 4u); - result ^= (unsigned)(data[pos + 2] << 8u); - } else { - size_t amount, i; - if(pos >= size) return 0; - amount = size - pos; - for(i = 0; i != amount; ++i) result ^= (unsigned)(data[pos + i] << (i * 8u)); - } - return result & HASH_BIT_MASK; -} - -static unsigned countZeros(const unsigned char* data, size_t size, size_t pos) -{ - const unsigned char* start = data + pos; - const unsigned char* end = start + MAX_SUPPORTED_DEFLATE_LENGTH; - if(end > data + size) end = data + size; - data = start; - while(data != end && *data == 0) ++data; - /*subtracting two addresses returned as 32-bit number (max value is MAX_SUPPORTED_DEFLATE_LENGTH)*/ - return (unsigned)(data - start); -} - -/*wpos = pos & (windowsize - 1)*/ -static void updateHashChain(Hash* hash, size_t wpos, unsigned hashval, unsigned short numzeros) -{ - hash->val[wpos] = (int)hashval; - if(hash->head[hashval] != -1) hash->chain[wpos] = hash->head[hashval]; - hash->head[hashval] = (unsigned)wpos; - - hash->zeros[wpos] = numzeros; - if(hash->headz[numzeros] != -1) hash->chainz[wpos] = hash->headz[numzeros]; - hash->headz[numzeros] = (unsigned)wpos; -} - -/* -LZ77-encode the data. Return value is error code. The input are raw bytes, the output -is in the form of unsigned integers with codes representing for example literal bytes, or -length/distance pairs. -It uses a hash table technique to let it encode faster. When doing LZ77 encoding, a -sliding window (of windowsize) is used, and all past bytes in that window can be used as -the "dictionary". A brute force search through all possible distances would be slow, and -this hash technique is one out of several ways to speed this up. -*/ -static unsigned encodeLZ77(uivector* out, Hash* hash, - const unsigned char* in, size_t inpos, size_t insize, unsigned windowsize, - unsigned minmatch, unsigned nicematch, unsigned lazymatching) -{ - size_t pos; - unsigned i, error = 0; - /*for large window lengths, assume the user wants no compression loss. Otherwise, max hash chain length speedup.*/ - unsigned maxchainlength = windowsize >= 8192 ? windowsize : windowsize / 8; - unsigned maxlazymatch = windowsize >= 8192 ? MAX_SUPPORTED_DEFLATE_LENGTH : 64; - - unsigned usezeros = 1; /*not sure if setting it to false for windowsize < 8192 is better or worse*/ - unsigned numzeros = 0; - - unsigned offset; /*the offset represents the distance in LZ77 terminology*/ - unsigned length; - unsigned lazy = 0; - unsigned lazylength = 0, lazyoffset = 0; - unsigned hashval; - unsigned current_offset, current_length; - unsigned prev_offset; - const unsigned char *lastptr, *foreptr, *backptr; - unsigned hashpos; - - if(windowsize == 0 || windowsize > 32768) return 60; /*error: windowsize smaller/larger than allowed*/ - if((windowsize & (windowsize - 1)) != 0) return 90; /*error: must be power of two*/ - - if(nicematch > MAX_SUPPORTED_DEFLATE_LENGTH) nicematch = MAX_SUPPORTED_DEFLATE_LENGTH; - - for(pos = inpos; pos < insize; ++pos) - { - size_t wpos = pos & (windowsize - 1); /*position for in 'circular' hash buffers*/ - unsigned chainlength = 0; - - hashval = getHash(in, insize, pos); - - if(usezeros && hashval == 0) - { - if(numzeros == 0) numzeros = countZeros(in, insize, pos); - else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; - } - else - { - numzeros = 0; - } - - updateHashChain(hash, wpos, hashval, numzeros); - - /*the length and offset found for the current position*/ - length = 0; - offset = 0; - - hashpos = hash->chain[wpos]; - - lastptr = &in[insize < pos + MAX_SUPPORTED_DEFLATE_LENGTH ? insize : pos + MAX_SUPPORTED_DEFLATE_LENGTH]; - - /*search for the longest string*/ - prev_offset = 0; - for(;;) - { - if(chainlength++ >= maxchainlength) break; - current_offset = (unsigned)(hashpos <= wpos ? wpos - hashpos : wpos - hashpos + windowsize); - - if(current_offset < prev_offset) break; /*stop when went completely around the circular buffer*/ - prev_offset = current_offset; - if(current_offset > 0) - { - /*test the next characters*/ - foreptr = &in[pos]; - backptr = &in[pos - current_offset]; - - /*common case in PNGs is lots of zeros. Quickly skip over them as a speedup*/ - if(numzeros >= 3) - { - unsigned skip = hash->zeros[hashpos]; - if(skip > numzeros) skip = numzeros; - backptr += skip; - foreptr += skip; - } - - while(foreptr != lastptr && *backptr == *foreptr) /*maximum supported length by deflate is max length*/ - { - ++backptr; - ++foreptr; - } - current_length = (unsigned)(foreptr - &in[pos]); - - if(current_length > length) - { - length = current_length; /*the longest length*/ - offset = current_offset; /*the offset that is related to this longest length*/ - /*jump out once a length of max length is found (speed gain). This also jumps - out if length is MAX_SUPPORTED_DEFLATE_LENGTH*/ - if(current_length >= nicematch) break; - } - } - - if(hashpos == hash->chain[hashpos]) break; - - if(numzeros >= 3 && length > numzeros) - { - hashpos = hash->chainz[hashpos]; - if(hash->zeros[hashpos] != numzeros) break; - } - else - { - hashpos = hash->chain[hashpos]; - /*outdated hash value, happens if particular value was not encountered in whole last window*/ - if(hash->val[hashpos] != (int)hashval) break; - } - } - - if(lazymatching) - { - if(!lazy && length >= 3 && length <= maxlazymatch && length < MAX_SUPPORTED_DEFLATE_LENGTH) - { - lazy = 1; - lazylength = length; - lazyoffset = offset; - continue; /*try the next byte*/ - } - if(lazy) - { - lazy = 0; - if(pos == 0) ERROR_BREAK(81); - if(length > lazylength + 1) - { - /*push the previous character as literal*/ - if(!uivector_push_back(out, in[pos - 1])) ERROR_BREAK(83 /*alloc fail*/); - } - else - { - length = lazylength; - offset = lazyoffset; - hash->head[hashval] = -1; /*the same hashchain update will be done, this ensures no wrong alteration*/ - hash->headz[numzeros] = -1; /*idem*/ - --pos; - } - } - } - if(length >= 3 && offset > windowsize) ERROR_BREAK(86 /*too big (or overflown negative) offset*/); - - /*encode it as length/distance pair or literal value*/ - if(length < 3) /*only lengths of 3 or higher are supported as length/distance pair*/ - { - if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/); - } - else if(length < minmatch || (length == 3 && offset > 4096)) - { - /*compensate for the fact that longer offsets have more extra bits, a - length of only 3 may be not worth it then*/ - if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/); - } - else - { - addLengthDistance(out, length, offset); - for(i = 1; i < length; ++i) - { - ++pos; - wpos = pos & (windowsize - 1); - hashval = getHash(in, insize, pos); - if(usezeros && hashval == 0) - { - if(numzeros == 0) numzeros = countZeros(in, insize, pos); - else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; - } - else - { - numzeros = 0; - } - updateHashChain(hash, wpos, hashval, numzeros); - } - } - } /*end of the loop through each character of input*/ - - return error; -} - -/* /////////////////////////////////////////////////////////////////////////// */ - -static unsigned deflateNoCompression(ucvector* out, const unsigned char* data, size_t datasize) -{ - /*non compressed deflate block data: 1 bit BFINAL,2 bits BTYPE,(5 bits): it jumps to start of next byte, - 2 bytes LEN, 2 bytes NLEN, LEN bytes literal DATA*/ - - size_t i, j, numdeflateblocks = (datasize + 65534) / 65535; - unsigned datapos = 0; - for(i = 0; i != numdeflateblocks; ++i) - { - unsigned BFINAL, BTYPE, LEN, NLEN; - unsigned char firstbyte; - - BFINAL = (i == numdeflateblocks - 1); - BTYPE = 0; - - firstbyte = (unsigned char)(BFINAL + ((BTYPE & 1) << 1) + ((BTYPE & 2) << 1)); - ucvector_push_back(out, firstbyte); - - LEN = 65535; - if(datasize - datapos < 65535) LEN = (unsigned)datasize - datapos; - NLEN = 65535 - LEN; - - ucvector_push_back(out, (unsigned char)(LEN & 255)); - ucvector_push_back(out, (unsigned char)(LEN >> 8)); - ucvector_push_back(out, (unsigned char)(NLEN & 255)); - ucvector_push_back(out, (unsigned char)(NLEN >> 8)); - - /*Decompressed data*/ - for(j = 0; j < 65535 && datapos < datasize; ++j) - { - ucvector_push_back(out, data[datapos++]); - } - } - - return 0; -} - -/* -write the lz77-encoded data, which has lit, len and dist codes, to compressed stream using huffman trees. -tree_ll: the tree for lit and len codes. -tree_d: the tree for distance codes. -*/ -static void writeLZ77data(size_t* bp, ucvector* out, const uivector* lz77_encoded, - const HuffmanTree* tree_ll, const HuffmanTree* tree_d) -{ - size_t i = 0; - for(i = 0; i != lz77_encoded->size; ++i) - { - unsigned val = lz77_encoded->data[i]; - addHuffmanSymbol(bp, out, HuffmanTree_getCode(tree_ll, val), HuffmanTree_getLength(tree_ll, val)); - if(val > 256) /*for a length code, 3 more things have to be added*/ - { - unsigned length_index = val - FIRST_LENGTH_CODE_INDEX; - unsigned n_length_extra_bits = LENGTHEXTRA[length_index]; - unsigned length_extra_bits = lz77_encoded->data[++i]; - - unsigned distance_code = lz77_encoded->data[++i]; - - unsigned distance_index = distance_code; - unsigned n_distance_extra_bits = DISTANCEEXTRA[distance_index]; - unsigned distance_extra_bits = lz77_encoded->data[++i]; - - addBitsToStream(bp, out, length_extra_bits, n_length_extra_bits); - addHuffmanSymbol(bp, out, HuffmanTree_getCode(tree_d, distance_code), - HuffmanTree_getLength(tree_d, distance_code)); - addBitsToStream(bp, out, distance_extra_bits, n_distance_extra_bits); - } - } -} - -/*Deflate for a block of type "dynamic", that is, with freely, optimally, created huffman trees*/ -static unsigned deflateDynamic(ucvector* out, size_t* bp, Hash* hash, - const unsigned char* data, size_t datapos, size_t dataend, - const LodePNGCompressSettings* settings, unsigned final) -{ - unsigned error = 0; - - /* - A block is compressed as follows: The PNG data is lz77 encoded, resulting in - literal bytes and length/distance pairs. This is then huffman compressed with - two huffman trees. One huffman tree is used for the lit and len values ("ll"), - another huffman tree is used for the dist values ("d"). These two trees are - stored using their code lengths, and to compress even more these code lengths - are also run-length encoded and huffman compressed. This gives a huffman tree - of code lengths "cl". The code lenghts used to describe this third tree are - the code length code lengths ("clcl"). - */ - - /*The lz77 encoded data, represented with integers since there will also be length and distance codes in it*/ - uivector lz77_encoded; - HuffmanTree tree_ll; /*tree for lit,len values*/ - HuffmanTree tree_d; /*tree for distance codes*/ - HuffmanTree tree_cl; /*tree for encoding the code lengths representing tree_ll and tree_d*/ - uivector frequencies_ll; /*frequency of lit,len codes*/ - uivector frequencies_d; /*frequency of dist codes*/ - uivector frequencies_cl; /*frequency of code length codes*/ - uivector bitlen_lld; /*lit,len,dist code lenghts (int bits), literally (without repeat codes).*/ - uivector bitlen_lld_e; /*bitlen_lld encoded with repeat codes (this is a rudemtary run length compression)*/ - /*bitlen_cl is the code length code lengths ("clcl"). The bit lengths of codes to represent tree_cl - (these are written as is in the file, it would be crazy to compress these using yet another huffman - tree that needs to be represented by yet another set of code lengths)*/ - uivector bitlen_cl; - size_t datasize = dataend - datapos; - - /* - Due to the huffman compression of huffman tree representations ("two levels"), there are some anologies: - bitlen_lld is to tree_cl what data is to tree_ll and tree_d. - bitlen_lld_e is to bitlen_lld what lz77_encoded is to data. - bitlen_cl is to bitlen_lld_e what bitlen_lld is to lz77_encoded. - */ - - unsigned BFINAL = final; - size_t numcodes_ll, numcodes_d, i; - unsigned HLIT, HDIST, HCLEN; - - uivector_init(&lz77_encoded); - HuffmanTree_init(&tree_ll); - HuffmanTree_init(&tree_d); - HuffmanTree_init(&tree_cl); - uivector_init(&frequencies_ll); - uivector_init(&frequencies_d); - uivector_init(&frequencies_cl); - uivector_init(&bitlen_lld); - uivector_init(&bitlen_lld_e); - uivector_init(&bitlen_cl); - - /*This while loop never loops due to a break at the end, it is here to - allow breaking out of it to the cleanup phase on error conditions.*/ - while(!error) - { - if(settings->use_lz77) - { - error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, - settings->minmatch, settings->nicematch, settings->lazymatching); - if(error) break; - } - else - { - if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83 /*alloc fail*/); - for(i = datapos; i < dataend; ++i) lz77_encoded.data[i - datapos] = data[i]; /*no LZ77, but still will be Huffman compressed*/ - } - - if(!uivector_resizev(&frequencies_ll, 286, 0)) ERROR_BREAK(83 /*alloc fail*/); - if(!uivector_resizev(&frequencies_d, 30, 0)) ERROR_BREAK(83 /*alloc fail*/); - - /*Count the frequencies of lit, len and dist codes*/ - for(i = 0; i != lz77_encoded.size; ++i) - { - unsigned symbol = lz77_encoded.data[i]; - ++frequencies_ll.data[symbol]; - if(symbol > 256) - { - unsigned dist = lz77_encoded.data[i + 2]; - ++frequencies_d.data[dist]; - i += 3; - } - } - frequencies_ll.data[256] = 1; /*there will be exactly 1 end code, at the end of the block*/ - - /*Make both huffman trees, one for the lit and len codes, one for the dist codes*/ - error = HuffmanTree_makeFromFrequencies(&tree_ll, frequencies_ll.data, 257, frequencies_ll.size, 15); - if(error) break; - /*2, not 1, is chosen for mincodes: some buggy PNG decoders require at least 2 symbols in the dist tree*/ - error = HuffmanTree_makeFromFrequencies(&tree_d, frequencies_d.data, 2, frequencies_d.size, 15); - if(error) break; - - numcodes_ll = tree_ll.numcodes; if(numcodes_ll > 286) numcodes_ll = 286; - numcodes_d = tree_d.numcodes; if(numcodes_d > 30) numcodes_d = 30; - /*store the code lengths of both generated trees in bitlen_lld*/ - for(i = 0; i != numcodes_ll; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_ll, (unsigned)i)); - for(i = 0; i != numcodes_d; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_d, (unsigned)i)); - - /*run-length compress bitlen_ldd into bitlen_lld_e by using repeat codes 16 (copy length 3-6 times), - 17 (3-10 zeroes), 18 (11-138 zeroes)*/ - for(i = 0; i != (unsigned)bitlen_lld.size; ++i) - { - unsigned j = 0; /*amount of repititions*/ - while(i + j + 1 < (unsigned)bitlen_lld.size && bitlen_lld.data[i + j + 1] == bitlen_lld.data[i]) ++j; - - if(bitlen_lld.data[i] == 0 && j >= 2) /*repeat code for zeroes*/ - { - ++j; /*include the first zero*/ - if(j <= 10) /*repeat code 17 supports max 10 zeroes*/ - { - uivector_push_back(&bitlen_lld_e, 17); - uivector_push_back(&bitlen_lld_e, j - 3); - } - else /*repeat code 18 supports max 138 zeroes*/ - { - if(j > 138) j = 138; - uivector_push_back(&bitlen_lld_e, 18); - uivector_push_back(&bitlen_lld_e, j - 11); - } - i += (j - 1); - } - else if(j >= 3) /*repeat code for value other than zero*/ - { - size_t k; - unsigned num = j / 6, rest = j % 6; - uivector_push_back(&bitlen_lld_e, bitlen_lld.data[i]); - for(k = 0; k < num; ++k) - { - uivector_push_back(&bitlen_lld_e, 16); - uivector_push_back(&bitlen_lld_e, 6 - 3); - } - if(rest >= 3) - { - uivector_push_back(&bitlen_lld_e, 16); - uivector_push_back(&bitlen_lld_e, rest - 3); - } - else j -= rest; - i += j; - } - else /*too short to benefit from repeat code*/ - { - uivector_push_back(&bitlen_lld_e, bitlen_lld.data[i]); - } - } - - /*generate tree_cl, the huffmantree of huffmantrees*/ - - if(!uivector_resizev(&frequencies_cl, NUM_CODE_LENGTH_CODES, 0)) ERROR_BREAK(83 /*alloc fail*/); - for(i = 0; i != bitlen_lld_e.size; ++i) - { - ++frequencies_cl.data[bitlen_lld_e.data[i]]; - /*after a repeat code come the bits that specify the number of repetitions, - those don't need to be in the frequencies_cl calculation*/ - if(bitlen_lld_e.data[i] >= 16) ++i; - } - - error = HuffmanTree_makeFromFrequencies(&tree_cl, frequencies_cl.data, - frequencies_cl.size, frequencies_cl.size, 7); - if(error) break; - - if(!uivector_resize(&bitlen_cl, tree_cl.numcodes)) ERROR_BREAK(83 /*alloc fail*/); - for(i = 0; i != tree_cl.numcodes; ++i) - { - /*lenghts of code length tree is in the order as specified by deflate*/ - bitlen_cl.data[i] = HuffmanTree_getLength(&tree_cl, CLCL_ORDER[i]); - } - while(bitlen_cl.data[bitlen_cl.size - 1] == 0 && bitlen_cl.size > 4) - { - /*remove zeros at the end, but minimum size must be 4*/ - if(!uivector_resize(&bitlen_cl, bitlen_cl.size - 1)) ERROR_BREAK(83 /*alloc fail*/); - } - if(error) break; - - /* - Write everything into the output - - After the BFINAL and BTYPE, the dynamic block consists out of the following: - - 5 bits HLIT, 5 bits HDIST, 4 bits HCLEN - - (HCLEN+4)*3 bits code lengths of code length alphabet - - HLIT + 257 code lenghts of lit/length alphabet (encoded using the code length - alphabet, + possible repetition codes 16, 17, 18) - - HDIST + 1 code lengths of distance alphabet (encoded using the code length - alphabet, + possible repetition codes 16, 17, 18) - - compressed data - - 256 (end code) - */ - - /*Write block type*/ - addBitToStream(bp, out, BFINAL); - addBitToStream(bp, out, 0); /*first bit of BTYPE "dynamic"*/ - addBitToStream(bp, out, 1); /*second bit of BTYPE "dynamic"*/ - - /*write the HLIT, HDIST and HCLEN values*/ - HLIT = (unsigned)(numcodes_ll - 257); - HDIST = (unsigned)(numcodes_d - 1); - HCLEN = (unsigned)bitlen_cl.size - 4; - /*trim zeroes for HCLEN. HLIT and HDIST were already trimmed at tree creation*/ - while(!bitlen_cl.data[HCLEN + 4 - 1] && HCLEN > 0) --HCLEN; - addBitsToStream(bp, out, HLIT, 5); - addBitsToStream(bp, out, HDIST, 5); - addBitsToStream(bp, out, HCLEN, 4); - - /*write the code lenghts of the code length alphabet*/ - for(i = 0; i != HCLEN + 4; ++i) addBitsToStream(bp, out, bitlen_cl.data[i], 3); - - /*write the lenghts of the lit/len AND the dist alphabet*/ - for(i = 0; i != bitlen_lld_e.size; ++i) - { - addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_cl, bitlen_lld_e.data[i]), - HuffmanTree_getLength(&tree_cl, bitlen_lld_e.data[i])); - /*extra bits of repeat codes*/ - if(bitlen_lld_e.data[i] == 16) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 2); - else if(bitlen_lld_e.data[i] == 17) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 3); - else if(bitlen_lld_e.data[i] == 18) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 7); - } - - /*write the compressed data symbols*/ - writeLZ77data(bp, out, &lz77_encoded, &tree_ll, &tree_d); - /*error: the length of the end code 256 must be larger than 0*/ - if(HuffmanTree_getLength(&tree_ll, 256) == 0) ERROR_BREAK(64); - - /*write the end code*/ - addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, 256), HuffmanTree_getLength(&tree_ll, 256)); - - break; /*end of error-while*/ - } - - /*cleanup*/ - uivector_cleanup(&lz77_encoded); - HuffmanTree_cleanup(&tree_ll); - HuffmanTree_cleanup(&tree_d); - HuffmanTree_cleanup(&tree_cl); - uivector_cleanup(&frequencies_ll); - uivector_cleanup(&frequencies_d); - uivector_cleanup(&frequencies_cl); - uivector_cleanup(&bitlen_lld_e); - uivector_cleanup(&bitlen_lld); - uivector_cleanup(&bitlen_cl); - - return error; -} - -static unsigned deflateFixed(ucvector* out, size_t* bp, Hash* hash, - const unsigned char* data, - size_t datapos, size_t dataend, - const LodePNGCompressSettings* settings, unsigned final) -{ - HuffmanTree tree_ll; /*tree for literal values and length codes*/ - HuffmanTree tree_d; /*tree for distance codes*/ - - unsigned BFINAL = final; - unsigned error = 0; - size_t i; - - HuffmanTree_init(&tree_ll); - HuffmanTree_init(&tree_d); - - generateFixedLitLenTree(&tree_ll); - generateFixedDistanceTree(&tree_d); - - addBitToStream(bp, out, BFINAL); - addBitToStream(bp, out, 1); /*first bit of BTYPE*/ - addBitToStream(bp, out, 0); /*second bit of BTYPE*/ - - if(settings->use_lz77) /*LZ77 encoded*/ - { - uivector lz77_encoded; - uivector_init(&lz77_encoded); - error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, - settings->minmatch, settings->nicematch, settings->lazymatching); - if(!error) writeLZ77data(bp, out, &lz77_encoded, &tree_ll, &tree_d); - uivector_cleanup(&lz77_encoded); - } - else /*no LZ77, but still will be Huffman compressed*/ - { - for(i = datapos; i < dataend; ++i) - { - addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, data[i]), HuffmanTree_getLength(&tree_ll, data[i])); - } - } - /*add END code*/ - if(!error) addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, 256), HuffmanTree_getLength(&tree_ll, 256)); - - /*cleanup*/ - HuffmanTree_cleanup(&tree_ll); - HuffmanTree_cleanup(&tree_d); - - return error; -} - -static unsigned lodepng_deflatev(ucvector* out, const unsigned char* in, size_t insize, - const LodePNGCompressSettings* settings) -{ - unsigned error = 0; - size_t i, blocksize, numdeflateblocks; - size_t bp = 0; /*the bit pointer*/ - Hash hash; - - if(settings->btype > 2) return 61; - else if(settings->btype == 0) return deflateNoCompression(out, in, insize); - else if(settings->btype == 1) blocksize = insize; - else /*if(settings->btype == 2)*/ - { - /*on PNGs, deflate blocks of 65-262k seem to give most dense encoding*/ - blocksize = insize / 8 + 8; - if(blocksize < 65536) blocksize = 65536; - if(blocksize > 262144) blocksize = 262144; - } - - numdeflateblocks = (insize + blocksize - 1) / blocksize; - if(numdeflateblocks == 0) numdeflateblocks = 1; - - error = hash_init(&hash, settings->windowsize); - if(error) return error; - - for(i = 0; i != numdeflateblocks && !error; ++i) - { - unsigned final = (i == numdeflateblocks - 1); - size_t start = i * blocksize; - size_t end = start + blocksize; - if(end > insize) end = insize; - - if(settings->btype == 1) error = deflateFixed(out, &bp, &hash, in, start, end, settings, final); - else if(settings->btype == 2) error = deflateDynamic(out, &bp, &hash, in, start, end, settings, final); - } - - hash_cleanup(&hash); - - return error; -} - -unsigned lodepng_deflate(unsigned char** out, size_t* outsize, - const unsigned char* in, size_t insize, - const LodePNGCompressSettings* settings) -{ - unsigned error; - ucvector v; - ucvector_init_buffer(&v, *out, *outsize); - error = lodepng_deflatev(&v, in, insize, settings); - *out = v.data; - *outsize = v.size; - return error; -} - -static unsigned deflate(unsigned char** out, size_t* outsize, - const unsigned char* in, size_t insize, - const LodePNGCompressSettings* settings) -{ - if(settings->custom_deflate) - { - return settings->custom_deflate(out, outsize, in, insize, settings); - } - else - { - return lodepng_deflate(out, outsize, in, insize, settings); - } -} - -#endif /*LODEPNG_COMPILE_DECODER*/ - -/* ////////////////////////////////////////////////////////////////////////// */ -/* / Adler32 */ -/* ////////////////////////////////////////////////////////////////////////// */ - -static unsigned update_adler32(unsigned adler, const unsigned char* data, unsigned len) -{ - unsigned s1 = adler & 0xffff; - unsigned s2 = (adler >> 16) & 0xffff; - - while(len > 0) - { - /*at least 5550 sums can be done before the sums overflow, saving a lot of module divisions*/ - unsigned amount = len > 5550 ? 5550 : len; - len -= amount; - while(amount > 0) - { - s1 += (*data++); - s2 += s1; - --amount; - } - s1 %= 65521; - s2 %= 65521; - } - - return (s2 << 16) | s1; -} - -/*Return the adler32 of the bytes data[0..len-1]*/ -static unsigned adler32(const unsigned char* data, unsigned len) -{ - return update_adler32(1L, data, len); -} - -/* ////////////////////////////////////////////////////////////////////////// */ -/* / Zlib / */ -/* ////////////////////////////////////////////////////////////////////////// */ - -#ifdef LODEPNG_COMPILE_DECODER - -unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, - size_t insize, const LodePNGDecompressSettings* settings) -{ - unsigned error = 0; - unsigned CM, CINFO, FDICT; - - if(insize < 2) return 53; /*error, size of zlib data too small*/ - /*read information from zlib header*/ - if((in[0] * 256 + in[1]) % 31 != 0) - { - /*error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way*/ - return 24; - } - - CM = in[0] & 15; - CINFO = (in[0] >> 4) & 15; - /*FCHECK = in[1] & 31;*/ /*FCHECK is already tested above*/ - FDICT = (in[1] >> 5) & 1; - /*FLEVEL = (in[1] >> 6) & 3;*/ /*FLEVEL is not used here*/ - - if(CM != 8 || CINFO > 7) - { - /*error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec*/ - return 25; - } - if(FDICT != 0) - { - /*error: the specification of PNG says about the zlib stream: - "The additional flags shall not specify a preset dictionary."*/ - return 26; - } - - error = inflate(out, outsize, in + 2, insize - 2, settings); - if(error) return error; - - if(!settings->ignore_adler32) - { - unsigned ADLER32 = lodepng_read32bitInt(&in[insize - 4]); - unsigned checksum = adler32(*out, (unsigned)(*outsize)); - if(checksum != ADLER32) return 58; /*error, adler checksum not correct, data must be corrupted*/ - } - - return 0; /*no error*/ -} - -static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, - size_t insize, const LodePNGDecompressSettings* settings) -{ - if(settings->custom_zlib) - { - return settings->custom_zlib(out, outsize, in, insize, settings); - } - else - { - return lodepng_zlib_decompress(out, outsize, in, insize, settings); - } -} - -#endif /*LODEPNG_COMPILE_DECODER*/ - -#ifdef LODEPNG_COMPILE_ENCODER - -unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, - size_t insize, const LodePNGCompressSettings* settings) -{ - /*initially, *out must be NULL and outsize 0, if you just give some random *out - that's pointing to a non allocated buffer, this'll crash*/ - ucvector outv; - size_t i; - unsigned error; - unsigned char* deflatedata = 0; - size_t deflatesize = 0; - - /*zlib data: 1 byte CMF (CM+CINFO), 1 byte FLG, deflate data, 4 byte ADLER32 checksum of the Decompressed data*/ - unsigned CMF = 120; /*0b01111000: CM 8, CINFO 7. With CINFO 7, any window size up to 32768 can be used.*/ - unsigned FLEVEL = 0; - unsigned FDICT = 0; - unsigned CMFFLG = 256 * CMF + FDICT * 32 + FLEVEL * 64; - unsigned FCHECK = 31 - CMFFLG % 31; - CMFFLG += FCHECK; - - /*ucvector-controlled version of the output buffer, for dynamic array*/ - ucvector_init_buffer(&outv, *out, *outsize); - - ucvector_push_back(&outv, (unsigned char)(CMFFLG >> 8)); - ucvector_push_back(&outv, (unsigned char)(CMFFLG & 255)); - - error = deflate(&deflatedata, &deflatesize, in, insize, settings); - - if(!error) - { - unsigned ADLER32 = adler32(in, (unsigned)insize); - for(i = 0; i != deflatesize; ++i) ucvector_push_back(&outv, deflatedata[i]); - lodepng_free(deflatedata); - lodepng_add32bitInt(&outv, ADLER32); - } - - *out = outv.data; - *outsize = outv.size; - - return error; -} - -/* compress using the default or custom zlib function */ -static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, - size_t insize, const LodePNGCompressSettings* settings) -{ - if(settings->custom_zlib) - { - return settings->custom_zlib(out, outsize, in, insize, settings); - } - else - { - return lodepng_zlib_compress(out, outsize, in, insize, settings); - } -} - -#endif /*LODEPNG_COMPILE_ENCODER*/ - -#else /*no LODEPNG_COMPILE_ZLIB*/ - -#ifdef LODEPNG_COMPILE_DECODER -static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, - size_t insize, const LodePNGDecompressSettings* settings) -{ - if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ - return settings->custom_zlib(out, outsize, in, insize, settings); -} -#endif /*LODEPNG_COMPILE_DECODER*/ -#ifdef LODEPNG_COMPILE_ENCODER -static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, - size_t insize, const LodePNGCompressSettings* settings) -{ - if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ - return settings->custom_zlib(out, outsize, in, insize, settings); -} -#endif /*LODEPNG_COMPILE_ENCODER*/ - -#endif /*LODEPNG_COMPILE_ZLIB*/ - -/* ////////////////////////////////////////////////////////////////////////// */ - -#ifdef LODEPNG_COMPILE_ENCODER - -/*this is a good tradeoff between speed and compression ratio*/ -#define DEFAULT_WINDOWSIZE 2048 - -void lodepng_compress_settings_init(LodePNGCompressSettings* settings) -{ - /*compress with dynamic huffman tree (not in the mathematical sense, just not the predefined one)*/ - settings->btype = 2; - settings->use_lz77 = 1; - settings->windowsize = DEFAULT_WINDOWSIZE; - settings->minmatch = 3; - settings->nicematch = 128; - settings->lazymatching = 1; - - settings->custom_zlib = 0; - settings->custom_deflate = 0; - settings->custom_context = 0; -} - -const LodePNGCompressSettings lodepng_default_compress_settings = {2, 1, DEFAULT_WINDOWSIZE, 3, 128, 1, 0, 0, 0}; - - -#endif /*LODEPNG_COMPILE_ENCODER*/ - -#ifdef LODEPNG_COMPILE_DECODER - -void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) -{ - settings->ignore_adler32 = 0; - - settings->custom_zlib = 0; - settings->custom_inflate = 0; - settings->custom_context = 0; -} - -const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0}; - -#endif /*LODEPNG_COMPILE_DECODER*/ - -/* ////////////////////////////////////////////////////////////////////////// */ -/* ////////////////////////////////////////////////////////////////////////// */ -/* // End of Zlib related code. Begin of PNG related code. // */ -/* ////////////////////////////////////////////////////////////////////////// */ -/* ////////////////////////////////////////////////////////////////////////// */ - -#ifdef LODEPNG_COMPILE_PNG - -/* ////////////////////////////////////////////////////////////////////////// */ -/* / CRC32 / */ -/* ////////////////////////////////////////////////////////////////////////// */ - - -#ifndef LODEPNG_NO_COMPILE_CRC -/* CRC polynomial: 0xedb88320 */ -static unsigned lodepng_crc32_table[256] = { - 0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u, 3915621685u, 2657392035u, - 249268274u, 2044508324u, 3772115230u, 2547177864u, 162941995u, 2125561021u, 3887607047u, 2428444049u, - 498536548u, 1789927666u, 4089016648u, 2227061214u, 450548861u, 1843258603u, 4107580753u, 2211677639u, - 325883990u, 1684777152u, 4251122042u, 2321926636u, 335633487u, 1661365465u, 4195302755u, 2366115317u, - 997073096u, 1281953886u, 3579855332u, 2724688242u, 1006888145u, 1258607687u, 3524101629u, 2768942443u, - 901097722u, 1119000684u, 3686517206u, 2898065728u, 853044451u, 1172266101u, 3705015759u, 2882616665u, - 651767980u, 1373503546u, 3369554304u, 3218104598u, 565507253u, 1454621731u, 3485111705u, 3099436303u, - 671266974u, 1594198024u, 3322730930u, 2970347812u, 795835527u, 1483230225u, 3244367275u, 3060149565u, - 1994146192u, 31158534u, 2563907772u, 4023717930u, 1907459465u, 112637215u, 2680153253u, 3904427059u, - 2013776290u, 251722036u, 2517215374u, 3775830040u, 2137656763u, 141376813u, 2439277719u, 3865271297u, - 1802195444u, 476864866u, 2238001368u, 4066508878u, 1812370925u, 453092731u, 2181625025u, 4111451223u, - 1706088902u, 314042704u, 2344532202u, 4240017532u, 1658658271u, 366619977u, 2362670323u, 4224994405u, - 1303535960u, 984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u, 2765210733u, 3554079995u, - 1131014506u, 879679996u, 2909243462u, 3663771856u, 1141124467u, 855842277u, 2852801631u, 3708648649u, - 1342533948u, 654459306u, 3188396048u, 3373015174u, 1466479909u, 544179635u, 3110523913u, 3462522015u, - 1591671054u, 702138776u, 2966460450u, 3352799412u, 1504918807u, 783551873u, 3082640443u, 3233442989u, - 3988292384u, 2596254646u, 62317068u, 1957810842u, 3939845945u, 2647816111u, 81470997u, 1943803523u, - 3814918930u, 2489596804u, 225274430u, 2053790376u, 3826175755u, 2466906013u, 167816743u, 2097651377u, - 4027552580u, 2265490386u, 503444072u, 1762050814u, 4150417245u, 2154129355u, 426522225u, 1852507879u, - 4275313526u, 2312317920u, 282753626u, 1742555852u, 4189708143u, 2394877945u, 397917763u, 1622183637u, - 3604390888u, 2714866558u, 953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u, - 3624741850u, 2936675148u, 906185462u, 1090812512u, 3747672003u, 2825379669u, 829329135u, 1181335161u, - 3412177804u, 3160834842u, 628085408u, 1382605366u, 3423369109u, 3138078467u, 570562233u, 1426400815u, - 3317316542u, 2998733608u, 733239954u, 1555261956u, 3268935591u, 3050360625u, 752459403u, 1541320221u, - 2607071920u, 3965973030u, 1969922972u, 40735498u, 2617837225u, 3943577151u, 1913087877u, 83908371u, - 2512341634u, 3803740692u, 2075208622u, 213261112u, 2463272603u, 3855990285u, 2094854071u, 198958881u, - 2262029012u, 4057260610u, 1759359992u, 534414190u, 2176718541u, 4139329115u, 1873836001u, 414664567u, - 2282248934u, 4279200368u, 1711684554u, 285281116u, 2405801727u, 4167216745u, 1634467795u, 376229701u, - 2685067896u, 3608007406u, 1308918612u, 956543938u, 2808555105u, 3495958263u, 1231636301u, 1047427035u, - 2932959818u, 3654703836u, 1088359270u, 936918000u, 2847714899u, 3736837829u, 1202900863u, 817233897u, - 3183342108u, 3401237130u, 1404277552u, 615818150u, 3134207493u, 3453421203u, 1423857449u, 601450431u, - 3009837614u, 3294710456u, 1567103746u, 711928724u, 3020668471u, 3272380065u, 1510334235u, 755167117u -}; - -/*Return the CRC of the bytes buf[0..len-1].*/ -unsigned lodepng_crc32(const unsigned char* data, size_t length) -{ - unsigned r = 0xffffffffu; - size_t i; - for(i = 0; i < length; ++i) - { - r = lodepng_crc32_table[(r ^ data[i]) & 0xff] ^ (r >> 8); - } - return r ^ 0xffffffffu; -} -#else /* !LODEPNG_NO_COMPILE_CRC */ -unsigned lodepng_crc32(const unsigned char* data, size_t length); -#endif /* !LODEPNG_NO_COMPILE_CRC */ - -/* ////////////////////////////////////////////////////////////////////////// */ -/* / Reading and writing single bits and bytes from/to stream for LodePNG / */ -/* ////////////////////////////////////////////////////////////////////////// */ - -static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream) -{ - unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1); - ++(*bitpointer); - return result; -} - -static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) -{ - unsigned result = 0; - size_t i; - for(i = 0 ; i < nbits; ++i) - { - result <<= 1; - result |= (unsigned)readBitFromReversedStream(bitpointer, bitstream); - } - return result; -} - -#ifdef LODEPNG_COMPILE_DECODER -static void setBitOfReversedStream0(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) -{ - /*the current bit in bitstream must be 0 for this to work*/ - if(bit) - { - /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/ - bitstream[(*bitpointer) >> 3] |= (bit << (7 - ((*bitpointer) & 0x7))); - } - ++(*bitpointer); -} -#endif /*LODEPNG_COMPILE_DECODER*/ - -static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) -{ - /*the current bit in bitstream may be 0 or 1 for this to work*/ - if(bit == 0) bitstream[(*bitpointer) >> 3] &= (unsigned char)(~(1 << (7 - ((*bitpointer) & 0x7)))); - else bitstream[(*bitpointer) >> 3] |= (1 << (7 - ((*bitpointer) & 0x7))); - ++(*bitpointer); -} - -/* ////////////////////////////////////////////////////////////////////////// */ -/* / PNG chunks / */ -/* ////////////////////////////////////////////////////////////////////////// */ - -unsigned lodepng_chunk_length(const unsigned char* chunk) -{ - return lodepng_read32bitInt(&chunk[0]); -} - -void lodepng_chunk_type(char type[5], const unsigned char* chunk) -{ - unsigned i; - for(i = 0; i != 4; ++i) type[i] = (char)chunk[4 + i]; - type[4] = 0; /*null termination char*/ -} - -unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type) -{ - if(strlen(type) != 4) return 0; - return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]); -} - -unsigned char lodepng_chunk_ancillary(const unsigned char* chunk) -{ - return((chunk[4] & 32) != 0); -} - -unsigned char lodepng_chunk_private(const unsigned char* chunk) -{ - return((chunk[6] & 32) != 0); -} - -unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk) -{ - return((chunk[7] & 32) != 0); -} - -unsigned char* lodepng_chunk_data(unsigned char* chunk) -{ - return &chunk[8]; -} - -const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk) -{ - return &chunk[8]; -} - -unsigned lodepng_chunk_check_crc(const unsigned char* chunk) -{ - unsigned length = lodepng_chunk_length(chunk); - unsigned CRC = lodepng_read32bitInt(&chunk[length + 8]); - /*the CRC is taken of the data and the 4 chunk type letters, not the length*/ - unsigned checksum = lodepng_crc32(&chunk[4], length + 4); - if(CRC != checksum) return 1; - else return 0; -} - -void lodepng_chunk_generate_crc(unsigned char* chunk) -{ - unsigned length = lodepng_chunk_length(chunk); - unsigned CRC = lodepng_crc32(&chunk[4], length + 4); - lodepng_set32bitInt(chunk + 8 + length, CRC); -} - -unsigned char* lodepng_chunk_next(unsigned char* chunk) -{ - unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; - return &chunk[total_chunk_length]; -} - -const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk) -{ - unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; - return &chunk[total_chunk_length]; -} - -unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk) -{ - unsigned i; - unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; - unsigned char *chunk_start, *new_buffer; - size_t new_length = (*outlength) + total_chunk_length; - if(new_length < total_chunk_length || new_length < (*outlength)) return 77; /*integer overflow happened*/ - - new_buffer = (unsigned char*)lodepng_realloc(*out, new_length); - if(!new_buffer) return 83; /*alloc fail*/ - (*out) = new_buffer; - (*outlength) = new_length; - chunk_start = &(*out)[new_length - total_chunk_length]; - - for(i = 0; i != total_chunk_length; ++i) chunk_start[i] = chunk[i]; - - return 0; -} - -unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, - const char* type, const unsigned char* data) -{ - unsigned i; - unsigned char *chunk, *new_buffer; - size_t new_length = (*outlength) + length + 12; - if(new_length < length + 12 || new_length < (*outlength)) return 77; /*integer overflow happened*/ - new_buffer = (unsigned char*)lodepng_realloc(*out, new_length); - if(!new_buffer) return 83; /*alloc fail*/ - (*out) = new_buffer; - (*outlength) = new_length; - chunk = &(*out)[(*outlength) - length - 12]; - - /*1: length*/ - lodepng_set32bitInt(chunk, (unsigned)length); - - /*2: chunk name (4 letters)*/ - chunk[4] = (unsigned char)type[0]; - chunk[5] = (unsigned char)type[1]; - chunk[6] = (unsigned char)type[2]; - chunk[7] = (unsigned char)type[3]; - - /*3: the data*/ - for(i = 0; i != length; ++i) chunk[8 + i] = data[i]; - - /*4: CRC (of the chunkname characters and the data)*/ - lodepng_chunk_generate_crc(chunk); - - return 0; -} - -/* ////////////////////////////////////////////////////////////////////////// */ -/* / Color types and such / */ -/* ////////////////////////////////////////////////////////////////////////// */ - -/*return type is a LodePNG error code*/ -static unsigned checkColorValidity(LodePNGColorType colortype, unsigned bd) /*bd = bitdepth*/ -{ - switch(colortype) - { - case 0: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break; /*grey*/ - case 2: if(!( bd == 8 || bd == 16)) return 37; break; /*RGB*/ - case 3: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break; /*palette*/ - case 4: if(!( bd == 8 || bd == 16)) return 37; break; /*grey + alpha*/ - case 6: if(!( bd == 8 || bd == 16)) return 37; break; /*RGBA*/ - default: return 31; - } - return 0; /*allowed color type / bits combination*/ -} - -static unsigned getNumColorChannels(LodePNGColorType colortype) -{ - switch(colortype) - { - case 0: return 1; /*grey*/ - case 2: return 3; /*RGB*/ - case 3: return 1; /*palette*/ - case 4: return 2; /*grey + alpha*/ - case 6: return 4; /*RGBA*/ - } - return 0; /*unexisting color type*/ -} - -static unsigned lodepng_get_bpp_lct(LodePNGColorType colortype, unsigned bitdepth) -{ - /*bits per pixel is amount of channels * bits per channel*/ - return getNumColorChannels(colortype) * bitdepth; -} - -/* ////////////////////////////////////////////////////////////////////////// */ - -void lodepng_color_mode_init(LodePNGColorMode* info) -{ - info->key_defined = 0; - info->key_r = info->key_g = info->key_b = 0; - info->colortype = LCT_RGBA; - info->bitdepth = 8; - info->palette = 0; - info->palettesize = 0; -} - -void lodepng_color_mode_cleanup(LodePNGColorMode* info) -{ - lodepng_palette_clear(info); -} - -unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source) -{ - size_t i; - lodepng_color_mode_cleanup(dest); - *dest = *source; - if(source->palette) - { - dest->palette = (unsigned char*)lodepng_malloc(1024); - if(!dest->palette && source->palettesize) return 83; /*alloc fail*/ - for(i = 0; i != source->palettesize * 4; ++i) dest->palette[i] = source->palette[i]; - } - return 0; -} - -static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColorMode* b) -{ - size_t i; - if(a->colortype != b->colortype) return 0; - if(a->bitdepth != b->bitdepth) return 0; - if(a->key_defined != b->key_defined) return 0; - if(a->key_defined) - { - if(a->key_r != b->key_r) return 0; - if(a->key_g != b->key_g) return 0; - if(a->key_b != b->key_b) return 0; - } - /*if one of the palette sizes is 0, then we consider it to be the same as the - other: it means that e.g. the palette was not given by the user and should be - considered the same as the palette inside the PNG.*/ - if(1/*a->palettesize != 0 && b->palettesize != 0*/) { - if(a->palettesize != b->palettesize) return 0; - for(i = 0; i != a->palettesize * 4; ++i) - { - if(a->palette[i] != b->palette[i]) return 0; - } - } - return 1; -} - -void lodepng_palette_clear(LodePNGColorMode* info) -{ - if(info->palette) lodepng_free(info->palette); - info->palette = 0; - info->palettesize = 0; -} - -unsigned lodepng_palette_add(LodePNGColorMode* info, - unsigned char r, unsigned char g, unsigned char b, unsigned char a) -{ - unsigned char* data; - /*the same resize technique as C++ std::vectors is used, and here it's made so that for a palette with - the max of 256 colors, it'll have the exact alloc size*/ - if(!info->palette) /*allocate palette if empty*/ - { - /*room for 256 colors with 4 bytes each*/ - data = (unsigned char*)lodepng_realloc(info->palette, 1024); - if(!data) return 83; /*alloc fail*/ - else info->palette = data; - } - info->palette[4 * info->palettesize + 0] = r; - info->palette[4 * info->palettesize + 1] = g; - info->palette[4 * info->palettesize + 2] = b; - info->palette[4 * info->palettesize + 3] = a; - ++info->palettesize; - return 0; -} - -unsigned lodepng_get_bpp(const LodePNGColorMode* info) -{ - /*calculate bits per pixel out of colortype and bitdepth*/ - return lodepng_get_bpp_lct(info->colortype, info->bitdepth); -} - -unsigned lodepng_get_channels(const LodePNGColorMode* info) -{ - return getNumColorChannels(info->colortype); -} - -unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info) -{ - return info->colortype == LCT_GREY || info->colortype == LCT_GREY_ALPHA; -} - -unsigned lodepng_is_alpha_type(const LodePNGColorMode* info) -{ - return (info->colortype & 4) != 0; /*4 or 6*/ -} - -unsigned lodepng_is_palette_type(const LodePNGColorMode* info) -{ - return info->colortype == LCT_PALETTE; -} - -unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info) -{ - size_t i; - for(i = 0; i != info->palettesize; ++i) - { - if(info->palette[i * 4 + 3] < 255) return 1; - } - return 0; -} - -unsigned lodepng_can_have_alpha(const LodePNGColorMode* info) -{ - return info->key_defined - || lodepng_is_alpha_type(info) - || lodepng_has_palette_alpha(info); -} - -size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color) -{ - /*will not overflow for any color type if roughly w * h < 268435455*/ - size_t bpp = lodepng_get_bpp(color); - size_t n = w * h; - return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8; -} - -size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) -{ - /*will not overflow for any color type if roughly w * h < 268435455*/ - size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth); - size_t n = w * h; - return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8; -} - - -#ifdef LODEPNG_COMPILE_PNG -#ifdef LODEPNG_COMPILE_DECODER -/*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer*/ -static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, const LodePNGColorMode* color) -{ - /*will not overflow for any color type if roughly w * h < 268435455*/ - size_t bpp = lodepng_get_bpp(color); - size_t line = ((w / 8) * bpp) + ((w & 7) * bpp + 7) / 8; - return h * line; -} -#endif /*LODEPNG_COMPILE_DECODER*/ -#endif /*LODEPNG_COMPILE_PNG*/ - -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS - -static void LodePNGUnknownChunks_init(LodePNGInfo* info) -{ - unsigned i; - for(i = 0; i != 3; ++i) info->unknown_chunks_data[i] = 0; - for(i = 0; i != 3; ++i) info->unknown_chunks_size[i] = 0; -} - -static void LodePNGUnknownChunks_cleanup(LodePNGInfo* info) -{ - unsigned i; - for(i = 0; i != 3; ++i) lodepng_free(info->unknown_chunks_data[i]); -} - -static unsigned LodePNGUnknownChunks_copy(LodePNGInfo* dest, const LodePNGInfo* src) -{ - unsigned i; - - LodePNGUnknownChunks_cleanup(dest); - - for(i = 0; i != 3; ++i) - { - size_t j; - dest->unknown_chunks_size[i] = src->unknown_chunks_size[i]; - dest->unknown_chunks_data[i] = (unsigned char*)lodepng_malloc(src->unknown_chunks_size[i]); - if(!dest->unknown_chunks_data[i] && dest->unknown_chunks_size[i]) return 83; /*alloc fail*/ - for(j = 0; j < src->unknown_chunks_size[i]; ++j) - { - dest->unknown_chunks_data[i][j] = src->unknown_chunks_data[i][j]; - } - } - - return 0; -} - -/******************************************************************************/ - -static void LodePNGText_init(LodePNGInfo* info) -{ - info->text_num = 0; - info->text_keys = NULL; - info->text_strings = NULL; -} - -static void LodePNGText_cleanup(LodePNGInfo* info) -{ - size_t i; - for(i = 0; i != info->text_num; ++i) - { - string_cleanup(&info->text_keys[i]); - string_cleanup(&info->text_strings[i]); - } - lodepng_free(info->text_keys); - lodepng_free(info->text_strings); -} - -static unsigned LodePNGText_copy(LodePNGInfo* dest, const LodePNGInfo* source) -{ - size_t i = 0; - dest->text_keys = 0; - dest->text_strings = 0; - dest->text_num = 0; - for(i = 0; i != source->text_num; ++i) - { - CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i])); - } - return 0; -} - -void lodepng_clear_text(LodePNGInfo* info) -{ - LodePNGText_cleanup(info); -} - -unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str) -{ - char** new_keys = (char**)(lodepng_realloc(info->text_keys, sizeof(char*) * (info->text_num + 1))); - char** new_strings = (char**)(lodepng_realloc(info->text_strings, sizeof(char*) * (info->text_num + 1))); - if(!new_keys || !new_strings) - { - lodepng_free(new_keys); - lodepng_free(new_strings); - return 83; /*alloc fail*/ - } - - ++info->text_num; - info->text_keys = new_keys; - info->text_strings = new_strings; - - string_init(&info->text_keys[info->text_num - 1]); - string_set(&info->text_keys[info->text_num - 1], key); - - string_init(&info->text_strings[info->text_num - 1]); - string_set(&info->text_strings[info->text_num - 1], str); - - return 0; -} - -/******************************************************************************/ - -static void LodePNGIText_init(LodePNGInfo* info) -{ - info->itext_num = 0; - info->itext_keys = NULL; - info->itext_langtags = NULL; - info->itext_transkeys = NULL; - info->itext_strings = NULL; -} - -static void LodePNGIText_cleanup(LodePNGInfo* info) -{ - size_t i; - for(i = 0; i != info->itext_num; ++i) - { - string_cleanup(&info->itext_keys[i]); - string_cleanup(&info->itext_langtags[i]); - string_cleanup(&info->itext_transkeys[i]); - string_cleanup(&info->itext_strings[i]); - } - lodepng_free(info->itext_keys); - lodepng_free(info->itext_langtags); - lodepng_free(info->itext_transkeys); - lodepng_free(info->itext_strings); -} - -static unsigned LodePNGIText_copy(LodePNGInfo* dest, const LodePNGInfo* source) -{ - size_t i = 0; - dest->itext_keys = 0; - dest->itext_langtags = 0; - dest->itext_transkeys = 0; - dest->itext_strings = 0; - dest->itext_num = 0; - for(i = 0; i != source->itext_num; ++i) - { - CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i], - source->itext_transkeys[i], source->itext_strings[i])); - } - return 0; -} - -void lodepng_clear_itext(LodePNGInfo* info) -{ - LodePNGIText_cleanup(info); -} - -unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, - const char* transkey, const char* str) -{ - char** new_keys = (char**)(lodepng_realloc(info->itext_keys, sizeof(char*) * (info->itext_num + 1))); - char** new_langtags = (char**)(lodepng_realloc(info->itext_langtags, sizeof(char*) * (info->itext_num + 1))); - char** new_transkeys = (char**)(lodepng_realloc(info->itext_transkeys, sizeof(char*) * (info->itext_num + 1))); - char** new_strings = (char**)(lodepng_realloc(info->itext_strings, sizeof(char*) * (info->itext_num + 1))); - if(!new_keys || !new_langtags || !new_transkeys || !new_strings) - { - lodepng_free(new_keys); - lodepng_free(new_langtags); - lodepng_free(new_transkeys); - lodepng_free(new_strings); - return 83; /*alloc fail*/ - } - - ++info->itext_num; - info->itext_keys = new_keys; - info->itext_langtags = new_langtags; - info->itext_transkeys = new_transkeys; - info->itext_strings = new_strings; - - string_init(&info->itext_keys[info->itext_num - 1]); - string_set(&info->itext_keys[info->itext_num - 1], key); - - string_init(&info->itext_langtags[info->itext_num - 1]); - string_set(&info->itext_langtags[info->itext_num - 1], langtag); - - string_init(&info->itext_transkeys[info->itext_num - 1]); - string_set(&info->itext_transkeys[info->itext_num - 1], transkey); - - string_init(&info->itext_strings[info->itext_num - 1]); - string_set(&info->itext_strings[info->itext_num - 1], str); - - return 0; -} -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ - -void lodepng_info_init(LodePNGInfo* info) -{ - lodepng_color_mode_init(&info->color); - info->interlace_method = 0; - info->compression_method = 0; - info->filter_method = 0; -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS - info->background_defined = 0; - info->background_r = info->background_g = info->background_b = 0; - - LodePNGText_init(info); - LodePNGIText_init(info); - - info->time_defined = 0; - info->phys_defined = 0; - - LodePNGUnknownChunks_init(info); -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ -} - -void lodepng_info_cleanup(LodePNGInfo* info) -{ - lodepng_color_mode_cleanup(&info->color); -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS - LodePNGText_cleanup(info); - LodePNGIText_cleanup(info); - - LodePNGUnknownChunks_cleanup(info); -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ -} - -unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source) -{ - lodepng_info_cleanup(dest); - *dest = *source; - lodepng_color_mode_init(&dest->color); - CERROR_TRY_RETURN(lodepng_color_mode_copy(&dest->color, &source->color)); - -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS - CERROR_TRY_RETURN(LodePNGText_copy(dest, source)); - CERROR_TRY_RETURN(LodePNGIText_copy(dest, source)); - - LodePNGUnknownChunks_init(dest); - CERROR_TRY_RETURN(LodePNGUnknownChunks_copy(dest, source)); -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ - return 0; -} - -void lodepng_info_swap(LodePNGInfo* a, LodePNGInfo* b) -{ - LodePNGInfo temp = *a; - *a = *b; - *b = temp; -} - -/* ////////////////////////////////////////////////////////////////////////// */ - -/*index: bitgroup index, bits: bitgroup size(1, 2 or 4), in: bitgroup value, out: octet array to add bits to*/ -static void addColorBits(unsigned char* out, size_t index, unsigned bits, unsigned in) -{ - unsigned m = bits == 1 ? 7 : bits == 2 ? 3 : 1; /*8 / bits - 1*/ - /*p = the partial index in the byte, e.g. with 4 palettebits it is 0 for first half or 1 for second half*/ - unsigned p = index & m; - in &= (1u << bits) - 1u; /*filter out any other bits of the input value*/ - in = in << (bits * (m - p)); - if(p == 0) out[index * bits / 8] = in; - else out[index * bits / 8] |= in; -} - -typedef struct ColorTree ColorTree; - -/* -One node of a color tree -This is the data structure used to count the number of unique colors and to get a palette -index for a color. It's like an octree, but because the alpha channel is used too, each -node has 16 instead of 8 children. -*/ -struct ColorTree -{ - ColorTree* children[16]; /*up to 16 pointers to ColorTree of next level*/ - int index; /*the payload. Only has a meaningful value if this is in the last level*/ -}; - -static void color_tree_init(ColorTree* tree) -{ - int i; - for(i = 0; i != 16; ++i) tree->children[i] = 0; - tree->index = -1; -} - -static void color_tree_cleanup(ColorTree* tree) -{ - int i; - for(i = 0; i != 16; ++i) - { - if(tree->children[i]) - { - color_tree_cleanup(tree->children[i]); - lodepng_free(tree->children[i]); - } - } -} - -/*returns -1 if color not present, its index otherwise*/ -static int color_tree_get(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) -{ - int bit = 0; - for(bit = 0; bit < 8; ++bit) - { - int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); - if(!tree->children[i]) return -1; - else tree = tree->children[i]; - } - return tree ? tree->index : -1; -} - -#ifdef LODEPNG_COMPILE_ENCODER -static int color_tree_has(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) -{ - return color_tree_get(tree, r, g, b, a) >= 0; -} -#endif /*LODEPNG_COMPILE_ENCODER*/ - -/*color is not allowed to already exist. -Index should be >= 0 (it's signed to be compatible with using -1 for "doesn't exist")*/ -static void color_tree_add(ColorTree* tree, - unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned index) -{ - int bit; - for(bit = 0; bit < 8; ++bit) - { - int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); - if(!tree->children[i]) - { - tree->children[i] = (ColorTree*)lodepng_malloc(sizeof(ColorTree)); - color_tree_init(tree->children[i]); - } - tree = tree->children[i]; - } - tree->index = (int)index; -} - -/*put a pixel, given its RGBA color, into image of any color type*/ -static unsigned rgba8ToPixel(unsigned char* out, size_t i, - const LodePNGColorMode* mode, ColorTree* tree /*for palette*/, - unsigned char r, unsigned char g, unsigned char b, unsigned char a) -{ - if(mode->colortype == LCT_GREY) - { - unsigned char grey = r; /*((unsigned short)r + g + b) / 3*/; - if(mode->bitdepth == 8) out[i] = grey; - else if(mode->bitdepth == 16) out[i * 2 + 0] = out[i * 2 + 1] = grey; - else - { - /*take the most significant bits of grey*/ - grey = (grey >> (8 - mode->bitdepth)) & ((1 << mode->bitdepth) - 1); - addColorBits(out, i, mode->bitdepth, grey); - } - } - else if(mode->colortype == LCT_RGB) - { - if(mode->bitdepth == 8) - { - out[i * 3 + 0] = r; - out[i * 3 + 1] = g; - out[i * 3 + 2] = b; - } - else - { - out[i * 6 + 0] = out[i * 6 + 1] = r; - out[i * 6 + 2] = out[i * 6 + 3] = g; - out[i * 6 + 4] = out[i * 6 + 5] = b; - } - } - else if(mode->colortype == LCT_PALETTE) - { - int index = color_tree_get(tree, r, g, b, a); - if(index < 0) return 82; /*color not in palette*/ - if(mode->bitdepth == 8) out[i] = index; - else addColorBits(out, i, mode->bitdepth, (unsigned)index); - } - else if(mode->colortype == LCT_GREY_ALPHA) - { - unsigned char grey = r; /*((unsigned short)r + g + b) / 3*/; - if(mode->bitdepth == 8) - { - out[i * 2 + 0] = grey; - out[i * 2 + 1] = a; - } - else if(mode->bitdepth == 16) - { - out[i * 4 + 0] = out[i * 4 + 1] = grey; - out[i * 4 + 2] = out[i * 4 + 3] = a; - } - } - else if(mode->colortype == LCT_RGBA) - { - if(mode->bitdepth == 8) - { - out[i * 4 + 0] = r; - out[i * 4 + 1] = g; - out[i * 4 + 2] = b; - out[i * 4 + 3] = a; - } - else - { - out[i * 8 + 0] = out[i * 8 + 1] = r; - out[i * 8 + 2] = out[i * 8 + 3] = g; - out[i * 8 + 4] = out[i * 8 + 5] = b; - out[i * 8 + 6] = out[i * 8 + 7] = a; - } - } - - return 0; /*no error*/ -} - -/*put a pixel, given its RGBA16 color, into image of any color 16-bitdepth type*/ -static void rgba16ToPixel(unsigned char* out, size_t i, - const LodePNGColorMode* mode, - unsigned short r, unsigned short g, unsigned short b, unsigned short a) -{ - if(mode->colortype == LCT_GREY) - { - unsigned short grey = r; /*((unsigned)r + g + b) / 3*/; - out[i * 2 + 0] = (grey >> 8) & 255; - out[i * 2 + 1] = grey & 255; - } - else if(mode->colortype == LCT_RGB) - { - out[i * 6 + 0] = (r >> 8) & 255; - out[i * 6 + 1] = r & 255; - out[i * 6 + 2] = (g >> 8) & 255; - out[i * 6 + 3] = g & 255; - out[i * 6 + 4] = (b >> 8) & 255; - out[i * 6 + 5] = b & 255; - } - else if(mode->colortype == LCT_GREY_ALPHA) - { - unsigned short grey = r; /*((unsigned)r + g + b) / 3*/; - out[i * 4 + 0] = (grey >> 8) & 255; - out[i * 4 + 1] = grey & 255; - out[i * 4 + 2] = (a >> 8) & 255; - out[i * 4 + 3] = a & 255; - } - else if(mode->colortype == LCT_RGBA) - { - out[i * 8 + 0] = (r >> 8) & 255; - out[i * 8 + 1] = r & 255; - out[i * 8 + 2] = (g >> 8) & 255; - out[i * 8 + 3] = g & 255; - out[i * 8 + 4] = (b >> 8) & 255; - out[i * 8 + 5] = b & 255; - out[i * 8 + 6] = (a >> 8) & 255; - out[i * 8 + 7] = a & 255; - } -} - -/*Get RGBA8 color of pixel with index i (y * width + x) from the raw image with given color type.*/ -static void getPixelColorRGBA8(unsigned char* r, unsigned char* g, - unsigned char* b, unsigned char* a, - const unsigned char* in, size_t i, - const LodePNGColorMode* mode) -{ - if(mode->colortype == LCT_GREY) - { - if(mode->bitdepth == 8) - { - *r = *g = *b = in[i]; - if(mode->key_defined && *r == mode->key_r) *a = 0; - else *a = 255; - } - else if(mode->bitdepth == 16) - { - *r = *g = *b = in[i * 2 + 0]; - if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; - else *a = 255; - } - else - { - unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ - size_t j = i * mode->bitdepth; - unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); - *r = *g = *b = (value * 255) / highest; - if(mode->key_defined && value == mode->key_r) *a = 0; - else *a = 255; - } - } - else if(mode->colortype == LCT_RGB) - { - if(mode->bitdepth == 8) - { - *r = in[i * 3 + 0]; *g = in[i * 3 + 1]; *b = in[i * 3 + 2]; - if(mode->key_defined && *r == mode->key_r && *g == mode->key_g && *b == mode->key_b) *a = 0; - else *a = 255; - } - else - { - *r = in[i * 6 + 0]; - *g = in[i * 6 + 2]; - *b = in[i * 6 + 4]; - if(mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r - && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g - && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; - else *a = 255; - } - } - else if(mode->colortype == LCT_PALETTE) - { - unsigned index; - if(mode->bitdepth == 8) index = in[i]; - else - { - size_t j = i * mode->bitdepth; - index = readBitsFromReversedStream(&j, in, mode->bitdepth); - } - - if(index >= mode->palettesize) - { - /*This is an error according to the PNG spec, but common PNG decoders make it black instead. - Done here too, slightly faster due to no error handling needed.*/ - *r = *g = *b = 0; - *a = 255; - } - else - { - *r = mode->palette[index * 4 + 0]; - *g = mode->palette[index * 4 + 1]; - *b = mode->palette[index * 4 + 2]; - *a = mode->palette[index * 4 + 3]; - } - } - else if(mode->colortype == LCT_GREY_ALPHA) - { - if(mode->bitdepth == 8) - { - *r = *g = *b = in[i * 2 + 0]; - *a = in[i * 2 + 1]; - } - else - { - *r = *g = *b = in[i * 4 + 0]; - *a = in[i * 4 + 2]; - } - } - else if(mode->colortype == LCT_RGBA) - { - if(mode->bitdepth == 8) - { - *r = in[i * 4 + 0]; - *g = in[i * 4 + 1]; - *b = in[i * 4 + 2]; - *a = in[i * 4 + 3]; - } - else - { - *r = in[i * 8 + 0]; - *g = in[i * 8 + 2]; - *b = in[i * 8 + 4]; - *a = in[i * 8 + 6]; - } - } -} - -/*Similar to getPixelColorRGBA8, but with all the for loops inside of the color -mode test cases, optimized to convert the colors much faster, when converting -to RGBA or RGB with 8 bit per cannel. buffer must be RGBA or RGB output with -enough memory, if has_alpha is true the output is RGBA. mode has the color mode -of the input buffer.*/ -static void getPixelColorsRGBA8(unsigned char* buffer, size_t numpixels, - unsigned has_alpha, const unsigned char* in, - const LodePNGColorMode* mode) -{ - unsigned num_channels = has_alpha ? 4 : 3; - size_t i; - if(mode->colortype == LCT_GREY) - { - if(mode->bitdepth == 8) - { - for(i = 0; i != numpixels; ++i, buffer += num_channels) - { - buffer[0] = buffer[1] = buffer[2] = in[i]; - if(has_alpha) buffer[3] = mode->key_defined && in[i] == mode->key_r ? 0 : 255; - } - } - else if(mode->bitdepth == 16) - { - for(i = 0; i != numpixels; ++i, buffer += num_channels) - { - buffer[0] = buffer[1] = buffer[2] = in[i * 2]; - if(has_alpha) buffer[3] = mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r ? 0 : 255; - } - } - else - { - unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ - size_t j = 0; - for(i = 0; i != numpixels; ++i, buffer += num_channels) - { - unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); - buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest; - if(has_alpha) buffer[3] = mode->key_defined && value == mode->key_r ? 0 : 255; - } - } - } - else if(mode->colortype == LCT_RGB) - { - if(mode->bitdepth == 8) - { - for(i = 0; i != numpixels; ++i, buffer += num_channels) - { - buffer[0] = in[i * 3 + 0]; - buffer[1] = in[i * 3 + 1]; - buffer[2] = in[i * 3 + 2]; - if(has_alpha) buffer[3] = mode->key_defined && buffer[0] == mode->key_r - && buffer[1]== mode->key_g && buffer[2] == mode->key_b ? 0 : 255; - } - } - else - { - for(i = 0; i != numpixels; ++i, buffer += num_channels) - { - buffer[0] = in[i * 6 + 0]; - buffer[1] = in[i * 6 + 2]; - buffer[2] = in[i * 6 + 4]; - if(has_alpha) buffer[3] = mode->key_defined - && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r - && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g - && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b ? 0 : 255; - } - } - } - else if(mode->colortype == LCT_PALETTE) - { - unsigned index; - size_t j = 0; - for(i = 0; i != numpixels; ++i, buffer += num_channels) - { - if(mode->bitdepth == 8) index = in[i]; - else index = readBitsFromReversedStream(&j, in, mode->bitdepth); - - if(index >= mode->palettesize) - { - /*This is an error according to the PNG spec, but most PNG decoders make it black instead. - Done here too, slightly faster due to no error handling needed.*/ - buffer[0] = buffer[1] = buffer[2] = 0; - if(has_alpha) buffer[3] = 255; - } - else - { - buffer[0] = mode->palette[index * 4 + 0]; - buffer[1] = mode->palette[index * 4 + 1]; - buffer[2] = mode->palette[index * 4 + 2]; - if(has_alpha) buffer[3] = mode->palette[index * 4 + 3]; - } - } - } - else if(mode->colortype == LCT_GREY_ALPHA) - { - if(mode->bitdepth == 8) - { - for(i = 0; i != numpixels; ++i, buffer += num_channels) - { - buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0]; - if(has_alpha) buffer[3] = in[i * 2 + 1]; - } - } - else - { - for(i = 0; i != numpixels; ++i, buffer += num_channels) - { - buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0]; - if(has_alpha) buffer[3] = in[i * 4 + 2]; - } - } - } - else if(mode->colortype == LCT_RGBA) - { - if(mode->bitdepth == 8) - { - for(i = 0; i != numpixels; ++i, buffer += num_channels) - { - buffer[0] = in[i * 4 + 0]; - buffer[1] = in[i * 4 + 1]; - buffer[2] = in[i * 4 + 2]; - if(has_alpha) buffer[3] = in[i * 4 + 3]; - } - } - else - { - for(i = 0; i != numpixels; ++i, buffer += num_channels) - { - buffer[0] = in[i * 8 + 0]; - buffer[1] = in[i * 8 + 2]; - buffer[2] = in[i * 8 + 4]; - if(has_alpha) buffer[3] = in[i * 8 + 6]; - } - } - } -} - -/*Get RGBA16 color of pixel with index i (y * width + x) from the raw image with -given color type, but the given color type must be 16-bit itself.*/ -static void getPixelColorRGBA16(unsigned short* r, unsigned short* g, unsigned short* b, unsigned short* a, - const unsigned char* in, size_t i, const LodePNGColorMode* mode) -{ - if(mode->colortype == LCT_GREY) - { - *r = *g = *b = 256 * in[i * 2 + 0] + in[i * 2 + 1]; - if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; - else *a = 65535; - } - else if(mode->colortype == LCT_RGB) - { - *r = 256u * in[i * 6 + 0] + in[i * 6 + 1]; - *g = 256u * in[i * 6 + 2] + in[i * 6 + 3]; - *b = 256u * in[i * 6 + 4] + in[i * 6 + 5]; - if(mode->key_defined - && 256u * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r - && 256u * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g - && 256u * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; - else *a = 65535; - } - else if(mode->colortype == LCT_GREY_ALPHA) - { - *r = *g = *b = 256u * in[i * 4 + 0] + in[i * 4 + 1]; - *a = 256u * in[i * 4 + 2] + in[i * 4 + 3]; - } - else if(mode->colortype == LCT_RGBA) - { - *r = 256u * in[i * 8 + 0] + in[i * 8 + 1]; - *g = 256u * in[i * 8 + 2] + in[i * 8 + 3]; - *b = 256u * in[i * 8 + 4] + in[i * 8 + 5]; - *a = 256u * in[i * 8 + 6] + in[i * 8 + 7]; - } -} - -unsigned lodepng_convert(unsigned char* out, const unsigned char* in, - const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, - unsigned w, unsigned h) -{ - size_t i; - ColorTree tree; - size_t numpixels = w * h; - unsigned error = 0; - - if(lodepng_color_mode_equal(mode_out, mode_in)) - { - size_t numbytes = lodepng_get_raw_size(w, h, mode_in); - for(i = 0; i != numbytes; ++i) out[i] = in[i]; - return 0; - } - - if(mode_out->colortype == LCT_PALETTE) - { - size_t palettesize = mode_out->palettesize; - const unsigned char* palette = mode_out->palette; - size_t palsize = 1u << mode_out->bitdepth; - /*if the user specified output palette but did not give the values, assume - they want the values of the input color type (assuming that one is palette). - Note that we never create a new palette ourselves.*/ - if(palettesize == 0) - { - palettesize = mode_in->palettesize; - palette = mode_in->palette; - } - if(palettesize < palsize) palsize = palettesize; - color_tree_init(&tree); - for(i = 0; i != palsize; ++i) - { - const unsigned char* p = &palette[i * 4]; - color_tree_add(&tree, p[0], p[1], p[2], p[3], (unsigned)i); - } - } - - if(mode_in->bitdepth == 16 && mode_out->bitdepth == 16) - { - for(i = 0; i != numpixels; ++i) - { - unsigned short r = 0, g = 0, b = 0, a = 0; - getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); - rgba16ToPixel(out, i, mode_out, r, g, b, a); - } - } - else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGBA) - { - getPixelColorsRGBA8(out, numpixels, 1, in, mode_in); - } - else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGB) - { - getPixelColorsRGBA8(out, numpixels, 0, in, mode_in); - } - else - { - unsigned char r = 0, g = 0, b = 0, a = 0; - for(i = 0; i != numpixels; ++i) - { - getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); - error = rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a); - if (error) break; - } - } - - if(mode_out->colortype == LCT_PALETTE) - { - color_tree_cleanup(&tree); - } - - return error; -} - -#ifdef LODEPNG_COMPILE_ENCODER - -void lodepng_color_profile_init(LodePNGColorProfile* profile) -{ - profile->colored = 0; - profile->key = 0; - profile->key_r = profile->key_g = profile->key_b = 0; - profile->alpha = 0; - profile->numcolors = 0; - profile->bits = 1; -} - -/*function used for debug purposes with C++*/ -/*void printColorProfile(LodePNGColorProfile* p) -{ - std::cout << "colored: " << (int)p->colored << ", "; - std::cout << "key: " << (int)p->key << ", "; - std::cout << "key_r: " << (int)p->key_r << ", "; - std::cout << "key_g: " << (int)p->key_g << ", "; - std::cout << "key_b: " << (int)p->key_b << ", "; - std::cout << "alpha: " << (int)p->alpha << ", "; - std::cout << "numcolors: " << (int)p->numcolors << ", "; - std::cout << "bits: " << (int)p->bits << std::endl; -}*/ - -/*Returns how many bits needed to represent given value (max 8 bit)*/ -static unsigned getValueRequiredBits(unsigned char value) -{ - if(value == 0 || value == 255) return 1; - /*The scaling of 2-bit and 4-bit values uses multiples of 85 and 17*/ - if(value % 17 == 0) return value % 85 == 0 ? 2 : 4; - return 8; -} - -/*profile must already have been inited with mode. -It's ok to set some parameters of profile to done already.*/ -unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, - const unsigned char* in, unsigned w, unsigned h, - const LodePNGColorMode* mode) -{ - unsigned error = 0; - size_t i; - ColorTree tree; - size_t numpixels = w * h; - - unsigned colored_done = lodepng_is_greyscale_type(mode) ? 1 : 0; - unsigned alpha_done = lodepng_can_have_alpha(mode) ? 0 : 1; - unsigned numcolors_done = 0; - unsigned bpp = lodepng_get_bpp(mode); - unsigned bits_done = bpp == 1 ? 1 : 0; - unsigned maxnumcolors = 257; - unsigned sixteen = 0; - if(bpp <= 8) maxnumcolors = bpp == 1 ? 2 : (bpp == 2 ? 4 : (bpp == 4 ? 16 : 256)); - - color_tree_init(&tree); - - /*Check if the 16-bit input is truly 16-bit*/ - if(mode->bitdepth == 16) - { - unsigned short r, g, b, a; - for(i = 0; i != numpixels; ++i) - { - getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); - if((r & 255) != ((r >> 8) & 255) || (g & 255) != ((g >> 8) & 255) || - (b & 255) != ((b >> 8) & 255) || (a & 255) != ((a >> 8) & 255)) /*first and second byte differ*/ - { - sixteen = 1; - break; - } - } - } - - if(sixteen) - { - unsigned short r = 0, g = 0, b = 0, a = 0; - profile->bits = 16; - bits_done = numcolors_done = 1; /*counting colors no longer useful, palette doesn't support 16-bit*/ - - for(i = 0; i != numpixels; ++i) - { - getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); - - if(!colored_done && (r != g || r != b)) - { - profile->colored = 1; - colored_done = 1; - } - - if(!alpha_done) - { - unsigned matchkey = (r == profile->key_r && g == profile->key_g && b == profile->key_b); - if(a != 65535 && (a != 0 || (profile->key && !matchkey))) - { - profile->alpha = 1; - profile->key = 0; - alpha_done = 1; - } - else if(a == 0 && !profile->alpha && !profile->key) - { - profile->key = 1; - profile->key_r = r; - profile->key_g = g; - profile->key_b = b; - } - else if(a == 65535 && profile->key && matchkey) - { - /* Color key cannot be used if an opaque pixel also has that RGB color. */ - profile->alpha = 1; - profile->key = 0; - alpha_done = 1; - } - } - if(alpha_done && numcolors_done && colored_done && bits_done) break; - } - - if(profile->key && !profile->alpha) - { - for(i = 0; i != numpixels; ++i) - { - getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); - if(a != 0 && r == profile->key_r && g == profile->key_g && b == profile->key_b) - { - /* Color key cannot be used if an opaque pixel also has that RGB color. */ - profile->alpha = 1; - profile->key = 0; - alpha_done = 1; - } - } - } - } - else /* < 16-bit */ - { - unsigned char r = 0, g = 0, b = 0, a = 0; - for(i = 0; i != numpixels; ++i) - { - getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode); - - if(!bits_done && profile->bits < 8) - { - /*only r is checked, < 8 bits is only relevant for greyscale*/ - unsigned bits = getValueRequiredBits(r); - if(bits > profile->bits) profile->bits = bits; - } - bits_done = (profile->bits >= bpp); - - if(!colored_done && (r != g || r != b)) - { - profile->colored = 1; - colored_done = 1; - if(profile->bits < 8) profile->bits = 8; /*PNG has no colored modes with less than 8-bit per channel*/ - } - - if(!alpha_done) - { - unsigned matchkey = (r == profile->key_r && g == profile->key_g && b == profile->key_b); - if(a != 255 && (a != 0 || (profile->key && !matchkey))) - { - profile->alpha = 1; - profile->key = 0; - alpha_done = 1; - if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ - } - else if(a == 0 && !profile->alpha && !profile->key) - { - profile->key = 1; - profile->key_r = r; - profile->key_g = g; - profile->key_b = b; - } - else if(a == 255 && profile->key && matchkey) - { - /* Color key cannot be used if an opaque pixel also has that RGB color. */ - profile->alpha = 1; - profile->key = 0; - alpha_done = 1; - if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ - } - } - - if(!numcolors_done) - { - if(!color_tree_has(&tree, r, g, b, a)) - { - color_tree_add(&tree, r, g, b, a, profile->numcolors); - if(profile->numcolors < 256) - { - unsigned char* p = profile->palette; - unsigned n = profile->numcolors; - p[n * 4 + 0] = r; - p[n * 4 + 1] = g; - p[n * 4 + 2] = b; - p[n * 4 + 3] = a; - } - ++profile->numcolors; - numcolors_done = profile->numcolors >= maxnumcolors; - } - } - - if(alpha_done && numcolors_done && colored_done && bits_done) break; - } - - if(profile->key && !profile->alpha) - { - for(i = 0; i != numpixels; ++i) - { - getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode); - if(a != 0 && r == profile->key_r && g == profile->key_g && b == profile->key_b) - { - /* Color key cannot be used if an opaque pixel also has that RGB color. */ - profile->alpha = 1; - profile->key = 0; - alpha_done = 1; - if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ - } - } - } - - /*make the profile's key always 16-bit for consistency - repeat each byte twice*/ - profile->key_r += (profile->key_r << 8); - profile->key_g += (profile->key_g << 8); - profile->key_b += (profile->key_b << 8); - } - - color_tree_cleanup(&tree); - return error; -} - -/*Automatically chooses color type that gives smallest amount of bits in the -output image, e.g. grey if there are only greyscale pixels, palette if there -are less than 256 colors, ... -Updates values of mode with a potentially smaller color model. mode_out should -contain the user chosen color model, but will be overwritten with the new chosen one.*/ -unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, - const unsigned char* image, unsigned w, unsigned h, - const LodePNGColorMode* mode_in) -{ - LodePNGColorProfile prof; - unsigned error = 0; - unsigned i, n, palettebits, palette_ok; - - lodepng_color_profile_init(&prof); - error = lodepng_get_color_profile(&prof, image, w, h, mode_in); - if(error) return error; - mode_out->key_defined = 0; - - if(prof.key && w * h <= 16) - { - prof.alpha = 1; /*too few pixels to justify tRNS chunk overhead*/ - prof.key = 0; - if(prof.bits < 8) prof.bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ - } - n = prof.numcolors; - palettebits = n <= 2 ? 1 : (n <= 4 ? 2 : (n <= 16 ? 4 : 8)); - palette_ok = n <= 256 && prof.bits <= 8; - if(w * h < n * 2) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/ - if(!prof.colored && prof.bits <= palettebits) palette_ok = 0; /*grey is less overhead*/ - - if(palette_ok) - { - unsigned char* p = prof.palette; - lodepng_palette_clear(mode_out); /*remove potential earlier palette*/ - for(i = 0; i != prof.numcolors; ++i) - { - error = lodepng_palette_add(mode_out, p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3]); - if(error) break; - } - - mode_out->colortype = LCT_PALETTE; - mode_out->bitdepth = palettebits; - - if(mode_in->colortype == LCT_PALETTE && mode_in->palettesize >= mode_out->palettesize - && mode_in->bitdepth == mode_out->bitdepth) - { - /*If input should have same palette colors, keep original to preserve its order and prevent conversion*/ - lodepng_color_mode_cleanup(mode_out); - lodepng_color_mode_copy(mode_out, mode_in); - } - } - else /*8-bit or 16-bit per channel*/ - { - mode_out->bitdepth = prof.bits; - mode_out->colortype = prof.alpha ? (prof.colored ? LCT_RGBA : LCT_GREY_ALPHA) - : (prof.colored ? LCT_RGB : LCT_GREY); - - if(prof.key) - { - unsigned mask = (1u << mode_out->bitdepth) - 1u; /*profile always uses 16-bit, mask converts it*/ - mode_out->key_r = prof.key_r & mask; - mode_out->key_g = prof.key_g & mask; - mode_out->key_b = prof.key_b & mask; - mode_out->key_defined = 1; - } - } - - return error; -} - -#endif /* #ifdef LODEPNG_COMPILE_ENCODER */ - -/* -Paeth predicter, used by PNG filter type 4 -The parameters are of type short, but should come from unsigned chars, the shorts -are only needed to make the paeth calculation correct. -*/ -static unsigned char paethPredictor(short a, short b, short c) -{ - short pa = abs(b - c); - short pb = abs(a - c); - short pc = abs(a + b - c - c); - - if(pc < pa && pc < pb) return (unsigned char)c; - else if(pb < pa) return (unsigned char)b; - else return (unsigned char)a; -} - -/*shared values used by multiple Adam7 related functions*/ - -static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/ -static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/ -static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/ -static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/ - -/* -Outputs various dimensions and positions in the image related to the Adam7 reduced images. -passw: output containing the width of the 7 passes -passh: output containing the height of the 7 passes -filter_passstart: output containing the index of the start and end of each - reduced image with filter bytes -padded_passstart output containing the index of the start and end of each - reduced image when without filter bytes but with padded scanlines -passstart: output containing the index of the start and end of each reduced - image without padding between scanlines, but still padding between the images -w, h: width and height of non-interlaced image -bpp: bits per pixel -"padded" is only relevant if bpp is less than 8 and a scanline or image does not - end at a full byte -*/ -static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t filter_passstart[8], - size_t padded_passstart[8], size_t passstart[8], unsigned w, unsigned h, unsigned bpp) -{ - /*the passstart values have 8 values: the 8th one indicates the byte after the end of the 7th (= last) pass*/ - unsigned i; - - /*calculate width and height in pixels of each pass*/ - for(i = 0; i != 7; ++i) - { - passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i]; - passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i]; - if(passw[i] == 0) passh[i] = 0; - if(passh[i] == 0) passw[i] = 0; - } - - filter_passstart[0] = padded_passstart[0] = passstart[0] = 0; - for(i = 0; i != 7; ++i) - { - /*if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte)*/ - filter_passstart[i + 1] = filter_passstart[i] - + ((passw[i] && passh[i]) ? passh[i] * (1 + (passw[i] * bpp + 7) / 8) : 0); - /*bits padded if needed to fill full byte at end of each scanline*/ - padded_passstart[i + 1] = padded_passstart[i] + passh[i] * ((passw[i] * bpp + 7) / 8); - /*only padded at end of reduced image*/ - passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7) / 8; - } -} - -#ifdef LODEPNG_COMPILE_DECODER - -/* ////////////////////////////////////////////////////////////////////////// */ -/* / PNG Decoder / */ -/* ////////////////////////////////////////////////////////////////////////// */ - -/*read the information from the header and store it in the LodePNGInfo. return value is error*/ -unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, - const unsigned char* in, size_t insize) -{ - LodePNGInfo* info = &state->info_png; - if(insize == 0 || in == 0) - { - CERROR_RETURN_ERROR(state->error, 48); /*error: the given data is empty*/ - } - if(insize < 33) - { - CERROR_RETURN_ERROR(state->error, 27); /*error: the data length is smaller than the length of a PNG header*/ - } - - /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/ - lodepng_info_cleanup(info); - lodepng_info_init(info); - - if(in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71 - || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) - { - CERROR_RETURN_ERROR(state->error, 28); /*error: the first 8 bytes are not the correct PNG signature*/ - } - if(lodepng_chunk_length(in + 8) != 13) - { - CERROR_RETURN_ERROR(state->error, 94); /*error: header size must be 13 bytes*/ - } - if(!lodepng_chunk_type_equals(in + 8, "IHDR")) - { - CERROR_RETURN_ERROR(state->error, 29); /*error: it doesn't start with a IHDR chunk!*/ - } - - /*read the values given in the header*/ - *w = lodepng_read32bitInt(&in[16]); - *h = lodepng_read32bitInt(&in[20]); - info->color.bitdepth = in[24]; - info->color.colortype = (LodePNGColorType)in[25]; - info->compression_method = in[26]; - info->filter_method = in[27]; - info->interlace_method = in[28]; - - if(*w == 0 || *h == 0) - { - CERROR_RETURN_ERROR(state->error, 93); - } - - if(!state->decoder.ignore_crc) - { - unsigned CRC = lodepng_read32bitInt(&in[29]); - unsigned checksum = lodepng_crc32(&in[12], 17); - if(CRC != checksum) - { - CERROR_RETURN_ERROR(state->error, 57); /*invalid CRC*/ - } - } - - /*error: only compression method 0 is allowed in the specification*/ - if(info->compression_method != 0) CERROR_RETURN_ERROR(state->error, 32); - /*error: only filter method 0 is allowed in the specification*/ - if(info->filter_method != 0) CERROR_RETURN_ERROR(state->error, 33); - /*error: only interlace methods 0 and 1 exist in the specification*/ - if(info->interlace_method > 1) CERROR_RETURN_ERROR(state->error, 34); - - state->error = checkColorValidity(info->color.colortype, info->color.bitdepth); - return state->error; -} - -static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, - size_t bytewidth, unsigned char filterType, size_t length) -{ - /* - For PNG filter method 0 - unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, - the filter works byte per byte (bytewidth = 1) - precon is the previous unfiltered scanline, recon the result, scanline the current one - the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead - recon and scanline MAY be the same memory address! precon must be disjoint. - */ - - size_t i; - switch(filterType) - { - case 0: - for(i = 0; i != length; ++i) recon[i] = scanline[i]; - break; - case 1: - for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; - for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth]; - break; - case 2: - if(precon) - { - for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i]; - } - else - { - for(i = 0; i != length; ++i) recon[i] = scanline[i]; - } - break; - case 3: - if(precon) - { - for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1); - for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1); - } - else - { - for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; - for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1); - } - break; - case 4: - if(precon) - { - for(i = 0; i != bytewidth; ++i) - { - recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/ - } - for(i = bytewidth; i < length; ++i) - { - recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); - } - } - else - { - for(i = 0; i != bytewidth; ++i) - { - recon[i] = scanline[i]; - } - for(i = bytewidth; i < length; ++i) - { - /*paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth]*/ - recon[i] = (scanline[i] + recon[i - bytewidth]); - } - } - break; - default: return 36; /*error: unexisting filter type given*/ - } - return 0; -} - -static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) -{ - /* - For PNG filter method 0 - this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 seven times) - out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline - w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel - in and out are allowed to be the same memory address (but aren't the same size since in has the extra filter bytes) - */ - - unsigned y; - unsigned char* prevline = 0; - - /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ - size_t bytewidth = (bpp + 7) / 8; - size_t linebytes = (w * bpp + 7) / 8; - - for(y = 0; y < h; ++y) - { - size_t outindex = linebytes * y; - size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ - unsigned char filterType = in[inindex]; - - CERROR_TRY_RETURN(unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes)); - - prevline = &out[outindex]; - } - - return 0; -} - -/* -in: Adam7 interlaced image, with no padding bits between scanlines, but between - reduced images so that each reduced image starts at a byte. -out: the same pixels, but re-ordered so that they're now a non-interlaced image with size w*h -bpp: bits per pixel -out has the following size in bits: w * h * bpp. -in is possibly bigger due to padding bits between reduced images. -out must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation -(because that's likely a little bit faster) -NOTE: comments about padding bits are only relevant if bpp < 8 -*/ -static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) -{ - unsigned passw[7], passh[7]; - size_t filter_passstart[8], padded_passstart[8], passstart[8]; - unsigned i; - - Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); - - if(bpp >= 8) - { - for(i = 0; i != 7; ++i) - { - unsigned x, y, b; - size_t bytewidth = bpp / 8; - for(y = 0; y < passh[i]; ++y) - for(x = 0; x < passw[i]; ++x) - { - size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth; - size_t pixeloutstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; - for(b = 0; b < bytewidth; ++b) - { - out[pixeloutstart + b] = in[pixelinstart + b]; - } - } - } - } - else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ - { - for(i = 0; i != 7; ++i) - { - unsigned x, y, b; - unsigned ilinebits = bpp * passw[i]; - unsigned olinebits = bpp * w; - size_t obp, ibp; /*bit pointers (for out and in buffer)*/ - for(y = 0; y < passh[i]; ++y) - for(x = 0; x < passw[i]; ++x) - { - ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp); - obp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; - for(b = 0; b < bpp; ++b) - { - unsigned char bit = readBitFromReversedStream(&ibp, in); - /*note that this function assumes the out buffer is completely 0, use setBitOfReversedStream otherwise*/ - setBitOfReversedStream0(&obp, out, bit); - } - } - } - } -} - -static void removePaddingBits(unsigned char* out, const unsigned char* in, - size_t olinebits, size_t ilinebits, unsigned h) -{ - /* - After filtering there are still padding bits if scanlines have non multiple of 8 bit amounts. They need - to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers - for the Adam7 code, the color convert code and the output to the user. - in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must - have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits - also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7 - only useful if (ilinebits - olinebits) is a value in the range 1..7 - */ - unsigned y; - size_t diff = ilinebits - olinebits; - size_t ibp = 0, obp = 0; /*input and output bit pointers*/ - for(y = 0; y < h; ++y) - { - size_t x; - for(x = 0; x < olinebits; ++x) - { - unsigned char bit = readBitFromReversedStream(&ibp, in); - setBitOfReversedStream(&obp, out, bit); - } - ibp += diff; - } -} - -/*out must be buffer big enough to contain full image, and in must contain the full decompressed data from -the IDAT chunks (with filter index bytes and possible padding bits) -return value is error*/ -static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, - unsigned w, unsigned h, const LodePNGInfo* info_png) -{ - /* - This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype. - Steps: - *) if no Adam7: 1) unfilter 2) remove padding bits (= posible extra bits per scanline if bpp < 8) - *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace - NOTE: the in buffer will be overwritten with intermediate data! - */ - unsigned bpp = lodepng_get_bpp(&info_png->color); - if(bpp == 0) return 31; /*error: invalid colortype*/ - - if(info_png->interlace_method == 0) - { - if(bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) - { - CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp)); - removePaddingBits(out, in, w * bpp, ((w * bpp + 7) / 8) * 8, h); - } - /*we can immediately filter into the out buffer, no other steps needed*/ - else CERROR_TRY_RETURN(unfilter(out, in, w, h, bpp)); - } - else /*interlace_method is 1 (Adam7)*/ - { - unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; - unsigned i; - - Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); - - for(i = 0; i != 7; ++i) - { - CERROR_TRY_RETURN(unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp)); - /*TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline, - move bytes instead of bits or move not at all*/ - if(bpp < 8) - { - /*remove padding bits in scanlines; after this there still may be padding - bits between the different reduced images: each reduced image still starts nicely at a byte*/ - removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp, - ((passw[i] * bpp + 7) / 8) * 8, passh[i]); - } - } - - Adam7_deinterlace(out, in, w, h, bpp); - } - - return 0; -} - -static unsigned readChunk_PLTE(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) -{ - unsigned pos = 0, i; - if(color->palette) lodepng_free(color->palette); - color->palettesize = chunkLength / 3; - color->palette = (unsigned char*)lodepng_malloc(4 * color->palettesize); - if(!color->palette && color->palettesize) - { - color->palettesize = 0; - return 83; /*alloc fail*/ - } - if(color->palettesize > 256) return 38; /*error: palette too big*/ - - for(i = 0; i != color->palettesize; ++i) - { - color->palette[4 * i + 0] = data[pos++]; /*R*/ - color->palette[4 * i + 1] = data[pos++]; /*G*/ - color->palette[4 * i + 2] = data[pos++]; /*B*/ - color->palette[4 * i + 3] = 255; /*alpha*/ - } - - return 0; /* OK */ -} - -static unsigned readChunk_tRNS(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) -{ - unsigned i; - if(color->colortype == LCT_PALETTE) - { - /*error: more alpha values given than there are palette entries*/ - if(chunkLength > color->palettesize) return 38; - - for(i = 0; i != chunkLength; ++i) color->palette[4 * i + 3] = data[i]; - } - else if(color->colortype == LCT_GREY) - { - /*error: this chunk must be 2 bytes for greyscale image*/ - if(chunkLength != 2) return 30; - - color->key_defined = 1; - color->key_r = color->key_g = color->key_b = 256u * data[0] + data[1]; - } - else if(color->colortype == LCT_RGB) - { - /*error: this chunk must be 6 bytes for RGB image*/ - if(chunkLength != 6) return 41; - - color->key_defined = 1; - color->key_r = 256u * data[0] + data[1]; - color->key_g = 256u * data[2] + data[3]; - color->key_b = 256u * data[4] + data[5]; - } - else return 42; /*error: tRNS chunk not allowed for other color models*/ - - return 0; /* OK */ -} - - -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS -/*background color chunk (bKGD)*/ -static unsigned readChunk_bKGD(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) -{ - if(info->color.colortype == LCT_PALETTE) - { - /*error: this chunk must be 1 byte for indexed color image*/ - if(chunkLength != 1) return 43; - - info->background_defined = 1; - info->background_r = info->background_g = info->background_b = data[0]; - } - else if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) - { - /*error: this chunk must be 2 bytes for greyscale image*/ - if(chunkLength != 2) return 44; - - info->background_defined = 1; - info->background_r = info->background_g = info->background_b = 256u * data[0] + data[1]; - } - else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) - { - /*error: this chunk must be 6 bytes for greyscale image*/ - if(chunkLength != 6) return 45; - - info->background_defined = 1; - info->background_r = 256u * data[0] + data[1]; - info->background_g = 256u * data[2] + data[3]; - info->background_b = 256u * data[4] + data[5]; - } - - return 0; /* OK */ -} - -/*text chunk (tEXt)*/ -static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) -{ - unsigned error = 0; - char *key = 0, *str = 0; - unsigned i; - - while(!error) /*not really a while loop, only used to break on error*/ - { - unsigned length, string2_begin; - - length = 0; - while(length < chunkLength && data[length] != 0) ++length; - /*even though it's not allowed by the standard, no error is thrown if - there's no null termination char, if the text is empty*/ - if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ - - key = (char*)lodepng_malloc(length + 1); - if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ - - key[length] = 0; - for(i = 0; i != length; ++i) key[i] = (char)data[i]; - - string2_begin = length + 1; /*skip keyword null terminator*/ - - length = (unsigned)(chunkLength < string2_begin ? 0 : chunkLength - string2_begin); - str = (char*)lodepng_malloc(length + 1); - if(!str) CERROR_BREAK(error, 83); /*alloc fail*/ - - str[length] = 0; - for(i = 0; i != length; ++i) str[i] = (char)data[string2_begin + i]; - - error = lodepng_add_text(info, key, str); - - break; - } - - lodepng_free(key); - lodepng_free(str); - - return error; -} - -/*compressed text chunk (zTXt)*/ -static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, - const unsigned char* data, size_t chunkLength) -{ - unsigned error = 0; - unsigned i; - - unsigned length, string2_begin; - char *key = 0; - ucvector decoded; - - ucvector_init(&decoded); - - while(!error) /*not really a while loop, only used to break on error*/ - { - for(length = 0; length < chunkLength && data[length] != 0; ++length) ; - if(length + 2 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ - if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ - - key = (char*)lodepng_malloc(length + 1); - if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ - - key[length] = 0; - for(i = 0; i != length; ++i) key[i] = (char)data[i]; - - if(data[length + 1] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ - - string2_begin = length + 2; - if(string2_begin > chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ - - length = (unsigned)chunkLength - string2_begin; - /*will fail if zlib error, e.g. if length is too small*/ - error = zlib_decompress(&decoded.data, &decoded.size, - (unsigned char*)(&data[string2_begin]), - length, zlibsettings); - if(error) break; - ucvector_push_back(&decoded, 0); - - error = lodepng_add_text(info, key, (char*)decoded.data); - - break; - } - - lodepng_free(key); - ucvector_cleanup(&decoded); - - return error; -} - -/*international text chunk (iTXt)*/ -static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, - const unsigned char* data, size_t chunkLength) -{ - unsigned error = 0; - unsigned i; - - unsigned length, begin, compressed; - char *key = 0, *langtag = 0, *transkey = 0; - ucvector decoded; - ucvector_init(&decoded); - - while(!error) /*not really a while loop, only used to break on error*/ - { - /*Quick check if the chunk length isn't too small. Even without check - it'd still fail with other error checks below if it's too short. This just gives a different error code.*/ - if(chunkLength < 5) CERROR_BREAK(error, 30); /*iTXt chunk too short*/ - - /*read the key*/ - for(length = 0; length < chunkLength && data[length] != 0; ++length) ; - if(length + 3 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination char, corrupt?*/ - if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ - - key = (char*)lodepng_malloc(length + 1); - if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ - - key[length] = 0; - for(i = 0; i != length; ++i) key[i] = (char)data[i]; - - /*read the compression method*/ - compressed = data[length + 1]; - if(data[length + 2] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ - - /*even though it's not allowed by the standard, no error is thrown if - there's no null termination char, if the text is empty for the next 3 texts*/ - - /*read the langtag*/ - begin = length + 3; - length = 0; - for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; - - langtag = (char*)lodepng_malloc(length + 1); - if(!langtag) CERROR_BREAK(error, 83); /*alloc fail*/ - - langtag[length] = 0; - for(i = 0; i != length; ++i) langtag[i] = (char)data[begin + i]; - - /*read the transkey*/ - begin += length + 1; - length = 0; - for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; - - transkey = (char*)lodepng_malloc(length + 1); - if(!transkey) CERROR_BREAK(error, 83); /*alloc fail*/ - - transkey[length] = 0; - for(i = 0; i != length; ++i) transkey[i] = (char)data[begin + i]; - - /*read the actual text*/ - begin += length + 1; - - length = (unsigned)chunkLength < begin ? 0 : (unsigned)chunkLength - begin; - - if(compressed) - { - /*will fail if zlib error, e.g. if length is too small*/ - error = zlib_decompress(&decoded.data, &decoded.size, - (unsigned char*)(&data[begin]), - length, zlibsettings); - if(error) break; - if(decoded.allocsize < decoded.size) decoded.allocsize = decoded.size; - ucvector_push_back(&decoded, 0); - } - else - { - if(!ucvector_resize(&decoded, length + 1)) CERROR_BREAK(error, 83 /*alloc fail*/); - - decoded.data[length] = 0; - for(i = 0; i != length; ++i) decoded.data[i] = data[begin + i]; - } - - error = lodepng_add_itext(info, key, langtag, transkey, (char*)decoded.data); - - break; - } - - lodepng_free(key); - lodepng_free(langtag); - lodepng_free(transkey); - ucvector_cleanup(&decoded); - - return error; -} - -static unsigned readChunk_tIME(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) -{ - if(chunkLength != 7) return 73; /*invalid tIME chunk size*/ - - info->time_defined = 1; - info->time.year = 256u * data[0] + data[1]; - info->time.month = data[2]; - info->time.day = data[3]; - info->time.hour = data[4]; - info->time.minute = data[5]; - info->time.second = data[6]; - - return 0; /* OK */ -} - -static unsigned readChunk_pHYs(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) -{ - if(chunkLength != 9) return 74; /*invalid pHYs chunk size*/ - - info->phys_defined = 1; - info->phys_x = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3]; - info->phys_y = 16777216u * data[4] + 65536u * data[5] + 256u * data[6] + data[7]; - info->phys_unit = data[8]; - - return 0; /* OK */ -} -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ - -/*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/ -static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, - LodePNGState* state, - const unsigned char* in, size_t insize) -{ - unsigned char IEND = 0; - const unsigned char* chunk; - size_t i; - ucvector idat; /*the data from idat chunks*/ - ucvector scanlines; - size_t predict; - size_t numpixels; - size_t outsize = 0; - - /*for unknown chunk order*/ - unsigned unknown = 0; -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS - unsigned critical_pos = 1; /*1 = after IHDR, 2 = after PLTE, 3 = after IDAT*/ -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ - - /*provide some proper output values if error will happen*/ - *out = 0; - - state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/ - if(state->error) return; - - numpixels = *w * *h; - - /*multiplication overflow*/ - if(*h != 0 && numpixels / *h != *w) CERROR_RETURN(state->error, 92); - /*multiplication overflow possible further below. Allows up to 2^31-1 pixel - bytes with 16-bit RGBA, the rest is room for filter bytes.*/ - if(numpixels > 268435455) CERROR_RETURN(state->error, 92); - - ucvector_init(&idat); - chunk = &in[33]; /*first byte of the first chunk after the header*/ - - /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. - IDAT data is put at the start of the in buffer*/ - while(!IEND && !state->error) - { - unsigned chunkLength; - const unsigned char* data; /*the data in the chunk*/ - - /*error: size of the in buffer too small to contain next chunk*/ - if((size_t)((chunk - in) + 12) > insize || chunk < in) - { - if(state->decoder.ignore_end) break; /*other errors may still happen though*/ - CERROR_BREAK(state->error, 30); - } - - /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/ - chunkLength = lodepng_chunk_length(chunk); - /*error: chunk length larger than the max PNG chunk size*/ - if(chunkLength > 2147483647) - { - if(state->decoder.ignore_end) break; /*other errors may still happen though*/ - CERROR_BREAK(state->error, 63); - } - - if((size_t)((chunk - in) + chunkLength + 12) > insize || (chunk + chunkLength + 12) < in) - { - CERROR_BREAK(state->error, 64); /*error: size of the in buffer too small to contain next chunk*/ - } - - data = lodepng_chunk_data_const(chunk); - - /*IDAT chunk, containing compressed image data*/ - if(lodepng_chunk_type_equals(chunk, "IDAT")) - { - size_t oldsize = idat.size; - if(!ucvector_resize(&idat, oldsize + chunkLength)) CERROR_BREAK(state->error, 83 /*alloc fail*/); - for(i = 0; i != chunkLength; ++i) idat.data[oldsize + i] = data[i]; -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS - critical_pos = 3; -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ - } - /*IEND chunk*/ - else if(lodepng_chunk_type_equals(chunk, "IEND")) - { - IEND = 1; - } - /*palette chunk (PLTE)*/ - else if(lodepng_chunk_type_equals(chunk, "PLTE")) - { - state->error = readChunk_PLTE(&state->info_png.color, data, chunkLength); - if(state->error) break; -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS - critical_pos = 2; -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ - } - /*palette transparency chunk (tRNS)*/ - else if(lodepng_chunk_type_equals(chunk, "tRNS")) - { - state->error = readChunk_tRNS(&state->info_png.color, data, chunkLength); - if(state->error) break; - } -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS - /*background color chunk (bKGD)*/ - else if(lodepng_chunk_type_equals(chunk, "bKGD")) - { - state->error = readChunk_bKGD(&state->info_png, data, chunkLength); - if(state->error) break; - } - /*text chunk (tEXt)*/ - else if(lodepng_chunk_type_equals(chunk, "tEXt")) - { - if(state->decoder.read_text_chunks) - { - state->error = readChunk_tEXt(&state->info_png, data, chunkLength); - if(state->error) break; - } - } - /*compressed text chunk (zTXt)*/ - else if(lodepng_chunk_type_equals(chunk, "zTXt")) - { - if(state->decoder.read_text_chunks) - { - state->error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); - if(state->error) break; - } - } - /*international text chunk (iTXt)*/ - else if(lodepng_chunk_type_equals(chunk, "iTXt")) - { - if(state->decoder.read_text_chunks) - { - state->error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); - if(state->error) break; - } - } - else if(lodepng_chunk_type_equals(chunk, "tIME")) - { - state->error = readChunk_tIME(&state->info_png, data, chunkLength); - if(state->error) break; - } - else if(lodepng_chunk_type_equals(chunk, "pHYs")) - { - state->error = readChunk_pHYs(&state->info_png, data, chunkLength); - if(state->error) break; - } -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ - else /*it's not an implemented chunk type, so ignore it: skip over the data*/ - { - /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/ - if(!state->decoder.ignore_critical && !lodepng_chunk_ancillary(chunk)) - { - CERROR_BREAK(state->error, 69); - } - - unknown = 1; -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS - if(state->decoder.remember_unknown_chunks) - { - state->error = lodepng_chunk_append(&state->info_png.unknown_chunks_data[critical_pos - 1], - &state->info_png.unknown_chunks_size[critical_pos - 1], chunk); - if(state->error) break; - } -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ - } - - if(!state->decoder.ignore_crc && !unknown) /*check CRC if wanted, only on known chunk types*/ - { - if(lodepng_chunk_check_crc(chunk)) CERROR_BREAK(state->error, 57); /*invalid CRC*/ - } - - if(!IEND) chunk = lodepng_chunk_next_const(chunk); - } - - ucvector_init(&scanlines); - /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation. - If the decompressed size does not match the prediction, the image must be corrupt.*/ - if(state->info_png.interlace_method == 0) - { - /*The extra *h is added because this are the filter bytes every scanline starts with*/ - predict = lodepng_get_raw_size_idat(*w, *h, &state->info_png.color) + *h; - } - else - { - /*Adam-7 interlaced: predicted size is the sum of the 7 sub-images sizes*/ - const LodePNGColorMode* color = &state->info_png.color; - predict = 0; - predict += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3); - if(*w > 4) predict += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3); - predict += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, color) + ((*h + 3) >> 3); - if(*w > 2) predict += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, color) + ((*h + 3) >> 2); - predict += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, color) + ((*h + 1) >> 2); - if(*w > 1) predict += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, color) + ((*h + 1) >> 1); - predict += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, color) + ((*h + 0) >> 1); - } - if(!state->error && !ucvector_reserve(&scanlines, predict)) state->error = 83; /*alloc fail*/ - if(!state->error) - { - state->error = zlib_decompress(&scanlines.data, &scanlines.size, idat.data, - idat.size, &state->decoder.zlibsettings); - if(!state->error && scanlines.size != predict) state->error = 91; /*decompressed size doesn't match prediction*/ - } - ucvector_cleanup(&idat); - - if(!state->error) - { - outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color); - *out = (unsigned char*)lodepng_malloc(outsize); - if(!*out) state->error = 83; /*alloc fail*/ - } - if(!state->error) - { - for(i = 0; i < outsize; i++) (*out)[i] = 0; - state->error = postProcessScanlines(*out, scanlines.data, *w, *h, &state->info_png); - } - ucvector_cleanup(&scanlines); -} - -unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, - LodePNGState* state, - const unsigned char* in, size_t insize) -{ - *out = 0; - decodeGeneric(out, w, h, state, in, insize); - if(state->error) return state->error; - if(!state->decoder.color_convert || lodepng_color_mode_equal(&state->info_raw, &state->info_png.color)) - { - /*same color type, no copying or converting of data needed*/ - /*store the info_png color settings on the info_raw so that the info_raw still reflects what colortype - the raw image has to the end user*/ - if(!state->decoder.color_convert) - { - state->error = lodepng_color_mode_copy(&state->info_raw, &state->info_png.color); - if(state->error) return state->error; - } - } - else - { - /*color conversion needed; sort of copy of the data*/ - unsigned char* data = *out; - size_t outsize; - - /*TODO: check if this works according to the statement in the documentation: "The converter can convert - from greyscale input color type, to 8-bit greyscale or greyscale with alpha"*/ - if(!(state->info_raw.colortype == LCT_RGB || state->info_raw.colortype == LCT_RGBA) - && !(state->info_raw.bitdepth == 8)) - { - return 56; /*unsupported color mode conversion*/ - } - - outsize = lodepng_get_raw_size(*w, *h, &state->info_raw); - *out = (unsigned char*)lodepng_malloc(outsize); - if(!(*out)) - { - state->error = 83; /*alloc fail*/ - } - else state->error = lodepng_convert(*out, data, &state->info_raw, - &state->info_png.color, *w, *h); - lodepng_free(data); - } - return state->error; -} - -unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, - size_t insize, LodePNGColorType colortype, unsigned bitdepth) -{ - unsigned error; - LodePNGState state; - lodepng_state_init(&state); - state.info_raw.colortype = colortype; - state.info_raw.bitdepth = bitdepth; - error = lodepng_decode(out, w, h, &state, in, insize); - lodepng_state_cleanup(&state); - return error; -} - -unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) -{ - return lodepng_decode_memory(out, w, h, in, insize, LCT_RGBA, 8); -} - -unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) -{ - return lodepng_decode_memory(out, w, h, in, insize, LCT_RGB, 8); -} - -#ifdef LODEPNG_COMPILE_DISK -unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename, - LodePNGColorType colortype, unsigned bitdepth) -{ - unsigned char* buffer = 0; - size_t buffersize; - unsigned error; - error = lodepng_load_file(&buffer, &buffersize, filename); - if(!error) error = lodepng_decode_memory(out, w, h, buffer, buffersize, colortype, bitdepth); - lodepng_free(buffer); - return error; -} - -unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) -{ - return lodepng_decode_file(out, w, h, filename, LCT_RGBA, 8); -} - -unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) -{ - return lodepng_decode_file(out, w, h, filename, LCT_RGB, 8); -} -#endif /*LODEPNG_COMPILE_DISK*/ - -void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings) -{ - settings->color_convert = 1; -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS - settings->read_text_chunks = 1; - settings->remember_unknown_chunks = 0; -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ - settings->ignore_crc = 0; - settings->ignore_critical = 0; - settings->ignore_end = 0; - lodepng_decompress_settings_init(&settings->zlibsettings); -} - -#endif /*LODEPNG_COMPILE_DECODER*/ - -#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) - -void lodepng_state_init(LodePNGState* state) -{ -#ifdef LODEPNG_COMPILE_DECODER - lodepng_decoder_settings_init(&state->decoder); -#endif /*LODEPNG_COMPILE_DECODER*/ -#ifdef LODEPNG_COMPILE_ENCODER - lodepng_encoder_settings_init(&state->encoder); -#endif /*LODEPNG_COMPILE_ENCODER*/ - lodepng_color_mode_init(&state->info_raw); - lodepng_info_init(&state->info_png); - state->error = 1; -} - -void lodepng_state_cleanup(LodePNGState* state) -{ - lodepng_color_mode_cleanup(&state->info_raw); - lodepng_info_cleanup(&state->info_png); -} - -void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source) -{ - lodepng_state_cleanup(dest); - *dest = *source; - lodepng_color_mode_init(&dest->info_raw); - lodepng_info_init(&dest->info_png); - dest->error = lodepng_color_mode_copy(&dest->info_raw, &source->info_raw); if(dest->error) return; - dest->error = lodepng_info_copy(&dest->info_png, &source->info_png); if(dest->error) return; -} - -#endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */ - -#ifdef LODEPNG_COMPILE_ENCODER - -/* ////////////////////////////////////////////////////////////////////////// */ -/* / PNG Encoder / */ -/* ////////////////////////////////////////////////////////////////////////// */ - -/*chunkName must be string of 4 characters*/ -static unsigned addChunk(ucvector* out, const char* chunkName, const unsigned char* data, size_t length) -{ - CERROR_TRY_RETURN(lodepng_chunk_create(&out->data, &out->size, (unsigned)length, chunkName, data)); - out->allocsize = out->size; /*fix the allocsize again*/ - return 0; -} - -static void writeSignature(ucvector* out) -{ - /*8 bytes PNG signature, aka the magic bytes*/ - ucvector_push_back(out, 137); - ucvector_push_back(out, 80); - ucvector_push_back(out, 78); - ucvector_push_back(out, 71); - ucvector_push_back(out, 13); - ucvector_push_back(out, 10); - ucvector_push_back(out, 26); - ucvector_push_back(out, 10); -} - -static unsigned addChunk_IHDR(ucvector* out, unsigned w, unsigned h, - LodePNGColorType colortype, unsigned bitdepth, unsigned interlace_method) -{ - unsigned error = 0; - ucvector header; - ucvector_init(&header); - - lodepng_add32bitInt(&header, w); /*width*/ - lodepng_add32bitInt(&header, h); /*height*/ - ucvector_push_back(&header, (unsigned char)bitdepth); /*bit depth*/ - ucvector_push_back(&header, (unsigned char)colortype); /*color type*/ - ucvector_push_back(&header, 0); /*compression method*/ - ucvector_push_back(&header, 0); /*filter method*/ - ucvector_push_back(&header, interlace_method); /*interlace method*/ - - error = addChunk(out, "IHDR", header.data, header.size); - ucvector_cleanup(&header); - - return error; -} - -static unsigned addChunk_PLTE(ucvector* out, const LodePNGColorMode* info) -{ - unsigned error = 0; - size_t i; - ucvector PLTE; - ucvector_init(&PLTE); - for(i = 0; i != info->palettesize * 4; ++i) - { - /*add all channels except alpha channel*/ - if(i % 4 != 3) ucvector_push_back(&PLTE, info->palette[i]); - } - error = addChunk(out, "PLTE", PLTE.data, PLTE.size); - ucvector_cleanup(&PLTE); - - return error; -} - -static unsigned addChunk_tRNS(ucvector* out, const LodePNGColorMode* info) -{ - unsigned error = 0; - size_t i; - ucvector tRNS; - ucvector_init(&tRNS); - if(info->colortype == LCT_PALETTE) - { - size_t amount = info->palettesize; - /*the tail of palette values that all have 255 as alpha, does not have to be encoded*/ - for(i = info->palettesize; i != 0; --i) - { - if(info->palette[4 * (i - 1) + 3] == 255) --amount; - else break; - } - /*add only alpha channel*/ - for(i = 0; i != amount; ++i) ucvector_push_back(&tRNS, info->palette[4 * i + 3]); - } - else if(info->colortype == LCT_GREY) - { - if(info->key_defined) - { - ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255)); - } - } - else if(info->colortype == LCT_RGB) - { - if(info->key_defined) - { - ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_g >> 8)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_g & 255)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_b >> 8)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_b & 255)); - } - } - - error = addChunk(out, "tRNS", tRNS.data, tRNS.size); - ucvector_cleanup(&tRNS); - - return error; -} - -static unsigned addChunk_IDAT(ucvector* out, const unsigned char* data, size_t datasize, - LodePNGCompressSettings* zlibsettings) -{ - ucvector zlibdata; - unsigned error = 0; - - /*compress with the Zlib compressor*/ - ucvector_init(&zlibdata); - error = zlib_compress(&zlibdata.data, &zlibdata.size, data, datasize, zlibsettings); - if(!error) error = addChunk(out, "IDAT", zlibdata.data, zlibdata.size); - ucvector_cleanup(&zlibdata); - - return error; -} - -static unsigned addChunk_IEND(ucvector* out) -{ - unsigned error = 0; - error = addChunk(out, "IEND", 0, 0); - return error; -} - -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS - -static unsigned addChunk_tEXt(ucvector* out, const char* keyword, const char* textstring) -{ - unsigned error = 0; - size_t i; - ucvector text; - ucvector_init(&text); - for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)keyword[i]); - if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ - ucvector_push_back(&text, 0); /*0 termination char*/ - for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)textstring[i]); - error = addChunk(out, "tEXt", text.data, text.size); - ucvector_cleanup(&text); - - return error; -} - -static unsigned addChunk_zTXt(ucvector* out, const char* keyword, const char* textstring, - LodePNGCompressSettings* zlibsettings) -{ - unsigned error = 0; - ucvector data, compressed; - size_t i, textsize = strlen(textstring); - - ucvector_init(&data); - ucvector_init(&compressed); - for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]); - if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ - ucvector_push_back(&data, 0); /*0 termination char*/ - ucvector_push_back(&data, 0); /*compression method: 0*/ - - error = zlib_compress(&compressed.data, &compressed.size, - (unsigned char*)textstring, textsize, zlibsettings); - if(!error) - { - for(i = 0; i != compressed.size; ++i) ucvector_push_back(&data, compressed.data[i]); - error = addChunk(out, "zTXt", data.data, data.size); - } - - ucvector_cleanup(&compressed); - ucvector_cleanup(&data); - return error; -} - -static unsigned addChunk_iTXt(ucvector* out, unsigned compressed, const char* keyword, const char* langtag, - const char* transkey, const char* textstring, LodePNGCompressSettings* zlibsettings) -{ - unsigned error = 0; - ucvector data; - size_t i, textsize = strlen(textstring); - - ucvector_init(&data); - - for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]); - if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ - ucvector_push_back(&data, 0); /*null termination char*/ - ucvector_push_back(&data, compressed ? 1 : 0); /*compression flag*/ - ucvector_push_back(&data, 0); /*compression method*/ - for(i = 0; langtag[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)langtag[i]); - ucvector_push_back(&data, 0); /*null termination char*/ - for(i = 0; transkey[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)transkey[i]); - ucvector_push_back(&data, 0); /*null termination char*/ - - if(compressed) - { - ucvector compressed_data; - ucvector_init(&compressed_data); - error = zlib_compress(&compressed_data.data, &compressed_data.size, - (unsigned char*)textstring, textsize, zlibsettings); - if(!error) - { - for(i = 0; i != compressed_data.size; ++i) ucvector_push_back(&data, compressed_data.data[i]); - } - ucvector_cleanup(&compressed_data); - } - else /*not compressed*/ - { - for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)textstring[i]); - } - - if(!error) error = addChunk(out, "iTXt", data.data, data.size); - ucvector_cleanup(&data); - return error; -} - -static unsigned addChunk_bKGD(ucvector* out, const LodePNGInfo* info) -{ - unsigned error = 0; - ucvector bKGD; - ucvector_init(&bKGD); - if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) - { - ucvector_push_back(&bKGD, (unsigned char)(info->background_r >> 8)); - ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); - } - else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) - { - ucvector_push_back(&bKGD, (unsigned char)(info->background_r >> 8)); - ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); - ucvector_push_back(&bKGD, (unsigned char)(info->background_g >> 8)); - ucvector_push_back(&bKGD, (unsigned char)(info->background_g & 255)); - ucvector_push_back(&bKGD, (unsigned char)(info->background_b >> 8)); - ucvector_push_back(&bKGD, (unsigned char)(info->background_b & 255)); - } - else if(info->color.colortype == LCT_PALETTE) - { - ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); /*palette index*/ - } - - error = addChunk(out, "bKGD", bKGD.data, bKGD.size); - ucvector_cleanup(&bKGD); - - return error; -} - -static unsigned addChunk_tIME(ucvector* out, const LodePNGTime* time) -{ - unsigned error = 0; - unsigned char* data = (unsigned char*)lodepng_malloc(7); - if(!data) return 83; /*alloc fail*/ - data[0] = (unsigned char)(time->year >> 8); - data[1] = (unsigned char)(time->year & 255); - data[2] = (unsigned char)time->month; - data[3] = (unsigned char)time->day; - data[4] = (unsigned char)time->hour; - data[5] = (unsigned char)time->minute; - data[6] = (unsigned char)time->second; - error = addChunk(out, "tIME", data, 7); - lodepng_free(data); - return error; -} - -static unsigned addChunk_pHYs(ucvector* out, const LodePNGInfo* info) -{ - unsigned error = 0; - ucvector data; - ucvector_init(&data); - - lodepng_add32bitInt(&data, info->phys_x); - lodepng_add32bitInt(&data, info->phys_y); - ucvector_push_back(&data, info->phys_unit); - - error = addChunk(out, "pHYs", data.data, data.size); - ucvector_cleanup(&data); - - return error; -} - -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ - -static void filterScanline(unsigned char* out, const unsigned char* scanline, const unsigned char* prevline, - size_t length, size_t bytewidth, unsigned char filterType) -{ - size_t i; - switch(filterType) - { - case 0: /*None*/ - for(i = 0; i != length; ++i) out[i] = scanline[i]; - break; - case 1: /*Sub*/ - for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; - for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - scanline[i - bytewidth]; - break; - case 2: /*Up*/ - if(prevline) - { - for(i = 0; i != length; ++i) out[i] = scanline[i] - prevline[i]; - } - else - { - for(i = 0; i != length; ++i) out[i] = scanline[i]; - } - break; - case 3: /*Average*/ - if(prevline) - { - for(i = 0; i != bytewidth; ++i) out[i] = scanline[i] - (prevline[i] >> 1); - for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - ((scanline[i - bytewidth] + prevline[i]) >> 1); - } - else - { - for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; - for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - (scanline[i - bytewidth] >> 1); - } - break; - case 4: /*Paeth*/ - if(prevline) - { - /*paethPredictor(0, prevline[i], 0) is always prevline[i]*/ - for(i = 0; i != bytewidth; ++i) out[i] = (scanline[i] - prevline[i]); - for(i = bytewidth; i < length; ++i) - { - out[i] = (scanline[i] - paethPredictor(scanline[i - bytewidth], prevline[i], prevline[i - bytewidth])); - } - } - else - { - for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; - /*paethPredictor(scanline[i - bytewidth], 0, 0) is always scanline[i - bytewidth]*/ - for(i = bytewidth; i < length; ++i) out[i] = (scanline[i] - scanline[i - bytewidth]); - } - break; - default: return; /*unexisting filter type given*/ - } -} - -/* log2 approximation. A slight bit faster than std::log. */ -static float flog2(float f) -{ - float result = 0; - while(f > 32) { result += 4; f /= 16; } - while(f > 2) { ++result; f /= 2; } - return result + 1.442695f * (f * f * f / 3 - 3 * f * f / 2 + 3 * f - 1.83333f); -} - -static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, - const LodePNGColorMode* info, const LodePNGEncoderSettings* settings) -{ - /* - For PNG filter method 0 - out must be a buffer with as size: h + (w * h * bpp + 7) / 8, because there are - the scanlines with 1 extra byte per scanline - */ - - unsigned bpp = lodepng_get_bpp(info); - /*the width of a scanline in bytes, not including the filter type*/ - size_t linebytes = (w * bpp + 7) / 8; - /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ - size_t bytewidth = (bpp + 7) / 8; - const unsigned char* prevline = 0; - unsigned x, y; - unsigned error = 0; - LodePNGFilterStrategy strategy = settings->filter_strategy; - - /* - There is a heuristic called the minimum sum of absolute differences heuristic, suggested by the PNG standard: - * If the image type is Palette, or the bit depth is smaller than 8, then do not filter the image (i.e. - use fixed filtering, with the filter None). - * (The other case) If the image type is Grayscale or RGB (with or without Alpha), and the bit depth is - not smaller than 8, then use adaptive filtering heuristic as follows: independently for each row, apply - all five filters and select the filter that produces the smallest sum of absolute values per row. - This heuristic is used if filter strategy is LFS_MINSUM and filter_palette_zero is true. - - If filter_palette_zero is true and filter_strategy is not LFS_MINSUM, the above heuristic is followed, - but for "the other case", whatever strategy filter_strategy is set to instead of the minimum sum - heuristic is used. - */ - if(settings->filter_palette_zero && - (info->colortype == LCT_PALETTE || info->bitdepth < 8)) strategy = LFS_ZERO; - - if(bpp == 0) return 31; /*error: invalid color type*/ - - if(strategy == LFS_ZERO) - { - for(y = 0; y != h; ++y) - { - size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ - size_t inindex = linebytes * y; - out[outindex] = 0; /*filter type byte*/ - filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, 0); - prevline = &in[inindex]; - } - } - else if(strategy == LFS_MINSUM) - { - /*adaptive filtering*/ - size_t sum[5]; - unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ - size_t smallest = 0; - unsigned char type, bestType = 0; - - for(type = 0; type != 5; ++type) - { - attempt[type] = (unsigned char*)lodepng_malloc(linebytes); - if(!attempt[type]) return 83; /*alloc fail*/ - } - - if(!error) - { - for(y = 0; y != h; ++y) - { - /*try the 5 filter types*/ - for(type = 0; type != 5; ++type) - { - filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); - - /*calculate the sum of the result*/ - sum[type] = 0; - if(type == 0) - { - for(x = 0; x != linebytes; ++x) sum[type] += (unsigned char)(attempt[type][x]); - } - else - { - for(x = 0; x != linebytes; ++x) - { - /*For differences, each byte should be treated as signed, values above 127 are negative - (converted to signed char). Filtertype 0 isn't a difference though, so use unsigned there. - This means filtertype 0 is almost never chosen, but that is justified.*/ - unsigned char s = attempt[type][x]; - sum[type] += s < 128 ? s : (255U - s); - } - } - - /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/ - if(type == 0 || sum[type] < smallest) - { - bestType = type; - smallest = sum[type]; - } - } - - prevline = &in[y * linebytes]; - - /*now fill the out values*/ - out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ - for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; - } - } - - for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); - } - else if(strategy == LFS_ENTROPY) - { - float sum[5]; - unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ - float smallest = 0; - unsigned type, bestType = 0; - unsigned count[256]; - - for(type = 0; type != 5; ++type) - { - attempt[type] = (unsigned char*)lodepng_malloc(linebytes); - if(!attempt[type]) return 83; /*alloc fail*/ - } - - for(y = 0; y != h; ++y) - { - /*try the 5 filter types*/ - for(type = 0; type != 5; ++type) - { - filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); - for(x = 0; x != 256; ++x) count[x] = 0; - for(x = 0; x != linebytes; ++x) ++count[attempt[type][x]]; - ++count[type]; /*the filter type itself is part of the scanline*/ - sum[type] = 0; - for(x = 0; x != 256; ++x) - { - float p = count[x] / (float)(linebytes + 1); - sum[type] += count[x] == 0 ? 0 : flog2(1 / p) * p; - } - /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/ - if(type == 0 || sum[type] < smallest) - { - bestType = type; - smallest = sum[type]; - } - } - - prevline = &in[y * linebytes]; - - /*now fill the out values*/ - out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ - for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; - } - - for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); - } - else if(strategy == LFS_PREDEFINED) - { - for(y = 0; y != h; ++y) - { - size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ - size_t inindex = linebytes * y; - unsigned char type = settings->predefined_filters[y]; - out[outindex] = type; /*filter type byte*/ - filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, type); - prevline = &in[inindex]; - } - } - else if(strategy == LFS_BRUTE_FORCE) - { - /*brute force filter chooser. - deflate the scanline after every filter attempt to see which one deflates best. - This is very slow and gives only slightly smaller, sometimes even larger, result*/ - size_t size[5]; - unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ - size_t smallest = 0; - unsigned type = 0, bestType = 0; - unsigned char* dummy; - LodePNGCompressSettings zlibsettings = settings->zlibsettings; - /*use fixed tree on the attempts so that the tree is not adapted to the filtertype on purpose, - to simulate the true case where the tree is the same for the whole image. Sometimes it gives - better result with dynamic tree anyway. Using the fixed tree sometimes gives worse, but in rare - cases better compression. It does make this a bit less slow, so it's worth doing this.*/ - zlibsettings.btype = 1; - /*a custom encoder likely doesn't read the btype setting and is optimized for complete PNG - images only, so disable it*/ - zlibsettings.custom_zlib = 0; - zlibsettings.custom_deflate = 0; - for(type = 0; type != 5; ++type) - { - attempt[type] = (unsigned char*)lodepng_malloc(linebytes); - if(!attempt[type]) return 83; /*alloc fail*/ - } - for(y = 0; y != h; ++y) /*try the 5 filter types*/ - { - for(type = 0; type != 5; ++type) - { - unsigned testsize = (unsigned)linebytes; - /*if(testsize > 8) testsize /= 8;*/ /*it already works good enough by testing a part of the row*/ - - filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); - size[type] = 0; - dummy = 0; - zlib_compress(&dummy, &size[type], attempt[type], testsize, &zlibsettings); - lodepng_free(dummy); - /*check if this is smallest size (or if type == 0 it's the first case so always store the values)*/ - if(type == 0 || size[type] < smallest) - { - bestType = type; - smallest = size[type]; - } - } - prevline = &in[y * linebytes]; - out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ - for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; - } - for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); - } - else return 88; /* unknown filter strategy */ - - return error; -} - -static void addPaddingBits(unsigned char* out, const unsigned char* in, - size_t olinebits, size_t ilinebits, unsigned h) -{ - /*The opposite of the removePaddingBits function - olinebits must be >= ilinebits*/ - unsigned y; - size_t diff = olinebits - ilinebits; - size_t obp = 0, ibp = 0; /*bit pointers*/ - for(y = 0; y != h; ++y) - { - size_t x; - for(x = 0; x < ilinebits; ++x) - { - unsigned char bit = readBitFromReversedStream(&ibp, in); - setBitOfReversedStream(&obp, out, bit); - } - /*obp += diff; --> no, fill in some value in the padding bits too, to avoid - "Use of uninitialised value of size ###" warning from valgrind*/ - for(x = 0; x != diff; ++x) setBitOfReversedStream(&obp, out, 0); - } -} - -/* -in: non-interlaced image with size w*h -out: the same pixels, but re-ordered according to PNG's Adam7 interlacing, with - no padding bits between scanlines, but between reduced images so that each - reduced image starts at a byte. -bpp: bits per pixel -there are no padding bits, not between scanlines, not between reduced images -in has the following size in bits: w * h * bpp. -out is possibly bigger due to padding bits between reduced images -NOTE: comments about padding bits are only relevant if bpp < 8 -*/ -static void Adam7_interlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) -{ - unsigned passw[7], passh[7]; - size_t filter_passstart[8], padded_passstart[8], passstart[8]; - unsigned i; - - Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); - - if(bpp >= 8) - { - for(i = 0; i != 7; ++i) - { - unsigned x, y, b; - size_t bytewidth = bpp / 8; - for(y = 0; y < passh[i]; ++y) - for(x = 0; x < passw[i]; ++x) - { - size_t pixelinstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; - size_t pixeloutstart = passstart[i] + (y * passw[i] + x) * bytewidth; - for(b = 0; b < bytewidth; ++b) - { - out[pixeloutstart + b] = in[pixelinstart + b]; - } - } - } - } - else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ - { - for(i = 0; i != 7; ++i) - { - unsigned x, y, b; - unsigned ilinebits = bpp * passw[i]; - unsigned olinebits = bpp * w; - size_t obp, ibp; /*bit pointers (for out and in buffer)*/ - for(y = 0; y < passh[i]; ++y) - for(x = 0; x < passw[i]; ++x) - { - ibp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; - obp = (8 * passstart[i]) + (y * ilinebits + x * bpp); - for(b = 0; b < bpp; ++b) - { - unsigned char bit = readBitFromReversedStream(&ibp, in); - setBitOfReversedStream(&obp, out, bit); - } - } - } - } -} - -/*out must be buffer big enough to contain uncompressed IDAT chunk data, and in must contain the full image. -return value is error**/ -static unsigned preProcessScanlines(unsigned char** out, size_t* outsize, const unsigned char* in, - unsigned w, unsigned h, - const LodePNGInfo* info_png, const LodePNGEncoderSettings* settings) -{ - /* - This function converts the pure 2D image with the PNG's colortype, into filtered-padded-interlaced data. Steps: - *) if no Adam7: 1) add padding bits (= posible extra bits per scanline if bpp < 8) 2) filter - *) if adam7: 1) Adam7_interlace 2) 7x add padding bits 3) 7x filter - */ - unsigned bpp = lodepng_get_bpp(&info_png->color); - unsigned error = 0; - - if(info_png->interlace_method == 0) - { - *outsize = h + (h * ((w * bpp + 7) / 8)); /*image size plus an extra byte per scanline + possible padding bits*/ - *out = (unsigned char*)lodepng_malloc(*outsize); - if(!(*out) && (*outsize)) error = 83; /*alloc fail*/ - - if(!error) - { - /*non multiple of 8 bits per scanline, padding bits needed per scanline*/ - if(bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) - { - unsigned char* padded = (unsigned char*)lodepng_malloc(h * ((w * bpp + 7) / 8)); - if(!padded) error = 83; /*alloc fail*/ - if(!error) - { - addPaddingBits(padded, in, ((w * bpp + 7) / 8) * 8, w * bpp, h); - error = filter(*out, padded, w, h, &info_png->color, settings); - } - lodepng_free(padded); - } - else - { - /*we can immediately filter into the out buffer, no other steps needed*/ - error = filter(*out, in, w, h, &info_png->color, settings); - } - } - } - else /*interlace_method is 1 (Adam7)*/ - { - unsigned passw[7], passh[7]; - size_t filter_passstart[8], padded_passstart[8], passstart[8]; - unsigned char* adam7; - - Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); - - *outsize = filter_passstart[7]; /*image size plus an extra byte per scanline + possible padding bits*/ - *out = (unsigned char*)lodepng_malloc(*outsize); - if(!(*out)) error = 83; /*alloc fail*/ - - adam7 = (unsigned char*)lodepng_malloc(passstart[7]); - if(!adam7 && passstart[7]) error = 83; /*alloc fail*/ - - if(!error) - { - unsigned i; - - Adam7_interlace(adam7, in, w, h, bpp); - for(i = 0; i != 7; ++i) - { - if(bpp < 8) - { - unsigned char* padded = (unsigned char*)lodepng_malloc(padded_passstart[i + 1] - padded_passstart[i]); - if(!padded) ERROR_BREAK(83); /*alloc fail*/ - addPaddingBits(padded, &adam7[passstart[i]], - ((passw[i] * bpp + 7) / 8) * 8, passw[i] * bpp, passh[i]); - error = filter(&(*out)[filter_passstart[i]], padded, - passw[i], passh[i], &info_png->color, settings); - lodepng_free(padded); - } - else - { - error = filter(&(*out)[filter_passstart[i]], &adam7[padded_passstart[i]], - passw[i], passh[i], &info_png->color, settings); - } - - if(error) break; - } - } - - lodepng_free(adam7); - } - - return error; -} - -/* -palette must have 4 * palettesize bytes allocated, and given in format RGBARGBARGBARGBA... -returns 0 if the palette is opaque, -returns 1 if the palette has a single color with alpha 0 ==> color key -returns 2 if the palette is semi-translucent. -*/ -static unsigned getPaletteTranslucency(const unsigned char* palette, size_t palettesize) -{ - size_t i; - unsigned key = 0; - unsigned r = 0, g = 0, b = 0; /*the value of the color with alpha 0, so long as color keying is possible*/ - for(i = 0; i != palettesize; ++i) - { - if(!key && palette[4 * i + 3] == 0) - { - r = palette[4 * i + 0]; g = palette[4 * i + 1]; b = palette[4 * i + 2]; - key = 1; - i = (size_t)(-1); /*restart from beginning, to detect earlier opaque colors with key's value*/ - } - else if(palette[4 * i + 3] != 255) return 2; - /*when key, no opaque RGB may have key's RGB*/ - else if(key && r == palette[i * 4 + 0] && g == palette[i * 4 + 1] && b == palette[i * 4 + 2]) return 2; - } - return key; -} - -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS -static unsigned addUnknownChunks(ucvector* out, unsigned char* data, size_t datasize) -{ - unsigned char* inchunk = data; - while((size_t)(inchunk - data) < datasize) - { - CERROR_TRY_RETURN(lodepng_chunk_append(&out->data, &out->size, inchunk)); - out->allocsize = out->size; /*fix the allocsize again*/ - inchunk = lodepng_chunk_next(inchunk); - } - return 0; -} -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ - -unsigned lodepng_encode(unsigned char** out, size_t* outsize, - const unsigned char* image, unsigned w, unsigned h, - LodePNGState* state) -{ - LodePNGInfo info; - ucvector outv; - unsigned char* data = 0; /*uncompressed version of the IDAT chunk data*/ - size_t datasize = 0; - - /*provide some proper output values if error will happen*/ - *out = 0; - *outsize = 0; - state->error = 0; - - /*check input values validity*/ - if((state->info_png.color.colortype == LCT_PALETTE || state->encoder.force_palette) - && (state->info_png.color.palettesize == 0 || state->info_png.color.palettesize > 256)) - { - CERROR_RETURN_ERROR(state->error, 68); /*invalid palette size, it is only allowed to be 1-256*/ - } - if(state->encoder.zlibsettings.btype > 2) - { - CERROR_RETURN_ERROR(state->error, 61); /*error: unexisting btype*/ - } - if(state->info_png.interlace_method > 1) - { - CERROR_RETURN_ERROR(state->error, 71); /*error: unexisting interlace mode*/ - } - state->error = checkColorValidity(state->info_png.color.colortype, state->info_png.color.bitdepth); - if(state->error) return state->error; /*error: unexisting color type given*/ - state->error = checkColorValidity(state->info_raw.colortype, state->info_raw.bitdepth); - if(state->error) return state->error; /*error: unexisting color type given*/ - - /* color convert and compute scanline filter types */ - lodepng_info_init(&info); - lodepng_info_copy(&info, &state->info_png); - if(state->encoder.auto_convert) - { - state->error = lodepng_auto_choose_color(&info.color, image, w, h, &state->info_raw); - } - if (!state->error) - { - if(!lodepng_color_mode_equal(&state->info_raw, &info.color)) - { - unsigned char* converted; - size_t size = (w * h * (size_t)lodepng_get_bpp(&info.color) + 7) / 8; - - converted = (unsigned char*)lodepng_malloc(size); - if(!converted && size) state->error = 83; /*alloc fail*/ - if(!state->error) - { - state->error = lodepng_convert(converted, image, &info.color, &state->info_raw, w, h); - } - if(!state->error) preProcessScanlines(&data, &datasize, converted, w, h, &info, &state->encoder); - lodepng_free(converted); - } - else preProcessScanlines(&data, &datasize, image, w, h, &info, &state->encoder); - } - - /* output all PNG chunks */ - ucvector_init(&outv); - while(!state->error) /*while only executed once, to break on error*/ - { -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS - size_t i; -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ - /*write signature and chunks*/ - writeSignature(&outv); - /*IHDR*/ - addChunk_IHDR(&outv, w, h, info.color.colortype, info.color.bitdepth, info.interlace_method); -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS - /*unknown chunks between IHDR and PLTE*/ - if(info.unknown_chunks_data[0]) - { - state->error = addUnknownChunks(&outv, info.unknown_chunks_data[0], info.unknown_chunks_size[0]); - if(state->error) break; - } -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ - /*PLTE*/ - if(info.color.colortype == LCT_PALETTE) - { - addChunk_PLTE(&outv, &info.color); - } - if(state->encoder.force_palette && (info.color.colortype == LCT_RGB || info.color.colortype == LCT_RGBA)) - { - addChunk_PLTE(&outv, &info.color); - } - /*tRNS*/ - if(info.color.colortype == LCT_PALETTE && getPaletteTranslucency(info.color.palette, info.color.palettesize) != 0) - { - addChunk_tRNS(&outv, &info.color); - } - if((info.color.colortype == LCT_GREY || info.color.colortype == LCT_RGB) && info.color.key_defined) - { - addChunk_tRNS(&outv, &info.color); - } -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS - /*bKGD (must come between PLTE and the IDAt chunks*/ - if(info.background_defined) addChunk_bKGD(&outv, &info); - /*pHYs (must come before the IDAT chunks)*/ - if(info.phys_defined) addChunk_pHYs(&outv, &info); - - /*unknown chunks between PLTE and IDAT*/ - if(info.unknown_chunks_data[1]) - { - state->error = addUnknownChunks(&outv, info.unknown_chunks_data[1], info.unknown_chunks_size[1]); - if(state->error) break; - } -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ - /*IDAT (multiple IDAT chunks must be consecutive)*/ - state->error = addChunk_IDAT(&outv, data, datasize, &state->encoder.zlibsettings); - if(state->error) break; -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS - /*tIME*/ - if(info.time_defined) addChunk_tIME(&outv, &info.time); - /*tEXt and/or zTXt*/ - for(i = 0; i != info.text_num; ++i) - { - if(strlen(info.text_keys[i]) > 79) - { - state->error = 66; /*text chunk too large*/ - break; - } - if(strlen(info.text_keys[i]) < 1) - { - state->error = 67; /*text chunk too small*/ - break; - } - if(state->encoder.text_compression) - { - addChunk_zTXt(&outv, info.text_keys[i], info.text_strings[i], &state->encoder.zlibsettings); - } - else - { - addChunk_tEXt(&outv, info.text_keys[i], info.text_strings[i]); - } - } - /*LodePNG version id in text chunk*/ - if(state->encoder.add_id) - { - unsigned alread_added_id_text = 0; - for(i = 0; i != info.text_num; ++i) - { - if(!strcmp(info.text_keys[i], "LodePNG")) - { - alread_added_id_text = 1; - break; - } - } - if(alread_added_id_text == 0) - { - addChunk_tEXt(&outv, "LodePNG", LODEPNG_VERSION_STRING); /*it's shorter as tEXt than as zTXt chunk*/ - } - } - /*iTXt*/ - for(i = 0; i != info.itext_num; ++i) - { - if(strlen(info.itext_keys[i]) > 79) - { - state->error = 66; /*text chunk too large*/ - break; - } - if(strlen(info.itext_keys[i]) < 1) - { - state->error = 67; /*text chunk too small*/ - break; - } - addChunk_iTXt(&outv, state->encoder.text_compression, - info.itext_keys[i], info.itext_langtags[i], info.itext_transkeys[i], info.itext_strings[i], - &state->encoder.zlibsettings); - } - - /*unknown chunks between IDAT and IEND*/ - if(info.unknown_chunks_data[2]) - { - state->error = addUnknownChunks(&outv, info.unknown_chunks_data[2], info.unknown_chunks_size[2]); - if(state->error) break; - } -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ - addChunk_IEND(&outv); - - break; /*this isn't really a while loop; no error happened so break out now!*/ - } - - lodepng_info_cleanup(&info); - lodepng_free(data); - /*instead of cleaning the vector up, give it to the output*/ - *out = outv.data; - *outsize = outv.size; - - return state->error; -} - -unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, const unsigned char* image, - unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) -{ - unsigned error; - LodePNGState state; - lodepng_state_init(&state); - state.info_raw.colortype = colortype; - state.info_raw.bitdepth = bitdepth; - state.info_png.color.colortype = colortype; - state.info_png.color.bitdepth = bitdepth; - lodepng_encode(out, outsize, image, w, h, &state); - error = state.error; - lodepng_state_cleanup(&state); - return error; -} - -unsigned lodepng_encode32(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) -{ - return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGBA, 8); -} - -unsigned lodepng_encode24(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) -{ - return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGB, 8); -} - -#ifdef LODEPNG_COMPILE_DISK -unsigned lodepng_encode_file(const char* filename, const unsigned char* image, unsigned w, unsigned h, - LodePNGColorType colortype, unsigned bitdepth) -{ - unsigned char* buffer; - size_t buffersize; - unsigned error = lodepng_encode_memory(&buffer, &buffersize, image, w, h, colortype, bitdepth); - if(!error) error = lodepng_save_file(buffer, buffersize, filename); - lodepng_free(buffer); - return error; -} - -unsigned lodepng_encode32_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) -{ - return lodepng_encode_file(filename, image, w, h, LCT_RGBA, 8); -} - -unsigned lodepng_encode24_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) -{ - return lodepng_encode_file(filename, image, w, h, LCT_RGB, 8); -} -#endif /*LODEPNG_COMPILE_DISK*/ - -void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings) -{ - lodepng_compress_settings_init(&settings->zlibsettings); - settings->filter_palette_zero = 1; - settings->filter_strategy = LFS_MINSUM; - settings->auto_convert = 1; - settings->force_palette = 0; - settings->predefined_filters = 0; -#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS - settings->add_id = 0; - settings->text_compression = 1; -#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ -} - -#endif /*LODEPNG_COMPILE_ENCODER*/ -#endif /*LODEPNG_COMPILE_PNG*/ - -#ifdef LODEPNG_COMPILE_ERROR_TEXT -/* -This returns the description of a numerical error code in English. This is also -the documentation of all the error codes. -*/ -const char* lodepng_error_text(unsigned code) -{ - switch(code) - { - case 0: return "no error, everything went ok"; - case 1: return "nothing done yet"; /*the Encoder/Decoder has done nothing yet, error checking makes no sense yet*/ - case 10: return "end of input memory reached without huffman end code"; /*while huffman decoding*/ - case 11: return "error in code tree made it jump outside of huffman tree"; /*while huffman decoding*/ - case 13: return "problem while processing dynamic deflate block"; - case 14: return "problem while processing dynamic deflate block"; - case 15: return "problem while processing dynamic deflate block"; - case 16: return "unexisting code while processing dynamic deflate block"; - case 17: return "end of out buffer memory reached while inflating"; - case 18: return "invalid distance code while inflating"; - case 19: return "end of out buffer memory reached while inflating"; - case 20: return "invalid deflate block BTYPE encountered while decoding"; - case 21: return "NLEN is not ones complement of LEN in a deflate block"; - /*end of out buffer memory reached while inflating: - This can happen if the inflated deflate data is longer than the amount of bytes required to fill up - all the pixels of the image, given the color depth and image dimensions. Something that doesn't - happen in a normal, well encoded, PNG image.*/ - case 22: return "end of out buffer memory reached while inflating"; - case 23: return "end of in buffer memory reached while inflating"; - case 24: return "invalid FCHECK in zlib header"; - case 25: return "invalid compression method in zlib header"; - case 26: return "FDICT encountered in zlib header while it's not used for PNG"; - case 27: return "PNG file is smaller than a PNG header"; - /*Checks the magic file header, the first 8 bytes of the PNG file*/ - case 28: return "incorrect PNG signature, it's no PNG or corrupted"; - case 29: return "first chunk is not the header chunk"; - case 30: return "chunk length too large, chunk broken off at end of file"; - case 31: return "illegal PNG color type or bpp"; - case 32: return "illegal PNG compression method"; - case 33: return "illegal PNG filter method"; - case 34: return "illegal PNG interlace method"; - case 35: return "chunk length of a chunk is too large or the chunk too small"; - case 36: return "illegal PNG filter type encountered"; - case 37: return "illegal bit depth for this color type given"; - case 38: return "the palette is too big"; /*more than 256 colors*/ - case 39: return "more palette alpha values given in tRNS chunk than there are colors in the palette"; - case 40: return "tRNS chunk has wrong size for greyscale image"; - case 41: return "tRNS chunk has wrong size for RGB image"; - case 42: return "tRNS chunk appeared while it was not allowed for this color type"; - case 43: return "bKGD chunk has wrong size for palette image"; - case 44: return "bKGD chunk has wrong size for greyscale image"; - case 45: return "bKGD chunk has wrong size for RGB image"; - case 48: return "empty input buffer given to decoder. Maybe caused by non-existing file?"; - case 49: return "jumped past memory while generating dynamic huffman tree"; - case 50: return "jumped past memory while generating dynamic huffman tree"; - case 51: return "jumped past memory while inflating huffman block"; - case 52: return "jumped past memory while inflating"; - case 53: return "size of zlib data too small"; - case 54: return "repeat symbol in tree while there was no value symbol yet"; - /*jumped past tree while generating huffman tree, this could be when the - tree will have more leaves than symbols after generating it out of the - given lenghts. They call this an oversubscribed dynamic bit lengths tree in zlib.*/ - case 55: return "jumped past tree while generating huffman tree"; - case 56: return "given output image colortype or bitdepth not supported for color conversion"; - case 57: return "invalid CRC encountered (checking CRC can be disabled)"; - case 58: return "invalid ADLER32 encountered (checking ADLER32 can be disabled)"; - case 59: return "requested color conversion not supported"; - case 60: return "invalid window size given in the settings of the encoder (must be 0-32768)"; - case 61: return "invalid BTYPE given in the settings of the encoder (only 0, 1 and 2 are allowed)"; - /*LodePNG leaves the choice of RGB to greyscale conversion formula to the user.*/ - case 62: return "conversion from color to greyscale not supported"; - case 63: return "length of a chunk too long, max allowed for PNG is 2147483647 bytes per chunk"; /*(2^31-1)*/ - /*this would result in the inability of a deflated block to ever contain an end code. It must be at least 1.*/ - case 64: return "the length of the END symbol 256 in the Huffman tree is 0"; - case 66: return "the length of a text chunk keyword given to the encoder is longer than the maximum of 79 bytes"; - case 67: return "the length of a text chunk keyword given to the encoder is smaller than the minimum of 1 byte"; - case 68: return "tried to encode a PLTE chunk with a palette that has less than 1 or more than 256 colors"; - case 69: return "unknown chunk type with 'critical' flag encountered by the decoder"; - case 71: return "unexisting interlace mode given to encoder (must be 0 or 1)"; - case 72: return "while decoding, unexisting compression method encountering in zTXt or iTXt chunk (it must be 0)"; - case 73: return "invalid tIME chunk size"; - case 74: return "invalid pHYs chunk size"; - /*length could be wrong, or data chopped off*/ - case 75: return "no null termination char found while decoding text chunk"; - case 76: return "iTXt chunk too short to contain required bytes"; - case 77: return "integer overflow in buffer size"; - case 78: return "failed to open file for reading"; /*file doesn't exist or couldn't be opened for reading*/ - case 79: return "failed to open file for writing"; - case 80: return "tried creating a tree of 0 symbols"; - case 81: return "lazy matching at pos 0 is impossible"; - case 82: return "color conversion to palette requested while a color isn't in palette"; - case 83: return "memory allocation failed"; - case 84: return "given image too small to contain all pixels to be encoded"; - case 86: return "impossible offset in lz77 encoding (internal bug)"; - case 87: return "must provide custom zlib function pointer if LODEPNG_COMPILE_ZLIB is not defined"; - case 88: return "invalid filter strategy given for LodePNGEncoderSettings.filter_strategy"; - case 89: return "text chunk keyword too short or long: must have size 1-79"; - /*the windowsize in the LodePNGCompressSettings. Requiring POT(==> & instead of %) makes encoding 12% faster.*/ - case 90: return "windowsize must be a power of two"; - case 91: return "invalid decompressed idat size"; - case 92: return "too many pixels, not supported"; - case 93: return "zero width or height is invalid"; - case 94: return "header chunk must have a size of 13 bytes"; - } - return "unknown error code"; -} -#endif /*LODEPNG_COMPILE_ERROR_TEXT*/ - -/* ////////////////////////////////////////////////////////////////////////// */ -/* ////////////////////////////////////////////////////////////////////////// */ -/* // C++ Wrapper // */ -/* ////////////////////////////////////////////////////////////////////////// */ -/* ////////////////////////////////////////////////////////////////////////// */ - -#ifdef LODEPNG_COMPILE_CPP -namespace lodepng -{ - -#ifdef LODEPNG_COMPILE_DISK -unsigned load_file(std::vector& buffer, const std::string& filename) -{ - long size = lodepng_filesize(filename.c_str()); - if(size < 0) return 78; - buffer.resize((size_t)size); - return size == 0 ? 0 : lodepng_buffer_file(&buffer[0], (size_t)size, filename.c_str()); -} - -/*write given buffer to the file, overwriting the file, it doesn't append to it.*/ -unsigned save_file(const std::vector& buffer, const std::string& filename) -{ - return lodepng_save_file(buffer.empty() ? 0 : &buffer[0], buffer.size(), filename.c_str()); -} -#endif /* LODEPNG_COMPILE_DISK */ - -#ifdef LODEPNG_COMPILE_ZLIB -#ifdef LODEPNG_COMPILE_DECODER -unsigned decompress(std::vector& out, const unsigned char* in, size_t insize, - const LodePNGDecompressSettings& settings) -{ - unsigned char* buffer = 0; - size_t buffersize = 0; - unsigned error = zlib_decompress(&buffer, &buffersize, in, insize, &settings); - if(buffer) - { - out.insert(out.end(), &buffer[0], &buffer[buffersize]); - lodepng_free(buffer); - } - return error; -} - -unsigned decompress(std::vector& out, const std::vector& in, - const LodePNGDecompressSettings& settings) -{ - return decompress(out, in.empty() ? 0 : &in[0], in.size(), settings); -} -#endif /* LODEPNG_COMPILE_DECODER */ - -#ifdef LODEPNG_COMPILE_ENCODER -unsigned compress(std::vector& out, const unsigned char* in, size_t insize, - const LodePNGCompressSettings& settings) -{ - unsigned char* buffer = 0; - size_t buffersize = 0; - unsigned error = zlib_compress(&buffer, &buffersize, in, insize, &settings); - if(buffer) - { - out.insert(out.end(), &buffer[0], &buffer[buffersize]); - lodepng_free(buffer); - } - return error; -} - -unsigned compress(std::vector& out, const std::vector& in, - const LodePNGCompressSettings& settings) -{ - return compress(out, in.empty() ? 0 : &in[0], in.size(), settings); -} -#endif /* LODEPNG_COMPILE_ENCODER */ -#endif /* LODEPNG_COMPILE_ZLIB */ - - -#ifdef LODEPNG_COMPILE_PNG - -State::State() -{ - lodepng_state_init(this); -} - -State::State(const State& other) -{ - lodepng_state_init(this); - lodepng_state_copy(this, &other); -} - -State::~State() -{ - lodepng_state_cleanup(this); -} - -State& State::operator=(const State& other) -{ - lodepng_state_copy(this, &other); - return *this; -} - -#ifdef LODEPNG_COMPILE_DECODER - -unsigned decode(std::vector& out, unsigned& w, unsigned& h, const unsigned char* in, - size_t insize, LodePNGColorType colortype, unsigned bitdepth) -{ - unsigned char* buffer; - unsigned error = lodepng_decode_memory(&buffer, &w, &h, in, insize, colortype, bitdepth); - if(buffer && !error) - { - State state; - state.info_raw.colortype = colortype; - state.info_raw.bitdepth = bitdepth; - size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw); - out.insert(out.end(), &buffer[0], &buffer[buffersize]); - lodepng_free(buffer); - } - return error; -} - -unsigned decode(std::vector& out, unsigned& w, unsigned& h, - const std::vector& in, LodePNGColorType colortype, unsigned bitdepth) -{ - return decode(out, w, h, in.empty() ? 0 : &in[0], (unsigned)in.size(), colortype, bitdepth); -} - -unsigned decode(std::vector& out, unsigned& w, unsigned& h, - State& state, - const unsigned char* in, size_t insize) -{ - unsigned char* buffer = NULL; - unsigned error = lodepng_decode(&buffer, &w, &h, &state, in, insize); - if(buffer && !error) - { - size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw); - out.insert(out.end(), &buffer[0], &buffer[buffersize]); - } - lodepng_free(buffer); - return error; -} - -unsigned decode(std::vector& out, unsigned& w, unsigned& h, - State& state, - const std::vector& in) -{ - return decode(out, w, h, state, in.empty() ? 0 : &in[0], in.size()); -} - -#ifdef LODEPNG_COMPILE_DISK -unsigned decode(std::vector& out, unsigned& w, unsigned& h, const std::string& filename, - LodePNGColorType colortype, unsigned bitdepth) -{ - std::vector buffer; - unsigned error = load_file(buffer, filename); - if(error) return error; - return decode(out, w, h, buffer, colortype, bitdepth); -} -#endif /* LODEPNG_COMPILE_DECODER */ -#endif /* LODEPNG_COMPILE_DISK */ - -#ifdef LODEPNG_COMPILE_ENCODER -unsigned encode(std::vector& out, const unsigned char* in, unsigned w, unsigned h, - LodePNGColorType colortype, unsigned bitdepth) -{ - unsigned char* buffer; - size_t buffersize; - unsigned error = lodepng_encode_memory(&buffer, &buffersize, in, w, h, colortype, bitdepth); - if(buffer) - { - out.insert(out.end(), &buffer[0], &buffer[buffersize]); - lodepng_free(buffer); - } - return error; -} - -unsigned encode(std::vector& out, - const std::vector& in, unsigned w, unsigned h, - LodePNGColorType colortype, unsigned bitdepth) -{ - if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; - return encode(out, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); -} - -unsigned encode(std::vector& out, - const unsigned char* in, unsigned w, unsigned h, - State& state) -{ - unsigned char* buffer; - size_t buffersize; - unsigned error = lodepng_encode(&buffer, &buffersize, in, w, h, &state); - if(buffer) - { - out.insert(out.end(), &buffer[0], &buffer[buffersize]); - lodepng_free(buffer); - } - return error; -} - -unsigned encode(std::vector& out, - const std::vector& in, unsigned w, unsigned h, - State& state) -{ - if(lodepng_get_raw_size(w, h, &state.info_raw) > in.size()) return 84; - return encode(out, in.empty() ? 0 : &in[0], w, h, state); -} - -#ifdef LODEPNG_COMPILE_DISK -unsigned encode(const std::string& filename, - const unsigned char* in, unsigned w, unsigned h, - LodePNGColorType colortype, unsigned bitdepth) -{ - std::vector buffer; - unsigned error = encode(buffer, in, w, h, colortype, bitdepth); - if(!error) error = save_file(buffer, filename); - return error; -} - -unsigned encode(const std::string& filename, - const std::vector& in, unsigned w, unsigned h, - LodePNGColorType colortype, unsigned bitdepth) -{ - if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; - return encode(filename, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); -} -#endif /* LODEPNG_COMPILE_DISK */ -#endif /* LODEPNG_COMPILE_ENCODER */ -#endif /* LODEPNG_COMPILE_PNG */ -} /* namespace lodepng */ -#endif /*LODEPNG_COMPILE_CPP*/ diff --git a/source/pp2d/pp2d.c b/source/pp2d/pp2d.c deleted file mode 100644 index a19c1f7..0000000 --- a/source/pp2d/pp2d.c +++ /dev/null @@ -1,969 +0,0 @@ -/* MIT License - * - * Copyright (c) 2017 Bernardo Giordano - * - * 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. - * - * https://discord.gg/bGKEyfY - */ - -/** - * Plug & Play 2D - * @file pp2d.c - * @author Bernardo Giordano - * @date 17 January 2018 - * @brief pp2d implementation - */ - -#include "pp2d.h" -#include "libnsbmp.h" -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" -#undef STB_IMAGE_IMPLEMENTATION - -static DVLB_s* vshader_dvlb; -static shaderProgram_s program; -static int uLoc_projection; -static C3D_Mtx projectionTopLeft; -static C3D_Mtx projectionTopRight; -static C3D_Mtx projectionBot; -static C3D_Tex* glyphSheets; -static float s_textScale; -static textVertex_s* textVtxArray; -static int textVtxArrayPos; -static C3D_RenderTarget* topLeft; -static C3D_RenderTarget* topRight; -static C3D_RenderTarget* bot; - -static struct { - GPU_TEXTURE_FILTER_PARAM magFilter; - GPU_TEXTURE_FILTER_PARAM minFilter; -} textureFilters; - -static struct { - size_t id; - int x; - int y; - int xbegin; - int ybegin; - int width; - int height; - u32 color; - flipType fliptype; - float scaleX; - float scaleY; - float angle; - float depth; - bool initialized; -} textureData; - -static struct { - C3D_Tex tex; - u32 width; - u32 height; - bool allocated; -} textures[MAX_TEXTURES]; - -static void pp2d_add_text_vertex(float vx, float vy, float vz, float tx, float ty); -static bool pp2d_add_quad(int x, int y, int height, int width, float left, float right, float top, float bottom, float depth); -static u32 pp2d_get_next_pow2(u32 n); -static void pp2d_get_text_size_internal(float* width, float* height, float scaleX, float scaleY, int wrapX, const char* text); -static void pp2d_set_text_color(u32 color); - -static void pp2d_add_text_vertex(float vx, float vy, float vz, float tx, float ty) -{ - textVertex_s* vtx = &textVtxArray[textVtxArrayPos++]; - vtx->position[0] = vx; - vtx->position[1] = vy; - vtx->position[2] = vz; - vtx->texcoord[0] = tx; - vtx->texcoord[1] = ty; -} - -void pp2d_begin_draw(gfxScreen_t target, gfx3dSide_t side) -{ - C3D_FrameBegin(C3D_FRAME_SYNCDRAW); - textVtxArrayPos = 0; - pp2d_draw_on(target, side); -} - -void pp2d_draw_on(gfxScreen_t target, gfx3dSide_t side) -{ - if (target == GFX_TOP) { - C3D_FrameDrawOn(side == GFX_LEFT ? topLeft : topRight); - C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, side == GFX_LEFT ? &projectionTopLeft : &projectionTopRight); - } else { - C3D_FrameDrawOn(bot); - C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projectionBot); - } -} - -void pp2d_draw_rectangle(int x, int y, int width, int height, u32 color) -{ - C3D_TexEnv* env = C3D_GetTexEnv(0); - C3D_TexEnvSrc(env, C3D_Both, GPU_CONSTANT, GPU_CONSTANT, 0); - C3D_TexEnvOpRgb(env, 0, 0, 0); - C3D_TexEnvOpAlpha(env, 0, 0, 0); - C3D_TexEnvFunc(env, C3D_RGB, GPU_INTERPOLATE); - C3D_TexEnvColor(env, color); - - if (pp2d_add_quad(x, y, height, width, 0, 0, 0, 0, DEFAULT_DEPTH)) - { - C3D_DrawArrays(GPU_TRIANGLE_STRIP, textVtxArrayPos - 4, 4); - } -} - -void pp2d_draw_text(float x, float y, float scaleX, float scaleY, u32 color, const char* text) -{ - pp2d_draw_text_wrap(x, y, scaleX, scaleY, color, -1, text); -} - -void pp2d_draw_text_center(gfxScreen_t target, float y, float scaleX, float scaleY, u32 color, const char* text) -{ - float width = pp2d_get_text_width(text, scaleX, scaleY); - float x = ((target == GFX_TOP ? TOP_WIDTH : BOTTOM_WIDTH) - width) / 2; - pp2d_draw_text(x, y, scaleX, scaleY, color, text); -} - -void pp2d_draw_textf(float x, float y, float scaleX, float scaleY, u32 color, const char* text, ...) -{ - char buffer[256]; - va_list args; - va_start(args, text); - vsnprintf(buffer, 256, text, args); - pp2d_draw_text(x, y, scaleX, scaleY, color, buffer); - va_end(args); -} - -void pp2d_draw_text_wrap(float x, float y, float scaleX, float scaleY, u32 color, float wrapX, const char* text) -{ - if (text == NULL) - return; - - pp2d_set_text_color(color); - - ssize_t units; - uint32_t code; - const uint8_t* p = (const uint8_t*)text; - float firstX = x; - int lastSheet = -1; - - scaleX *= s_textScale; - scaleY *= s_textScale; - - do - { - if (!*p) break; - units = decode_utf8(&code, p); - if (units == -1) - break; - p += units; - if (code == '\n' || (wrapX != -1 && x + scaleX * fontGetCharWidthInfo(fontGlyphIndexFromCodePoint(code))->charWidth >= firstX + wrapX)) - { - x = firstX; - y += scaleY*fontGetInfo()->lineFeed; - p -= code == '\n' ? 0 : 1; - } - else if (code > 0) - { - int glyphIdx = fontGlyphIndexFromCodePoint(code); - fontGlyphPos_s data; - fontCalcGlyphPos(&data, glyphIdx, GLYPH_POS_CALC_VTXCOORD, scaleX, scaleY); - - if (data.sheetIndex != lastSheet) - { - lastSheet = data.sheetIndex; - C3D_TexBind(0, &glyphSheets[lastSheet]); - } - - if ((textVtxArrayPos+4) >= TEXT_VTX_ARRAY_COUNT) - break; - - pp2d_add_text_vertex(x+data.vtxcoord.left, y+data.vtxcoord.bottom, DEFAULT_DEPTH, data.texcoord.left, data.texcoord.bottom); - pp2d_add_text_vertex(x+data.vtxcoord.right, y+data.vtxcoord.bottom, DEFAULT_DEPTH, data.texcoord.right, data.texcoord.bottom); - pp2d_add_text_vertex(x+data.vtxcoord.left, y+data.vtxcoord.top, DEFAULT_DEPTH, data.texcoord.left, data.texcoord.top); - pp2d_add_text_vertex(x+data.vtxcoord.right, y+data.vtxcoord.top, DEFAULT_DEPTH, data.texcoord.right, data.texcoord.top); - - C3D_DrawArrays(GPU_TRIANGLE_STRIP, textVtxArrayPos - 4, 4); - - x += data.xAdvance; - } - } while (code > 0); -} - -void pp2d_draw_texture(size_t id, int x, int y) -{ - pp2d_texture_select(id, x, y); - pp2d_texture_draw(); -} - -void pp2d_draw_texture_blend(size_t id, int x, int y, u32 color) -{ - pp2d_texture_select(id, x, y); - pp2d_texture_blend(color); - pp2d_texture_draw(); -} - -void pp2d_draw_texture_flip(size_t id, int x, int y, flipType fliptype) -{ - pp2d_texture_select(id, x, y); - pp2d_texture_flip(fliptype); - pp2d_texture_draw(); -} - -void pp2d_draw_texture_rotate(size_t id, int x, int y, float angle) -{ - pp2d_texture_select(id, x, y); - pp2d_texture_rotate(angle); - pp2d_texture_draw(); -} - -void pp2d_draw_texture_scale(size_t id, int x, int y, float scaleX, float scaleY) -{ - pp2d_texture_select(id, x, y); - pp2d_texture_scale(scaleX, scaleY); - pp2d_texture_draw(); -} - -void pp2d_draw_texture_part(size_t id, int x, int y, int xbegin, int ybegin, int width, int height) -{ - pp2d_texture_select_part(id, x, y, xbegin, ybegin, width, height); - pp2d_texture_draw(); -} - -void pp2d_draw_wtext(float x, float y, float scaleX, float scaleY, u32 color, const wchar_t* text) -{ - pp2d_draw_wtext_wrap(x, y, scaleX, scaleY, color, -1, text); -} - -void pp2d_draw_wtext_center(gfxScreen_t target, float y, float scaleX, float scaleY, u32 color, const wchar_t* text) -{ - float width = pp2d_get_wtext_width(text, scaleX, scaleY); - float x = ((target == GFX_TOP ? TOP_WIDTH : BOTTOM_WIDTH) - width) / 2; - pp2d_draw_wtext(x, y, scaleX, scaleY, color, text); -} - -void pp2d_draw_wtext_wrap(float x, float y, float scaleX, float scaleY, u32 color, float wrapX, const wchar_t* text) -{ - if (text == NULL) - return; - - u32 size = wcslen(text) * sizeof(wchar_t); - char buf[size]; - memset(buf, 0, size); - utf32_to_utf8((uint8_t*)buf, (uint32_t*)text, size); - buf[size - 1] = '\0'; - - pp2d_draw_text_wrap(x, y, scaleX, scaleY, color, wrapX, buf); -} - -void pp2d_draw_wtextf(float x, float y, float scaleX, float scaleY, u32 color, const wchar_t* text, ...) -{ - wchar_t buffer[256]; - va_list args; - va_start(args, text); - vswprintf(buffer, 256, text, args); - pp2d_draw_wtext(x, y, scaleX, scaleY, color, buffer); - va_end(args); -} - -void pp2d_end_draw(void) -{ - C3D_FrameEnd(0); -} - -void pp2d_exit(void) -{ - for (size_t id = 0; id < MAX_TEXTURES; id++) - { - pp2d_free_texture(id); - } - - linearFree(textVtxArray); - free(glyphSheets); - - shaderProgramFree(&program); - DVLB_Free(vshader_dvlb); - - C3D_Fini(); - gfxExit(); -} - -static bool pp2d_add_quad(int x, int y, int height, int width, float left, float right, float top, float bottom, float depth) -{ - if ((textVtxArrayPos+4) >= TEXT_VTX_ARRAY_COUNT) - return false; - - pp2d_add_text_vertex( x, y + height, depth, left, bottom); - pp2d_add_text_vertex(x + width, y + height, depth, right, bottom); - pp2d_add_text_vertex( x, y, depth, left, top); - pp2d_add_text_vertex(x + width, y, depth, right, top); - - return true; -} - -void pp2d_free_texture(size_t id) -{ - if (id >= MAX_TEXTURES) - return; - - if (!textures[id].allocated) - return; - - C3D_TexDelete(&textures[id].tex); - textures[id].width = 0; - textures[id].height = 0; - textures[id].allocated = false; -} - -Result pp2d_init(void) -{ - Result res = 0; - - gfxInitDefault(); - C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); - - topLeft = C3D_RenderTargetCreate(SCREEN_HEIGHT, TOP_WIDTH, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); - C3D_RenderTargetClear(topLeft, C3D_CLEAR_ALL, BACKGROUND_COLOR, 0); - C3D_RenderTargetSetOutput(topLeft, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); - - topRight = C3D_RenderTargetCreate(SCREEN_HEIGHT, TOP_WIDTH, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); - C3D_RenderTargetClear(topRight, C3D_CLEAR_ALL, BACKGROUND_COLOR, 0); - C3D_RenderTargetSetOutput(topRight, GFX_TOP, GFX_RIGHT, DISPLAY_TRANSFER_FLAGS); - - bot = C3D_RenderTargetCreate(SCREEN_HEIGHT, BOTTOM_WIDTH, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); - C3D_RenderTargetClear(bot, C3D_CLEAR_ALL, BACKGROUND_COLOR, 0); - C3D_RenderTargetSetOutput(bot, GFX_BOTTOM, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); - - res = fontEnsureMapped(); - if (R_FAILED(res)) - return res; - - pp2d_set_texture_filter(GPU_NEAREST, GPU_NEAREST); - -#ifdef BUILDTOOLS - vshader_dvlb = DVLB_ParseFile((u32*)vshader_shbin, vshader_shbin_len); -#else - vshader_dvlb = DVLB_ParseFile((u32*)vshader_shbin, vshader_shbin_size); -#endif - - shaderProgramInit(&program); - shaderProgramSetVsh(&program, &vshader_dvlb->DVLE[0]); - C3D_BindProgram(&program); - - uLoc_projection = shaderInstanceGetUniformLocation(program.vertexShader, "projection"); - - C3D_AttrInfo* attrInfo = C3D_GetAttrInfo(); - AttrInfo_Init(attrInfo); - AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); - AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); - - Mtx_OrthoTilt(&projectionTopLeft, 0, TOP_WIDTH, SCREEN_HEIGHT, 0.0f, 0.0f, 1.0f, true); - Mtx_OrthoTilt(&projectionTopRight, 0, TOP_WIDTH, SCREEN_HEIGHT, 0.0f, 0.0f, 1.0f, true); - Mtx_OrthoTilt(&projectionBot, 0, BOTTOM_WIDTH, SCREEN_HEIGHT, 0.0f, 0.0f, 1.0f, true); - - C3D_DepthTest(true, GPU_GEQUAL, GPU_WRITE_ALL); - - int i; - TGLP_s* glyphInfo = fontGetGlyphInfo(); - glyphSheets = malloc(sizeof(C3D_Tex)*glyphInfo->nSheets); - for (i = 0; i < glyphInfo->nSheets; i ++) - { - C3D_Tex* tex = &glyphSheets[i]; - tex->data = fontGetGlyphSheetTex(i); - tex->fmt = glyphInfo->sheetFmt; - tex->size = glyphInfo->sheetSize; - tex->width = glyphInfo->sheetWidth; - tex->height = glyphInfo->sheetHeight; - tex->param = GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) | GPU_TEXTURE_MIN_FILTER(GPU_LINEAR) - | GPU_TEXTURE_WRAP_S(GPU_CLAMP_TO_EDGE) | GPU_TEXTURE_WRAP_T(GPU_CLAMP_TO_EDGE); - tex->border = 0; - tex->lodParam = 0; - } - - charWidthInfo_s* cwi = fontGetCharWidthInfo(fontGlyphIndexFromCodePoint(0x3042)); - s_textScale = 20.0f / (cwi->glyphWidth); // 20 is glyphWidth in J machines - - textVtxArray = (textVertex_s*)linearAlloc(sizeof(textVertex_s)*TEXT_VTX_ARRAY_COUNT); - C3D_BufInfo* bufInfo = C3D_GetBufInfo(); - BufInfo_Init(bufInfo); - BufInfo_Add(bufInfo, textVtxArray, sizeof(textVertex_s), 2, 0x10); - - return 0; -} - -static u32 pp2d_get_next_pow2(u32 v) -{ - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v++; - return v >= 64 ? v : 64; -} - -float pp2d_get_text_height(const char* text, float scaleX, float scaleY) -{ - float height; - pp2d_get_text_size_internal(NULL, &height, scaleX, scaleY, -1, text); - return height; -} - -float pp2d_get_text_height_wrap(const char* text, float scaleX, float scaleY, int wrapX) -{ - float height; - pp2d_get_text_size_internal(NULL, &height, scaleX, scaleY, wrapX, text); - return height; -} - -void pp2d_get_text_size(float* width, float* height, float scaleX, float scaleY, const char* text) -{ - pp2d_get_text_size_internal(width, height, scaleX, scaleY, -1, text); -} - -static void pp2d_get_text_size_internal(float* width, float* height, float scaleX, float scaleY, int wrapX, const char* text) -{ - float maxW = 0.0f; - float w = 0.0f; - float h = 0.0f; - - ssize_t units; - uint32_t code; - float x = 0; - float firstX = x; - const uint8_t* p = (const uint8_t*)text; - - scaleX *= s_textScale; - scaleY *= s_textScale; - - do - { - if (!*p) break; - units = decode_utf8(&code, p); - if (units == -1) - break; - p += units; - if (code == '\n' || (wrapX != -1 && x + scaleX * fontGetCharWidthInfo(fontGlyphIndexFromCodePoint(code))->charWidth >= firstX + wrapX)) - { - x = firstX; - h += scaleY*fontGetInfo()->lineFeed; - p -= code == '\n' ? 0 : 1; - if (w > maxW) - maxW = w; - w = 0.f; - } - else if (code > 0) - { - float len = (scaleX * fontGetCharWidthInfo(fontGlyphIndexFromCodePoint(code))->charWidth); - w += len; - x += len; - } - } while (code > 0); - - if (width) - { - *width = w > maxW ? w : maxW; - } - - if (height) - { - h += scaleY*fontGetInfo()->lineFeed; - *height = h; - } -} - -float pp2d_get_text_width(const char* text, float scaleX, float scaleY) -{ - float width; - pp2d_get_text_size_internal(&width, NULL, scaleX, scaleY, -1, text); - return width; -} - -float pp2d_get_wtext_height(const wchar_t* text, float scaleX, float scaleY) -{ - u32 size = wcslen(text) * sizeof(wchar_t); - char buf[size]; - memset(buf, 0, size); - utf32_to_utf8((uint8_t*)buf, (uint32_t*)text, size); - buf[size - 1] = '\0'; - - float height; - pp2d_get_text_size_internal(NULL, &height, scaleX, scaleY, -1, buf); - return height; -} - -float pp2d_get_wtext_width(const wchar_t* text, float scaleX, float scaleY) -{ - u32 size = wcslen(text) * sizeof(wchar_t); - char buf[size]; - memset(buf, 0, size); - utf32_to_utf8((uint8_t*)buf, (uint32_t*)text, size); - buf[size - 1] = '\0'; - - float width; - pp2d_get_text_size_internal(&width, NULL, scaleX, scaleY, -1, buf); - return width; -} - -float pp2d_get_texture_width(size_t id) -{ - return textures[id].width; -} - -float pp2d_get_texture_height(size_t id) -{ - return textures[id].height; -} - -void pp2d_load_texture_memory(size_t id, void* buf, u32 size, u32 width, u32 height, GPU_TEXCOLOR format) -{ - u32 w_pow2 = pp2d_get_next_pow2(width); - u32 h_pow2 = pp2d_get_next_pow2(height); - - C3D_TexInit(&textures[id].tex, (u16)w_pow2, (u16)h_pow2, format); - C3D_TexSetFilter(&textures[id].tex, textureFilters.magFilter, textureFilters.minFilter); - - u32 pixelSize = size / width / height; - - textures[id].allocated = true; - textures[id].width = width; - textures[id].height = height; - - for (u32 x = 0; x < width; x++) - { - for (u32 y = 0; y < height; y++) - { - u32 dstPos = ((((y >> 3) * (w_pow2 >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3))) * pixelSize; - u32 srcPos = (y * width + x) * pixelSize; - - memcpy(&((u8*) textures[id].tex.data)[dstPos], &((u8*) buf)[srcPos], pixelSize); - } - } - - C3D_TexFlush(&textures[id].tex); -} - -static void *pp2d_create_bitmap(int width, int height, unsigned int state) -{ - (void) state; /* unused */ - return calloc(width * height, 4); -} - -static unsigned char *pp2d_get_bitmap_buf(void *bitmap) -{ - return (unsigned char *)bitmap; -} - -static size_t pp2d_get_bitmap_bpp(void *bitmap) -{ - (void) bitmap; /* unused */ - return 4; -} - -void pp2d_free_bitmap(void *bitmap) -{ - free(bitmap); -} - -static void *pp2d_bitmap_to_buf(const char *path, u32 *size) -{ - FILE *fd = fopen(path, "rb"); - - if (fd == NULL) - return NULL; - - u8 *buffer; - long lSize; - fseek(fd, 0, SEEK_END); - lSize = ftell(fd); - rewind(fd); - - buffer = (u8 *)malloc(lSize); - - if (size) - *size = lSize; - - if (!buffer) - { - fclose(fd); - return NULL; - } - - fread(buffer, 1, lSize, fd); - fclose(fd); - return buffer; -} - -void pp2d_load_texture_bmp(size_t id, const char* path) -{ - if (id >= MAX_TEXTURES) - return; - - u32 size; - u8 * buf = (u8 *)pp2d_bitmap_to_buf(path, &size); - - bmp_bitmap_callback_vt bitmap_callbacks = - { - pp2d_create_bitmap, - pp2d_free_bitmap, - pp2d_get_bitmap_buf, - pp2d_get_bitmap_bpp - }; - - bmp_result code; - bmp_image bmp; - - /* create our bmp image */ - bmp_create(&bmp, &bitmap_callbacks); - - /* analyse the BMP */ - code = bmp_analyse(&bmp, size, buf); - - if (code != BMP_OK) - { - bmp_finalise(&bmp); - return; - } - - /* decode the image */ - code = bmp_decode(&bmp); - - if (code != BMP_OK) - { - bmp_finalise(&bmp); - return; - } - - u8 * image; - image = (u8 *)bmp.bitmap; - - for (u32 x = 0; x < bmp.width; x++) - { - for (u32 y = 0; y < bmp.height; y++) - { - u32 pos = (y * bmp.width + x) * 4; - - u8 c1 = image[pos + 0]; - u8 c2 = image[pos + 1]; - u8 c3 = image[pos + 2]; - u8 c4 = image[pos + 3]; - - image[pos + 0] = c4; - image[pos + 1] = c3; - image[pos + 2] = c2; - image[pos + 3] = c1; - } - } - - pp2d_load_texture_memory(id, image, (bmp.width * bmp.height * 4), bmp.width, bmp.height, GPU_RGBA8); - - bmp_finalise(&bmp); - free(buf); -} - -void pp2d_load_texture_other(size_t id, const char* path) -{ - if (id >= MAX_TEXTURES) - return; - - int width = 0, height = 0, channel = 0; - stbi_uc *image = stbi_load(path, &width, &height, &channel, STBI_rgb_alpha); - - if ((image == NULL) || (channel != STBI_rgb_alpha)) - return; - - for (u32 x = 0; x < width; x++) - { - for (u32 y = 0; y < height; y++) - { - u32 pos = (y * width + x) * channel; - - u8 c1 = image[pos + 0]; - u8 c2 = image[pos + 1]; - u8 c3 = image[pos + 2]; - u8 c4 = image[pos + 3]; - - image[pos + 0] = c4; - image[pos + 1] = c3; - image[pos + 2] = c2; - image[pos + 3] = c1; - } - } - - pp2d_load_texture_memory(id, image, (u32)(width * height * channel), (u32)width, (u32)height, GPU_RGBA8); - stbi_image_free(image); -} - -void pp2d_load_texture_jpg(size_t id, const char* path) -{ - if (id >= MAX_TEXTURES) - return; - - int width = 0, height = 0, channel = 0; - stbi_uc *image = stbi_load(path, &width, &height, &channel, STBI_rgb); - - if ((image == NULL) || (channel != STBI_rgb)) - return; - - for (u32 x = 0; x < width; x++) - { - for (u32 y = 0; y < height; y++) - { - u32 pos = (y * width + x) * channel; - - u8 c1 = image[pos + 0]; - u8 c2 = image[pos + 1]; - u8 c3 = image[pos + 2]; - - image[pos + 0] = c3; - image[pos + 1] = c2; - image[pos + 2] = c1; - } - } - - pp2d_load_texture_memory(id, image, (u32)(width * height * channel), (u32)width, (u32)height, GPU_RGB8); - stbi_image_free(image); -} - -void pp2d_load_texture_png(size_t id, const char* path) -{ - if (id >= MAX_TEXTURES) - return; - - unsigned char *image; - unsigned width = 0, height = 0; - - lodepng_decode32_file(&image, &width, &height, path); - - for (u32 i = 0; i < width; i++) - { - for (u32 j = 0; j < height; j++) - { - u32 p = (i + j*width) * 4; - - u8 r = *(u8*)(image + p); - u8 g = *(u8*)(image + p + 1); - u8 b = *(u8*)(image + p + 2); - u8 a = *(u8*)(image + p + 3); - - *(image + p) = a; - *(image + p + 1) = b; - *(image + p + 2) = g; - *(image + p + 3) = r; - } - } - - pp2d_load_texture_memory(id, image, (u32)(width * height * 4), (u32)width, (u32)height, GPU_RGBA8); - free(image); -} - -void pp2d_set_3D(int enable) -{ - gfxSet3D(enable); -} - -void pp2d_set_screen_color(gfxScreen_t target, u32 color) -{ - if (target == GFX_TOP) - { - C3D_RenderTargetClear(topLeft, C3D_CLEAR_ALL, color, 0); - C3D_RenderTargetClear(topRight, C3D_CLEAR_ALL, color, 0); - } - else - { - C3D_RenderTargetClear(bot, C3D_CLEAR_ALL, color, 0); - } -} - -void pp2d_set_texture_filter(GPU_TEXTURE_FILTER_PARAM magFilter, GPU_TEXTURE_FILTER_PARAM minFilter) -{ - textureFilters.magFilter = magFilter; - textureFilters.minFilter = minFilter; -} - -static void pp2d_set_text_color(u32 color) -{ - C3D_TexEnv* env = C3D_GetTexEnv(0); - C3D_TexEnvSrc(env, C3D_RGB, GPU_CONSTANT, 0, 0); - C3D_TexEnvSrc(env, C3D_Alpha, GPU_TEXTURE0, GPU_CONSTANT, 0); - C3D_TexEnvOpRgb(env, 0, 0, 0); - C3D_TexEnvOpAlpha(env, 0, 0, 0); - C3D_TexEnvFunc(env, C3D_RGB, GPU_REPLACE); - C3D_TexEnvFunc(env, C3D_Alpha, GPU_MODULATE); - C3D_TexEnvColor(env, color); -} - -void pp2d_texture_select(size_t id, int x, int y) -{ - if (id >= MAX_TEXTURES) - { - textureData.initialized = false; - return; - } - - textureData.id = id; - textureData.x = x; - textureData.y = y; - textureData.xbegin = 0; - textureData.ybegin = 0; - textureData.width = textures[id].width; - textureData.height = textures[id].height; - textureData.color = PP2D_NEUTRAL; - textureData.fliptype = NONE; - textureData.scaleX = 1; - textureData.scaleY = 1; - textureData.angle = 0; - textureData.depth = DEFAULT_DEPTH; - textureData.initialized = true; -} - -void pp2d_texture_select_part(size_t id, int x, int y, int xbegin, int ybegin, int width, int height) -{ - if (id >= MAX_TEXTURES) - { - textureData.initialized = false; - return; - } - - textureData.id = id; - textureData.x = x; - textureData.y = y; - textureData.xbegin = xbegin; - textureData.ybegin = ybegin; - textureData.width = width; - textureData.height = height; - textureData.color = PP2D_NEUTRAL; - textureData.fliptype = NONE; - textureData.scaleX = 1; - textureData.scaleY = 1; - textureData.angle = 0; - textureData.depth = DEFAULT_DEPTH; - textureData.initialized = true; -} - -void pp2d_texture_blend(u32 color) -{ - textureData.color = color; -} - -void pp2d_texture_scale(float scaleX, float scaleY) -{ - textureData.scaleX = scaleX; - textureData.scaleY = scaleY; -} - -void pp2d_texture_flip(flipType fliptype) -{ - textureData.fliptype = fliptype; -} - -void pp2d_texture_rotate(float angle) -{ - textureData.angle = angle; -} - -void pp2d_texture_depth(float depth) -{ - textureData.depth = depth; -} - -void pp2d_texture_draw(void) -{ - if (!textureData.initialized) - return; - - if ((textVtxArrayPos+4) >= TEXT_VTX_ARRAY_COUNT) - return; - - size_t id = textureData.id; - - float left = (float)textureData.xbegin / (float)textures[id].tex.width; - float right = (float)(textureData.xbegin + textureData.width) / (float)textures[id].tex.width; - float top = (float)(textures[id].tex.height - textureData.ybegin) / (float)textures[id].tex.height; - float bottom = (float)(textures[id].tex.height - textureData.ybegin - textureData.height) / (float)textures[id].tex.height; - - // scaling - textureData.height *= textureData.scaleY; - textureData.width *= textureData.scaleX; - - float vert[4][2] = { - { textureData.x, textureData.height + textureData.y}, - {textureData.width + textureData.x, textureData.height + textureData.y}, - { textureData.x, textureData.y}, - {textureData.width + textureData.x, textureData.y}, - }; - - // flipping - if (textureData.fliptype == BOTH || textureData.fliptype == HORIZONTAL) - { - float tmp = left; - left = right; - right = tmp; - } - - if (textureData.fliptype == BOTH || textureData.fliptype == VERTICAL) - { - float tmp = top; - top = bottom; - bottom = tmp; - } - - // rotating - textureData.angle = fmod(textureData.angle, 360); - if (textureData.angle != 0) - { - const float rad = textureData.angle/(180/M_PI); - const float c = cosf(rad); - const float s = sinf(rad); - - const float xcenter = textureData.x + textureData.width/2.0f; - const float ycenter = textureData.y + textureData.height/2.0f; - - for (int i = 0; i < 4; i++) - { - float oldx = vert[i][0]; - float oldy = vert[i][1]; - - float newx = c * (oldx - xcenter) - s * (oldy - ycenter) + xcenter; - float newy = s * (oldx - xcenter) + c * (oldy - ycenter) + ycenter; - - vert[i][0] = newx; - vert[i][1] = newy; - } - } - - // blending - C3D_TexBind(0, &textures[id].tex); - C3D_TexEnv* env = C3D_GetTexEnv(0); - C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_CONSTANT, 0); - C3D_TexEnvOpRgb(env, 0, 0, 0); - C3D_TexEnvOpAlpha(env, 0, 0, 0); - C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE); - C3D_TexEnvColor(env, textureData.color); - - // rendering - pp2d_add_text_vertex(vert[0][0], vert[0][1], textureData.depth, left, bottom); - pp2d_add_text_vertex(vert[1][0], vert[1][1], textureData.depth, right, bottom); - pp2d_add_text_vertex(vert[2][0], vert[2][1], textureData.depth, left, top); - pp2d_add_text_vertex(vert[3][0], vert[3][1], textureData.depth, right, top); - - C3D_DrawArrays(GPU_TRIANGLE_STRIP, textVtxArrayPos - 4, 4); -} diff --git a/source/pp2d/vshader.v.pica b/source/pp2d/vshader.v.pica deleted file mode 100644 index f526f2f..0000000 --- a/source/pp2d/vshader.v.pica +++ /dev/null @@ -1,39 +0,0 @@ -; Uniforms -.fvec projection[4] - -; Constants -.constf myconst(0.0, 1.0, -1.0, 0.1) -.constf RGBA_TO_FLOAT4(0.00392156862, 0, 0, 0) -.alias zeros myconst.xxxx ; Vector full of zeros -.alias ones myconst.yyyy ; Vector full of ones - -; Outputs -.out outpos position -.out outclr color -.out outtc0 texcoord0 - -; Inputs (defined as aliases for convenience) -.alias inpos v0 -.alias intex v1 - -.bool test - -.proc main - ; Force the w component of inpos to be 1.0 - mov r0.xyz, inpos - mov r0.w, ones - - ; outpos = projectionMatrix * inpos - dp4 outpos.x, projection[0], r0 - dp4 outpos.y, projection[1], r0 - dp4 outpos.z, projection[2], r0 - dp4 outpos.w, projection[3], r0 - - ;outtc0 = intexcoord - mov outtc0, intex - - ;outclr - mul outclr, RGBA_TO_FLOAT4.xxxx, intex - - end -.end \ No newline at end of file diff --git a/source/progress_bar.c b/source/progress_bar.c index 4d4048c..e260e03 100644 --- a/source/progress_bar.c +++ b/source/progress_bar.c @@ -1,31 +1,22 @@ -#include "pp2d.h" -#include "textures.h" -#include "theme.h" +#include <3ds.h> -struct colour BottomScreen_colour; -struct colour Options_title_text_colour; -struct colour Settings_title_text_colour; -struct colour Storage_colour; +#include "common.h" +#include "config.h" +#include "progress_bar.h" +#include "C2D_helper.h" +#include "textures.h" -void ProgressBar_DisplayProgress(char * msg, char * src, u32 offset, u32 size) +void ProgressBar_DisplayProgress(char *msg, char *src, u32 offset, u32 size) { - pp2d_begin_draw(GFX_BOTTOM, GFX_LEFT); - - pp2d_draw_rectangle(0, 0, 320, 240, RGBA8(BottomScreen_colour.r, BottomScreen_colour.g, BottomScreen_colour.b, 255)); - - pp2d_draw_texture(TEXTURE_DIALOG, ((320 - (pp2d_get_texture_width(TEXTURE_DIALOG))) / 2), - ((240 - (pp2d_get_texture_height(TEXTURE_DIALOG))) / 2)); + float text_width = 0; + Draw_GetTextSize(0.48f, &text_width, NULL, src); - pp2d_draw_text(((320 - (pp2d_get_texture_width(TEXTURE_DIALOG))) / 2) + 10, ((240 - (pp2d_get_texture_height(TEXTURE_DIALOG))) / 2) + 20, - 0.45f, 0.45f, RGBA8(Settings_title_text_colour.r, Settings_title_text_colour.g, Settings_title_text_colour.b, 255), msg); + Draw_Image(config_dark_theme? dialog_dark : dialog, ((320 - (dialog.subtex->width)) / 2), ((240 - (dialog.subtex->height)) / 2)); - pp2d_draw_text(((320 - (pp2d_get_text_width(src, 0.45f, 0.45f))) / 2), ((240 - (pp2d_get_texture_height(TEXTURE_DIALOG))) / 2) + 45, - 0.45f, 0.45f, RGBA8(Options_title_text_colour.r, Options_title_text_colour.g, Options_title_text_colour.b, 255), src); + Draw_Text(((320 - (dialog.subtex->width)) / 2) + 80, ((240 - (dialog.subtex->height)) / 2) + 45, 0.48f, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR, msg); - pp2d_draw_rectangle(((320 - (pp2d_get_texture_width(TEXTURE_DIALOG))) / 2) + 20, ((240 - (pp2d_get_texture_height(TEXTURE_DIALOG))) / 2) + 70, - 240, 4, RGBA8(200, 200, 200, 255)); - pp2d_draw_rectangle(((320 - (pp2d_get_texture_width(TEXTURE_DIALOG))) / 2) + 20, ((240 - (pp2d_get_texture_height(TEXTURE_DIALOG))) / 2) + 70, - (double)offset / (double)size * 240.0, 4, RGBA8(Storage_colour.r, Storage_colour.g, Storage_colour.b, 255)); + Draw_Text(((320 - (text_width)) / 2), ((240 - (dialog.subtex->height)) / 2) + 111, 0.48f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, src); - pp2d_end_draw(); + Draw_Rect(((320 - (dialog.subtex->width)) / 2) + 80, ((240 - (dialog.subtex->height)) / 2) + 178, 240, 12, config_dark_theme? SELECTOR_COLOUR_DARK : SELECTOR_COLOUR_LIGHT); + Draw_Rect(((320 - (dialog.subtex->width)) / 2) + 80, ((240 - (dialog.subtex->height)) / 2) + 178, (double)offset / (double)size * 240.0, 12, config_dark_theme? TITLE_COLOUR : PROGRESS_COLOUR); } \ No newline at end of file diff --git a/source/misc/screenshot.c b/source/screenshot.c similarity index 100% rename from source/misc/screenshot.c rename to source/screenshot.c diff --git a/source/status_bar.c b/source/status_bar.c new file mode 100644 index 0000000..a5f0966 --- /dev/null +++ b/source/status_bar.c @@ -0,0 +1,42 @@ +#include <3ds.h> +#include + +#include "C2D_helper.h" +#include "common.h" +#include "status_bar.h" + +static char *Clock_GetCurrentTime(bool _12hour) +{ + static char buffer[10]; + + time_t unix_time = time(0); + struct tm* time_struct = gmtime((const time_t*)&unix_time); + int hours = time_struct->tm_hour; + int minutes = time_struct->tm_min; + int amOrPm = 0; + + if (_12hour) + { + if (hours < 12) + amOrPm = 1; + if (hours == 0) + hours = 12; + else if (hours > 12) + hours = hours - 12; + + if ((hours >= 1) && (hours < 10)) + snprintf(buffer, 10, "%2i:%02i %s", hours, minutes, amOrPm ? "AM" : "PM"); + else + snprintf(buffer, 10, "%2i:%02i %s", hours, minutes, amOrPm ? "AM" : "PM"); + } + + return buffer; +} + +void StatusBar_DisplayTime(void) +{ + float width = 0, height = 0; + Draw_GetTextSize(0.48f, &width, &height, Clock_GetCurrentTime(true)); + + Draw_Text(390 - width, (18 - height) / 2, 0.48f, WHITE, Clock_GetCurrentTime(true)); +} \ No newline at end of file diff --git a/source/texture.c b/source/texture.c deleted file mode 100644 index 5179f45..0000000 --- a/source/texture.c +++ /dev/null @@ -1,78 +0,0 @@ -#include "pp2d.h" -#include "textures.h" -#include "theme.h" - -void Load_Textures(void) -{ - Theme_Load(); - - pp2d_load_texture_png(TEXTURE_BACKGROUND, background_path); - pp2d_load_texture_png(TEXTURE_DIALOG, dialog_path); - pp2d_load_texture_png(TEXTURE_OPTIONS, options_path); - pp2d_load_texture_png(TEXTURE_PROPERTIES, properties_path); - pp2d_load_texture_png(TEXTURE_SELECTOR, selector_path); - - pp2d_load_texture_png(TEXTURE_ICON_APP, app_path); - pp2d_load_texture_png(TEXTURE_ICON_ARCHIVE, archive_path); - pp2d_load_texture_png(TEXTURE_ICON_AUDIO, audio_path); - pp2d_load_texture_png(TEXTURE_ICON_FILE, file_path); - pp2d_load_texture_png(TEXTURE_ICON_FOLDER, folder_path); - pp2d_load_texture_png(TEXTURE_ICON_IMG, img_path); - pp2d_load_texture_png(TEXTURE_ICON_SYSTEM, system_path); - pp2d_load_texture_png(TEXTURE_ICON_TEXT, text_path); - - pp2d_load_texture_png(TEXTURE_ICON_CHECK, check_path); - pp2d_load_texture_png(TEXTURE_ICON_UNCHECK, uncheck_path); - - pp2d_load_texture_png(TEXTURE_HOME_ICON, "romfs:/res/drawable/ic_material_light_home.png"); - pp2d_load_texture_png(TEXTURE_OPTIONS_ICON, "romfs:/res/drawable/ic_material_light_options.png"); - pp2d_load_texture_png(TEXTURE_SETTINGS_ICON, "romfs:/res/drawable/ic_material_light_settings.png"); - pp2d_load_texture_png(TEXTURE_FTP_ICON, "romfs:/res/drawable/ic_material_light_ftp.png"); - pp2d_load_texture_png(TEXTURE_UPDATE_ICON, "romfs:/res/drawable/ic_material_light_update.png"); - - pp2d_load_texture_png(TEXTURE_HOME_ICON_SELECTED, "romfs:/res/drawable/ic_overlay_home.png"); - pp2d_load_texture_png(TEXTURE_OPTIONS_ICON_SELECTED, "romfs:/res/drawable/ic_overlay_options.png"); - pp2d_load_texture_png(TEXTURE_SETTINGS_ICON_SELECTED, "romfs:/res/drawable/ic_overlay_settings.png"); - pp2d_load_texture_png(TEXTURE_FTP_ICON_SELECTED, "romfs:/res/drawable/ic_overlay_ftp.png"); - pp2d_load_texture_png(TEXTURE_UPDATE_ICON_SELECTED, "romfs:/res/drawable/ic_overlay_update.png"); - - pp2d_load_texture_png(TEXTURE_SD_ICON, "romfs:/res/drawable/ic_material_light_sd.png"); - pp2d_load_texture_png(TEXTURE_NAND_ICON, "romfs:/res/drawable/ic_material_light_nand.png"); - pp2d_load_texture_png(TEXTURE_SD_ICON_SELECTED, "romfs:/res/drawable/ic_overlay_sd.png"); - pp2d_load_texture_png(TEXTURE_NAND_ICON_SELECTED, "romfs:/res/drawable/ic_overlay_nand.png"); - - pp2d_load_texture_png(TEXTURE_TOGGLE_ON, "romfs:/res/drawable/btn_material_toggle_on.png"); - pp2d_load_texture_png(TEXTURE_TOGGLE_OFF, "romfs:/res/drawable/btn_material_toggle_off.png"); - pp2d_load_texture_png(TEXTURE_RADIO_ON, "romfs:/res/drawable/btn_material_radio_on.png"); - pp2d_load_texture_png(TEXTURE_RADIO_OFF, "romfs:/res/drawable/btn_material_radio_off.png"); - - pp2d_load_texture_png(TEXTURE_THEMES_LAUNCHER, "romfs:/res/drawable/ic_launcher_themes.png"); - - pp2d_load_texture_png(TEXTURE_SEARCH_ICON, "romfs:/res/drawable/ic_material_light_search.png"); - - pp2d_load_texture_png(TEXTURE_BATTERY_0, "romfs:/res/drawable/battery/0.png"); - pp2d_load_texture_png(TEXTURE_BATTERY_15, "romfs:/res/drawable/battery/15.png"); - pp2d_load_texture_png(TEXTURE_BATTERY_28, "romfs:/res/drawable/battery/28.png"); - pp2d_load_texture_png(TEXTURE_BATTERY_43, "romfs:/res/drawable/battery/43.png"); - pp2d_load_texture_png(TEXTURE_BATTERY_57, "romfs:/res/drawable/battery/57.png"); - pp2d_load_texture_png(TEXTURE_BATTERY_71, "romfs:/res/drawable/battery/71.png"); - pp2d_load_texture_png(TEXTURE_BATTERY_85, "romfs:/res/drawable/battery/85.png"); - pp2d_load_texture_png(TEXTURE_BATTERY_100, "romfs:/res/drawable/battery/100.png"); - pp2d_load_texture_png(TEXTURE_BATTERY_CHARGE, "romfs:/res/drawable/battery/charge.png"); - - pp2d_load_texture_png(TEXTURE_WIFI_NULL, "romfs:/res/drawable/wifi/stat_sys_wifi_signal_null.png"); - pp2d_load_texture_png(TEXTURE_WIFI_0, "romfs:/res/drawable/wifi/stat_sys_wifi_signal_0.png"); - pp2d_load_texture_png(TEXTURE_WIFI_1, "romfs:/res/drawable/wifi/stat_sys_wifi_signal_1.png"); - pp2d_load_texture_png(TEXTURE_WIFI_2, "romfs:/res/drawable/wifi/stat_sys_wifi_signal_2.png"); - pp2d_load_texture_png(TEXTURE_WIFI_3, "romfs:/res/drawable/wifi/stat_sys_wifi_signal_3.png"); - - pp2d_load_texture_png(TEXTURE_GALLERY_BAR, "romfs:/res/drawable/gallery/ic_gallery_bar.png"); - - pp2d_load_texture_png(TEXTURE_MUSIC_TOP_BG, "romfs:/res/drawable/music/ic_music_bg_top.png"); - pp2d_load_texture_png(TEXTURE_MUSIC_BOTTOM_BG, "romfs:/res/drawable/music/ic_music_bg_bottom.png"); - pp2d_load_texture_png(TEXTURE_MUSIC_PLAY, "romfs:/res/drawable/music/ic_music_play.png"); - pp2d_load_texture_png(TEXTURE_MUSIC_PAUSE, "romfs:/res/drawable/music/ic_music_pause.png"); - pp2d_load_texture_png(TEXTURE_MUSIC_STATUS, "romfs:/res/drawable/music/ic_music_status.png"); - - pp2d_load_texture_png(TEXTURE_TEXTVIEWER_BG, "romfs:/res/drawable/textviewer/textview.png"); -} \ No newline at end of file diff --git a/source/textures.c b/source/textures.c new file mode 100644 index 0000000..361bfe5 --- /dev/null +++ b/source/textures.c @@ -0,0 +1,68 @@ +#include "C2D_helper.h" +#include "common.h" +#include "sprites.h" +#include "textures.h" + +static C2D_SpriteSheet spritesheet; + +void Textures_Load(void) +{ + spritesheet = C2D_SpriteSheetLoad("romfs:/res/drawable/sprites.t3x"); + + icon_app = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_fso_type_executable_idx); + icon_archive = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_fso_type_compress_idx); + icon_audio = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_fso_type_audio_idx); + icon_dir = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_fso_folder_idx); + icon_dir_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_fso_folder_dark_idx); + icon_doc = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_fso_type_document_idx); + icon_file = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_fso_default_idx); + icon_image = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_fso_type_image_idx); + icon_text = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_fso_type_text_idx); + icon_check = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_material_light_check_on_normal_idx); + icon_check_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_material_light_check_on_normal_dark_idx); + icon_uncheck = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_material_light_check_off_normal_idx); + icon_uncheck_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_material_light_check_off_normal_dark_idx); + dialog = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_dialog_idx); + options_dialog = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_options_dialog_idx); + properties_dialog = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_properties_dialog_idx); + dialog_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_dialog_dark_idx); + options_dialog_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_options_dialog_dark_idx); + properties_dialog_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_properties_dialog_dark_idx); + icon_radio_off = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_material_light_radio_off_normal_idx); + icon_radio_on = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_material_light_radio_on_normal_idx); + icon_radio_dark_off = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_material_light_radio_off_normal_dark_idx); + icon_radio_dark_on = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_material_light_radio_on_normal_dark_idx); + icon_toggle_on = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_material_light_toggle_on_normal_idx); + icon_toggle_dark_on = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_material_light_toggle_on_normal_dark_idx); + icon_toggle_off = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_material_light_toggle_off_normal_idx); + + icon_home = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_home_idx); + icon_home_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_home_dark_idx); + icon_options = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_filesystem_idx); + icon_options_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_filesystem_dark_idx); + icon_settings = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_settings_idx); + icon_settings_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_settings_dark_idx); + icon_ftp = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_remote_idx); + icon_ftp_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_remote_dark_idx); + icon_sd = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_sdcard_idx); + icon_sd_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_sdcard_dark_idx); + icon_secure = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_secure_idx); + icon_secure_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_secure_dark_idx); + icon_search = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_search_idx); + btn_play = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_playback_play_idx); + btn_pause = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_playback_pause_idx); + btn_rewind = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_playback_rewind_idx); + btn_forward = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_playback_forward_idx); + btn_repeat = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_playback_repeat_idx); + btn_shuffle = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_playback_shuffle_idx); + btn_repeat_overlay = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_playback_repeat_overlay_idx); + btn_shuffle_overlay = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_playback_shuffle_overlay_idx); + icon_nav_drawer = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_navigation_drawer_idx); + icon_actions = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_contextual_action_idx); + icon_back = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_arrow_back_normal_idx); +} + +void Textures_Free(void) +{ + C2D_SpriteSheetFree(spritesheet); +} \ No newline at end of file diff --git a/source/theme.c b/source/theme.c deleted file mode 100644 index 076f933..0000000 --- a/source/theme.c +++ /dev/null @@ -1,257 +0,0 @@ -#include -#include -#include - -#include "fs.h" -#include "common.h" -#include "pp2d.h" -#include "textures.h" -#include "theme.h" -#include "utils.h" - -struct colour BottomScreen_colour; -struct colour BottomScreen_bar_colour; -struct colour BottomScreen_text_colour; -struct colour Options_select_colour; -struct colour Options_text_colour; -struct colour Options_title_text_colour; -struct colour Settings_colour; -struct colour Settings_text_colour; -struct colour Settings_text_min_colour; -struct colour Settings_title_text_colour; -struct colour Storage_colour; -struct colour TopScreen_colour; -struct colour TopScreen_bar_colour; -struct colour TopScreen_min_colour; - -const char * themeConfig = - "theme = %s\n" - "colours = %s"; - -const char * coloursConfig = - "storage = RGBA(%d, %d, %d, 255)\n" - "topscreen_bar = RGBA(%d, %d, %d, 255)\n" - "topscreen_text = RGBA(%d, %d, %d, 255)\n" - "topscreen_text_min = RGBA(%d, %d, %d, 255)\n" - "bottomscreen_bg = RGBA(%d, %d, %d, 255)\n" - "bottomscreen_bar = RGBA(%d, %d, %d, 255)\n" - "bottomscreen_text = RGBA(%d, %d, %d, 255)\n" - "options_selector = RGBA(%d, %d, %d, 255)\n" - "options_title_text = RGBA(%d, %d, %d, 255)\n" - "options_text = RGBA(%d, %d, %d, 255)\n" - "settings_bg = RGBA(%d, %d, %d, 255)\n" - "settings_title_text = RGBA(%d, %d, %d, 255)\n" - "settins_text = RGBA(%d, %d, %d, 255)\n" - "settings_text_min = RGBA(%d, %d, %d, 255)"; - -Result Theme_SaveConfig(char * themePath, char * coloursPath) -{ - Result ret = 0; - - char * buf = (char *)malloc(512); - snprintf(buf, 512, themeConfig, themePath, coloursPath); - - if (R_FAILED(ret = FS_Write(archive, "/3ds/3DShell/theme.cfg", buf))) - { - free(buf); - return ret; - } - - free(buf); - return 0; -} - -static Result Theme_LoadConfig(void) -{ - Handle handle; - Result ret = 0; - - if (!FS_FileExists(archive, "/3ds/3DShell/theme.cfg")) - Theme_SaveConfig("romfs:/res/drawable", "/3ds/3DShell/themes/default"); - - u64 size64 = 0; - u32 size = 0; - - size64 = FS_GetFileSize(archive, "/3ds/3DShell/theme.cfg"); - size = (u32)size64; - char * buf = (char *)malloc(size + 1); - - if (R_FAILED(ret = FS_Read(archive, "/3ds/3DShell/theme.cfg", size, buf))) - { - free(buf); - return ret; - } - - buf[size] = '\0'; - - sscanf(buf, themeConfig, theme_dir, colour_dir); - - free(buf); - return 0; -} - -static Result Theme_CreateFontColours(void) -{ - Result ret = 0; - - char * buf = (char *)malloc(1024); - snprintf(buf, 1024, coloursConfig, 48, 174, 222, - 255, 255, 255, - 0, 0, 0, - 95, 95, 95, - 30, 136, 229, - 25, 118, 210, - 251, 251, 251, - 237, 237, 237, - 32, 32, 32, - 120, 120, 120, - 251, 251, 251, - 30, 136, 229, - 32, 32, 32, - 120, 120, 120); - - if (R_FAILED(ret = FS_Write(archive, "/3ds/3DShell/themes/default/colours.cfg", buf))) - { - free(buf); - return ret; - } - - free(buf); - return 0; -} - -static Result Theme_LoadFontColours(void) -{ - Handle handle; - Result ret = 0; - - if (!FS_FileExists(archive, "/3ds/3DShell/themes/default/colours.cfg")) - Theme_CreateFontColours(); - - char colours_cfg[100] = "/colours.cfg"; - strcat(colour_dir, colours_cfg); - - u64 size64 = 0; - u32 size = 0; - - size64 = FS_GetFileSize(archive, colour_dir); - size = (u32)size64; - char * buf = (char *)malloc(size + 1); - - if (R_FAILED(ret = FS_Read(archive, colour_dir, size, buf))) - { - free(buf); - return ret; - } - - buf[size] = '\0'; - - sscanf(buf, coloursConfig, &Storage_colour.r, &Storage_colour.g, &Storage_colour.b, - &TopScreen_bar_colour.r, &TopScreen_bar_colour.g, &TopScreen_bar_colour.b, - &TopScreen_colour.r, &TopScreen_colour.g, &TopScreen_colour.b, - &TopScreen_min_colour.r, &TopScreen_min_colour.g, &TopScreen_min_colour.b, - &BottomScreen_colour.r, &BottomScreen_colour.g, &BottomScreen_colour.b, - &BottomScreen_bar_colour.r, &BottomScreen_bar_colour.g, &BottomScreen_bar_colour.b, - &BottomScreen_text_colour.r, &BottomScreen_text_colour.g, &BottomScreen_text_colour.b, - &Options_select_colour.r, &Options_select_colour.g, &Options_select_colour.b, - &Options_title_text_colour.r, &Options_title_text_colour.g, &Options_title_text_colour.b, - &Options_text_colour.r, &Options_text_colour.g, &Options_text_colour.b, - &Settings_colour.r, &Settings_colour.g, &Settings_colour.b, - &Settings_title_text_colour.r, &Settings_title_text_colour.g, &Settings_title_text_colour.b, - &Settings_text_colour.r, &Settings_text_colour.g, &Settings_text_colour.b, - &Settings_text_min_colour.r, &Settings_text_min_colour.g, &Settings_text_min_colour.b); - - free(buf); - return 0; -} - -static void Theme_ReplaceAsset(char arr[], char path[], char img_path[], char redirect_path[]) -{ - strcpy(arr, path); - strcat(arr, img_path); - strcpy(redirect_path, arr); -} - -void Theme_Load(void) -{ - Theme_LoadConfig(); - - char background_res[100] = "/ic_background.png"; - char dialog_res[100] = "/ic_dialog.png"; - char options_res[100] = "/ic_options.png"; - char properties_res[100] = "/ic_properties.png"; - char selector_res[100] = "/ic_selector.png"; - - char app_res[100] = "/ic_fso_type_app.png"; - char archive_res[100] = "/ic_fso_type_archive.png"; - char audio_res[100] = "/ic_fso_type_audio.png"; - char file_res[100] = "/ic_fso_type_file.png"; - char folder_res[100] = "/ic_fso_type_folder.png"; - char img_res[100] = "/ic_fso_type_img.png"; - char system_res[100] = "/ic_fso_type_system.png"; - char text_res[100] = "/ic_fso_type_text.png"; - - char check_res[100] = "/btn_material_check_on.png"; - char uncheck_res[100] = "/btn_material_check_off.png"; - - Theme_ReplaceAsset(temp_arr, theme_dir, background_res, background_path); - Theme_ReplaceAsset(temp_arr, theme_dir, dialog_res, dialog_path); - Theme_ReplaceAsset(temp_arr, theme_dir, options_res, options_path); - Theme_ReplaceAsset(temp_arr, theme_dir, properties_res, properties_path); - Theme_ReplaceAsset(temp_arr, theme_dir, selector_res, selector_path); - - Theme_ReplaceAsset(temp_arr, theme_dir, app_res, app_path); - Theme_ReplaceAsset(temp_arr, theme_dir, archive_res, archive_path); - Theme_ReplaceAsset(temp_arr, theme_dir, audio_res, audio_path); - Theme_ReplaceAsset(temp_arr, theme_dir, file_res, file_path); - Theme_ReplaceAsset(temp_arr, theme_dir, folder_res, folder_path); - Theme_ReplaceAsset(temp_arr, theme_dir, img_res, img_path); - Theme_ReplaceAsset(temp_arr, theme_dir, system_res, system_path); - Theme_ReplaceAsset(temp_arr, theme_dir, text_res, text_path); - - Theme_ReplaceAsset(temp_arr, theme_dir, check_res, check_path); - Theme_ReplaceAsset(temp_arr, theme_dir, uncheck_res, uncheck_path); - - Theme_LoadFontColours(); -} - -void Theme_Reload(void) -{ - pp2d_free_texture(TEXTURE_BACKGROUND); - pp2d_free_texture(TEXTURE_DIALOG); - pp2d_free_texture(TEXTURE_OPTIONS); - pp2d_free_texture(TEXTURE_PROPERTIES); - pp2d_free_texture(TEXTURE_SELECTOR); - - pp2d_free_texture(TEXTURE_ICON_APP); - pp2d_free_texture(TEXTURE_ICON_ARCHIVE); - pp2d_free_texture(TEXTURE_ICON_AUDIO); - pp2d_free_texture(TEXTURE_ICON_FILE); - pp2d_free_texture(TEXTURE_ICON_FOLDER); - pp2d_free_texture(TEXTURE_ICON_IMG); - pp2d_free_texture(TEXTURE_ICON_SYSTEM); - pp2d_free_texture(TEXTURE_ICON_TEXT); - - pp2d_free_texture(TEXTURE_ICON_CHECK); - pp2d_free_texture(TEXTURE_ICON_UNCHECK); - - pp2d_load_texture_png(TEXTURE_BACKGROUND, background_path); - pp2d_load_texture_png(TEXTURE_DIALOG, dialog_path); - pp2d_load_texture_png(TEXTURE_OPTIONS, options_path); - pp2d_load_texture_png(TEXTURE_PROPERTIES, properties_path); - pp2d_load_texture_png(TEXTURE_SELECTOR, selector_path); - - pp2d_load_texture_png(TEXTURE_ICON_APP, app_path); - pp2d_load_texture_png(TEXTURE_ICON_ARCHIVE, archive_path); - pp2d_load_texture_png(TEXTURE_ICON_AUDIO, audio_path); - pp2d_load_texture_png(TEXTURE_ICON_FILE, file_path); - pp2d_load_texture_png(TEXTURE_ICON_FOLDER, folder_path); - pp2d_load_texture_png(TEXTURE_ICON_IMG, img_path); - pp2d_load_texture_png(TEXTURE_ICON_SYSTEM, system_path); - pp2d_load_texture_png(TEXTURE_ICON_TEXT, text_path); - - pp2d_load_texture_png(TEXTURE_ICON_CHECK, check_path); - pp2d_load_texture_png(TEXTURE_ICON_UNCHECK, uncheck_path); - - Theme_LoadFontColours(); -} \ No newline at end of file diff --git a/source/misc/touch.c b/source/touch.c similarity index 79% rename from source/misc/touch.c rename to source/touch.c index 8101f4a..d24801f 100644 --- a/source/misc/touch.c +++ b/source/touch.c @@ -2,7 +2,7 @@ #include "touch.h" -u16 touchGetX(void) +u16 Touch_GetX(void) { touchPosition pos; hidTouchRead(&pos); @@ -10,7 +10,7 @@ u16 touchGetX(void) return pos.px; } -u16 touchGetY(void) +u16 Touch_GetY(void) { touchPosition pos; hidTouchRead(&pos); diff --git a/source/utils.c b/source/utils.c index 7b10237..5d9a67e 100644 --- a/source/utils.c +++ b/source/utils.c @@ -1,134 +1,42 @@ #include -#include #include -#include "common.h" -#include "fs.h" -#include "utils.h" - -const char * configFile = - "sortBy = %d\n" - "recycleBin = %d\n" - "systemProtection = %d\n" - "showHiddenFiles = %d"; +#include <3ds.h> -void Utils_MakeDirectories(void) -{ - if (BROWSE_STATE != STATE_NAND) - { - if (!(FS_DirExists(archive, "/3ds/3DShell/themes/default/"))) - FS_RecursiveMakeDir(archive, "/3ds/3DShell/themes/default"); - if (!(FS_DirExists(archive, "/3ds/3DShell/bin/"))) - FS_RecursiveMakeDir(archive, "/3ds/3DShell/bin"); - } -} +#include "utils.h" -Result Utils_SaveConfig(int sortBy, bool recycleBin, bool galleryDisplay, bool hidden) -{ - Result ret = 0; - - char * buf = (char *)malloc(256); - snprintf(buf, 256, configFile, sortBy, recycleBin, galleryDisplay, hidden); - - if (R_FAILED(ret = FS_Write(archive, "/3ds/3DShell/config.cfg", buf))) - { - free(buf); - return ret; - } - - free(buf); - return 0; -} - -Result Utils_LoadConfig(void) +bool Utils_IsN3DS(void) { - Handle handle; - Result ret = 0; - - if (!FS_FileExists(archive, "/3ds/3DShell/config.cfg")) - { - // set these to the following by default: - sortBy = 1; - recycleBin = 0; - galleryDisplay = 1; - isHiddenEnabled = 0; - return Utils_SaveConfig(sortBy, recycleBin, galleryDisplay, isHiddenEnabled); - } - - u64 size64 = 0; - u32 size = 0; - - size64 = FS_GetFileSize(archive, "/3ds/3DShell/config.cfg"); - size = (u32)size64; - char * buf = (char *)malloc(size + 1); + bool isNew3DS = false; - if (R_FAILED(ret = FS_Read(archive, "/3ds/3DShell/config.cfg", size, buf))) - { - free(buf); - return ret; - } + if (R_SUCCEEDED(APT_CheckNew3DS(&isNew3DS))) + return isNew3DS; - buf[size] = '\0'; - - sscanf(buf, configFile, &sortBy, &recycleBin, &galleryDisplay, &isHiddenEnabled); - - free(buf); - return 0; + return false; } -Result Utils_GetLastDirectory(void) +void Utils_U16_To_U8(char *buf, const u16 *input, size_t bufsize) { - Handle handle; - Result ret = 0; - - if (!FS_FileExists(archive, "/3ds/3DShell/lastdir.txt")) - { - FS_Write(archive, "/3ds/3DShell/lastdir.txt", START_PATH); - strcpy(cwd, START_PATH); // Set Start Path to "sdmc:/" if lastDir.txt hasn't been created. - } - else - { - u64 size64 = 0; - u32 size = 0; - - size64 = FS_GetFileSize(archive, "/3ds/3DShell/lastdir.txt"); - size = (u32)size64; - char * buf = (char *)malloc(size + 1); - - if (R_FAILED(ret = FS_Read(archive, "/3ds/3DShell/lastdir.txt", size, buf))) - { - free(buf); - return ret; - } - - buf[size] = '\0'; + ssize_t units = utf16_to_utf8((u8 *)buf, input, bufsize); - char tempPath[256]; - sscanf(buf, "%[^\n]s", tempPath); - - if (FS_DirExists(archive, tempPath)) // Incase a directory previously visited had been deleted, set start path to sdmc:/ to avoid errors. - strcpy(cwd, tempPath); - else - strcpy(cwd, START_PATH); - - free(buf); - } + if (units < 0) + units = 0; - return 0; + buf[units] = 0; } -char * Utils_Basename(const char * filename) +char *Utils_Basename(const char *filename) { char *p = strrchr (filename, '/'); return p ? p + 1 : (char *) filename; } -void Utils_GetSizeString(char * string, uint64_t size) //Thanks TheOfficialFloW +void Utils_GetSizeString(char *string, u64 size) { double double_size = (double)size; int i = 0; - static char *units[] = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; + static char *units[] = {"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}; while (double_size >= 1024.0f) { @@ -139,64 +47,45 @@ void Utils_GetSizeString(char * string, uint64_t size) //Thanks TheOfficialFloW sprintf(string, "%.*f %s", (i == 0) ? 0 : 2, double_size, units[i]); } -CFG_Region Utils_GetRegion(void) +void Utils_SetMax(int *set, int value, int max) { - CFG_Region region = 0; - - if (R_SUCCEEDED(CFGU_SecureInfoGetRegion(®ion))) - return region; - - return 0; + if (*set > max) + *set = value; } -CFG_Language Utils_GetLanguage(void) +void Utils_SetMin(int *set, int value, int min) { - CFG_Language language = 0; - - if (R_SUCCEEDED(CFGU_GetSystemLanguage(&language))) - return language; - - return 0; + if (*set < min) + *set = value; } -const char * Utils_GetUsername(void) +int Utils_Alphasort(const void *p1, const void *p2) { - u8 data[0x1C]; - static wchar_t whcar_username[0x13]; - char * username = (char *)malloc(0x1C); - - if (R_SUCCEEDED(CFGU_GetConfigInfoBlk2(0x1C, 0x000A0000, data))) - { - for (int i = 0; i < 0x13; i++) - whcar_username[i] = (wchar_t)((u16 *)data)[i]; - } - else - { - free(username); - return NULL; - } - - wcstombs(username, whcar_username, 0x1C); - - return username; -} + FS_DirectoryEntry* entryA = (FS_DirectoryEntry*) p1; + FS_DirectoryEntry* entryB = (FS_DirectoryEntry*) p2; -bool Utils_IsN3DS(void) -{ - bool isNew3DS = false; - - if (R_SUCCEEDED(APT_CheckNew3DS(&isNew3DS))) - return isNew3DS; - - return false; + if ((entryA->attributes & FS_ATTRIBUTE_DIRECTORY) && !(entryB->attributes & FS_ATTRIBUTE_DIRECTORY)) + return -1; + else if (!(entryA->attributes & FS_ATTRIBUTE_DIRECTORY) && (entryB->attributes & FS_ATTRIBUTE_DIRECTORY)) + return 1; + + char entryNameA[256] = {'\0'}, entryNameB[256] = {'\0'}; + Utils_U16_To_U8((u8 *) entryNameA, entryA->name, sizeof(entryNameA) - 1); + Utils_U16_To_U8((u8 *) entryNameB, entryB->name, sizeof(entryNameB) - 1); + return strcasecmp(entryNameA, entryNameB); } -void u16_to_u8(char * buf, const u16 * input, size_t bufsize) +void Utils_AppendArr(char subject[], const char insert[], int pos) { - ssize_t units = utf16_to_utf8((u8 *)buf, input, bufsize); - - if (units < 0) - units = 0; - - buf[units] = 0; + char buf[100] = {}; // 100 so that it's big enough. fill with 0 + // or you could use malloc() to allocate sufficient space + + strncpy(buf, subject, pos); // copy at most first pos characters + int len = strlen(buf); + strcpy(buf+len, insert); // copy all of insert[] at the end + len += strlen(insert); // increase the length by length of insert[] + strcpy(buf+len, subject+pos); // copy the rest + + strcpy(subject, buf); // copy it back to subject + // deallocate buf[] here, if used malloc() } \ No newline at end of file From 1a98ed56a85140542f73e4cea7ed81cfc3fc8aaa Mon Sep 17 00:00:00 2001 From: Joel16 Date: Sun, 8 Jul 2018 14:06:45 -0500 Subject: [PATCH 02/33] Switch to C2D (2) Also trying docker now instead. --- .travis.yml | 27 +- .travis/build.sh | 7 + include/C2D_helper.h | 7 +- include/common.h | 26 +- include/fs.h | 30 +- include/menus/lodepng.h | 1770 +++++ include/menus/menu_fileoptions.h | 13 + include/menus/menu_main.h | 2 + include/menus/menu_settings.h | 11 + include/touch.h | 2 + .../btn_material_light_radio_off_normal.png | Bin 824 -> 535 bytes ...n_material_light_radio_off_normal_dark.png | Bin 683 -> 447 bytes .../btn_material_light_radio_on_normal.png | Bin 1007 -> 625 bytes ...tn_material_light_radio_on_normal_dark.png | Bin 809 -> 519 bytes .../btn_material_light_toggle_off_normal.png | Bin 1568 -> 15590 bytes .../btn_material_light_toggle_off_normalx.png | Bin 0 -> 15698 bytes .../btn_material_light_toggle_on_normal.png | Bin 1694 -> 996 bytes ...n_material_light_toggle_on_normal_dark.png | Bin 1686 -> 15617 bytes ..._material_light_toggle_on_normal_darkx.png | Bin 0 -> 15698 bytes res/drawable/ic_material_dialog.png | Bin 20552 -> 553 bytes res/drawable/ic_material_dialog_dark.png | Bin 5594 -> 492 bytes res/drawable/ic_material_light_home.png | Bin 268 -> 295 bytes res/drawable/ic_material_light_home_dark.png | Bin 263 -> 289 bytes res/drawable/ic_material_light_search.png | Bin 351 -> 15314 bytes res/drawable/ic_material_options_dialog.png | Bin 959 -> 963 bytes .../ic_material_options_dialog_dark.png | Bin 1036 -> 1067 bytes .../ic_material_properties_dialog.png | Bin 769 -> 748 bytes .../ic_material_properties_dialog_dark.png | Bin 884 -> 899 bytes source/C2D_helper.c | 20 +- source/dirbrowse.c | 5 +- source/main.c | 3 +- source/menus/lodepng.c | 6245 +++++++++++++++++ source/menus/menu_fileoptions.c | 642 ++ source/menus/menu_ftp.c | 9 +- source/menus/menu_gallery.c | 2 +- source/menus/menu_main.c | 183 +- source/menus/menu_music.c | 4 +- source/menus/menu_settings.c | 135 + source/progress_bar.c | 19 +- source/screenshot.c | 2 +- source/touch.c | 16 +- 41 files changed, 9043 insertions(+), 137 deletions(-) create mode 100644 .travis/build.sh create mode 100644 include/menus/lodepng.h create mode 100644 include/menus/menu_fileoptions.h create mode 100644 include/menus/menu_settings.h create mode 100644 res/drawable/btn_material_light_toggle_off_normalx.png create mode 100644 res/drawable/btn_material_light_toggle_on_normal_darkx.png create mode 100644 source/menus/lodepng.c create mode 100644 source/menus/menu_fileoptions.c create mode 100644 source/menus/menu_settings.c diff --git a/.travis.yml b/.travis.yml index a91ab47..d18f846 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,26 +1,21 @@ language: c -sudo: true +sudo: required -#Cache devkitARM and doxygen -cache: - directories: - - /home/travis/devkitPro +services: docker before_install: - - cd include/dr_libs/ - - git clone https://github.com/mackron/dr_libs.git - - cd ../.. - - export DEVKITPRO=/opt/devkitpro - - export DEVKITARM=/opt/devkitpro/devkitARM - - export CTRULIB=${DEVKITPRO}/libctru - - export PORTLIBS=${DEVKITPRO}/portlibs/armv6k - - sh resources/.travis-deps.sh +- cd include/dr_libs/ +- git clone https://github.com/mackron/dr_libs.git +- cd ../.. + +install: +- docker pull devkitpro/devkitarm script: - - cd $TRAVIS_BUILD_DIR - - sh resources/.build.sh - +- docker run -e ENABLE_COMPATIBILITY_REPORTING -v $TRAVIS_BUILD_DIR:/3DShell + devkitpro/devkitarm /bin/bash -ex /3DShell/.travis/build.sh + deploy: provider: pages skip_cleanup: true diff --git a/.travis/build.sh b/.travis/build.sh new file mode 100644 index 0000000..1762df7 --- /dev/null +++ b/.travis/build.sh @@ -0,0 +1,7 @@ +#!/bin/bash -ex + +source /etc/profile.d/devkit-env.sh + +cd /3DShell + +make \ No newline at end of file diff --git a/include/C2D_helper.h b/include/C2D_helper.h index 3e48376..f45614c 100644 --- a/include/C2D_helper.h +++ b/include/C2D_helper.h @@ -22,14 +22,15 @@ C3D_RenderTarget *RENDER_TOP, *RENDER_BOTTOM; C2D_TextBuf staticBuf, dynamicBuf, sizeBuf; -typedef u32 Color; +typedef u32 Colour; void Draw_EndFrame(void); -void Draw_Text(float x, float y, float size, Color color, const char *text); +void Draw_Text(float x, float y, float size, Colour colour, const char *text); +void Draw_Textf(float x, float y, float size, Colour colour, const char* text, ...); void Draw_GetTextSize(float size, float *width, float *height, const char *text); float Draw_GetTextWidth(float size, const char *text); float Draw_GetTextHeight(float size, const char *text); -bool Draw_Rect(float x, float y, float w, float h, Color color); +bool Draw_Rect(float x, float y, float w, float h, Colour colour); bool Draw_Image(C2D_Image image, float x, float y); bool Draw_ImageScale(C2D_Image image, float x, float y, float scaleX, float scaleY); diff --git a/include/common.h b/include/common.h index 1087d1b..2b18eee 100644 --- a/include/common.h +++ b/include/common.h @@ -10,22 +10,22 @@ #define MAX_FILES 1024 #define FILES_PER_PAGE 5 -#define MENU_STATE_HOME 0 -#define MENU_STATE_OPTIONS 1 -#define MENU_STATE_MENUBAR 2 -#define MENU_STATE_SETTINGS 3 -#define MENU_STATE_FTP 4 -#define MENU_STATE_SORT 5 -#define MENU_STATE_THEMES 6 -#define MENU_STATE_DIALOG 7 -#define MENU_STATE_PROPERTIES 8 - -#define STATE_SD 0 -#define STATE_NAND 1 +#define MENU_STATE_HOME 0 +#define MENU_STATE_FILEOPTIONS 1 +#define MENU_STATE_MENUBAR 2 +#define MENU_STATE_SETTINGS 3 +#define MENU_STATE_FTP 4 +#define MENU_STATE_SORT 5 +#define MENU_STATE_THEMES 6 +#define MENU_STATE_DIALOG 7 +#define MENU_STATE_PROPERTIES 8 + +#define BROWSE_STATE_SD 0 +#define BROWSE_STATE_NAND 1 jmp_buf exitJmp; -int MENU_DEFAULT_STATE; +int MENU_STATE; int BROWSE_STATE; char cwd[512]; diff --git a/include/fs.h b/include/fs.h index 94898b4..b3900b9 100644 --- a/include/fs.h +++ b/include/fs.h @@ -5,24 +5,24 @@ FS_Archive archive; -Result FS_OpenArchive(FS_Archive * archive, FS_ArchiveID id); +Result FS_OpenArchive(FS_Archive *archive, FS_ArchiveID id); Result FS_CloseArchive(FS_Archive archive); -Result FS_MakeDir(FS_Archive archive, const char * path); -Result FS_RecursiveMakeDir(FS_Archive archive, const char * dir); -bool FS_FileExists(FS_Archive archive, const char * path); -bool FS_DirExists(FS_Archive archive, const char * path); -char* FS_GetFileModifiedTime(char * path); -u64 FS_GetFileSize(FS_Archive archive, const char * path); +Result FS_MakeDir(FS_Archive archive, const char *path); +Result FS_RecursiveMakeDir(FS_Archive archive, const char *dir); +bool FS_FileExists(FS_Archive archive, const char *path); +bool FS_DirExists(FS_Archive archive, const char *path); +char* FS_GetFileModifiedTime(char *path); +u64 FS_GetFileSize(FS_Archive archive, const char *path); u64 FS_GetFreeStorage(FS_SystemMediaType mediaType); u64 FS_GetTotalStorage(FS_SystemMediaType mediaType); u64 FS_GetUsedStorage(FS_SystemMediaType mediaType); -Result FS_Remove(FS_Archive archive, const char * filename); -Result FS_Rmdir(FS_Archive archive, const char * path); -Result FS_RmdirRecursive(FS_Archive archive, const char * path); -Result FS_RenameFile(FS_Archive archive, const char * old_filename, const char * new_filename); -Result FS_RenameDir(FS_Archive archive, const char * old_filename, const char * new_filename); -Result FS_Open(Handle * handle, FS_Archive archive, const char * path, u32 flags); -Result FS_Read(FS_Archive archive, const char * path, u64 size, char * buf); -Result FS_Write(FS_Archive archive, const char * path, const char * buf); +Result FS_Remove(FS_Archive archive, const char *filename); +Result FS_Rmdir(FS_Archive archive, const char *path); +Result FS_RmdirRecursive(FS_Archive archive, const char *path); +Result FS_RenameFile(FS_Archive archive, const char *old_filename, const char *new_filename); +Result FS_RenameDir(FS_Archive archive, const char *old_filename, const char *new_filename); +Result FS_Open(Handle *handle, FS_Archive archive, const char *path, u32 flags); +Result FS_Read(FS_Archive archive, const char *path, u64 size, char *buf); +Result FS_Write(FS_Archive archive, const char *path, const char *buf); #endif \ No newline at end of file diff --git a/include/menus/lodepng.h b/include/menus/lodepng.h new file mode 100644 index 0000000..6c56c1d --- /dev/null +++ b/include/menus/lodepng.h @@ -0,0 +1,1770 @@ +/* +LodePNG version 20180611 + +Copyright (c) 2005-2018 Lode Vandevenne + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +#ifndef LODEPNG_H +#define LODEPNG_H + +#include /*for size_t*/ + +extern const char* LODEPNG_VERSION_STRING; + +/* +The following #defines are used to create code sections. They can be disabled +to disable code sections, which can give faster compile time and smaller binary. +The "NO_COMPILE" defines are designed to be used to pass as defines to the +compiler command to disable them without modifying this header, e.g. +-DLODEPNG_NO_COMPILE_ZLIB for gcc. +In addition to those below, you can also define LODEPNG_NO_COMPILE_CRC to +allow implementing a custom lodepng_crc32. +*/ +/*deflate & zlib. If disabled, you must specify alternative zlib functions in +the custom_zlib field of the compress and decompress settings*/ +#ifndef LODEPNG_NO_COMPILE_ZLIB +#define LODEPNG_COMPILE_ZLIB +#endif +/*png encoder and png decoder*/ +#ifndef LODEPNG_NO_COMPILE_PNG +#define LODEPNG_COMPILE_PNG +#endif +/*deflate&zlib decoder and png decoder*/ +#ifndef LODEPNG_NO_COMPILE_DECODER +#define LODEPNG_COMPILE_DECODER +#endif +/*deflate&zlib encoder and png encoder*/ +#ifndef LODEPNG_NO_COMPILE_ENCODER +#define LODEPNG_COMPILE_ENCODER +#endif +/*the optional built in harddisk file loading and saving functions*/ +#ifndef LODEPNG_NO_COMPILE_DISK +#define LODEPNG_COMPILE_DISK +#endif +/*support for chunks other than IHDR, IDAT, PLTE, tRNS, IEND: ancillary and unknown chunks*/ +#ifndef LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS +#define LODEPNG_COMPILE_ANCILLARY_CHUNKS +#endif +/*ability to convert error numerical codes to English text string*/ +#ifndef LODEPNG_NO_COMPILE_ERROR_TEXT +#define LODEPNG_COMPILE_ERROR_TEXT +#endif +/*Compile the default allocators (C's free, malloc and realloc). If you disable this, +you can define the functions lodepng_free, lodepng_malloc and lodepng_realloc in your +source files with custom allocators.*/ +#ifndef LODEPNG_NO_COMPILE_ALLOCATORS +#define LODEPNG_COMPILE_ALLOCATORS +#endif +/*compile the C++ version (you can disable the C++ wrapper here even when compiling for C++)*/ +#ifdef __cplusplus +#ifndef LODEPNG_NO_COMPILE_CPP +#define LODEPNG_COMPILE_CPP +#endif +#endif + +#ifdef LODEPNG_COMPILE_CPP +#include +#include +#endif /*LODEPNG_COMPILE_CPP*/ + +#ifdef LODEPNG_COMPILE_PNG +/*The PNG color types (also used for raw).*/ +typedef enum LodePNGColorType +{ + LCT_GREY = 0, /*greyscale: 1,2,4,8,16 bit*/ + LCT_RGB = 2, /*RGB: 8,16 bit*/ + LCT_PALETTE = 3, /*palette: 1,2,4,8 bit*/ + LCT_GREY_ALPHA = 4, /*greyscale with alpha: 8,16 bit*/ + LCT_RGBA = 6 /*RGB with alpha: 8,16 bit*/ +} LodePNGColorType; + +#ifdef LODEPNG_COMPILE_DECODER +/* +Converts PNG data in memory to raw pixel data. +out: Output parameter. Pointer to buffer that will contain the raw pixel data. + After decoding, its size is w * h * (bytes per pixel) bytes larger than + initially. Bytes per pixel depends on colortype and bitdepth. + Must be freed after usage with free(*out). + Note: for 16-bit per channel colors, uses big endian format like PNG does. +w: Output parameter. Pointer to width of pixel data. +h: Output parameter. Pointer to height of pixel data. +in: Memory buffer with the PNG file. +insize: size of the in buffer. +colortype: the desired color type for the raw output image. See explanation on PNG color types. +bitdepth: the desired bit depth for the raw output image. See explanation on PNG color types. +Return value: LodePNG error code (0 means no error). +*/ +unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, + const unsigned char* in, size_t insize, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_decode_memory, but always decodes to 32-bit RGBA raw image*/ +unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, + const unsigned char* in, size_t insize); + +/*Same as lodepng_decode_memory, but always decodes to 24-bit RGB raw image*/ +unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, + const unsigned char* in, size_t insize); + +#ifdef LODEPNG_COMPILE_DISK +/* +Load PNG from disk, from file with given name. +Same as the other decode functions, but instead takes a filename as input. +*/ +unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, + const char* filename, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.*/ +unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, + const char* filename); + +/*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.*/ +unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, + const char* filename); +#endif /*LODEPNG_COMPILE_DISK*/ +#endif /*LODEPNG_COMPILE_DECODER*/ + + +#ifdef LODEPNG_COMPILE_ENCODER +/* +Converts raw pixel data into a PNG image in memory. The colortype and bitdepth + of the output PNG image cannot be chosen, they are automatically determined + by the colortype, bitdepth and content of the input pixel data. + Note: for 16-bit per channel colors, needs big endian format like PNG does. +out: Output parameter. Pointer to buffer that will contain the PNG image data. + Must be freed after usage with free(*out). +outsize: Output parameter. Pointer to the size in bytes of the out buffer. +image: The raw pixel data to encode. The size of this buffer should be + w * h * (bytes per pixel), bytes per pixel depends on colortype and bitdepth. +w: width of the raw pixel data in pixels. +h: height of the raw pixel data in pixels. +colortype: the color type of the raw input image. See explanation on PNG color types. +bitdepth: the bit depth of the raw input image. See explanation on PNG color types. +Return value: LodePNG error code (0 means no error). +*/ +unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_encode_memory, but always encodes from 32-bit RGBA raw image.*/ +unsigned lodepng_encode32(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h); + +/*Same as lodepng_encode_memory, but always encodes from 24-bit RGB raw image.*/ +unsigned lodepng_encode24(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h); + +#ifdef LODEPNG_COMPILE_DISK +/* +Converts raw pixel data into a PNG file on disk. +Same as the other encode functions, but instead takes a filename as output. +NOTE: This overwrites existing files without warning! +*/ +unsigned lodepng_encode_file(const char* filename, + const unsigned char* image, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.*/ +unsigned lodepng_encode32_file(const char* filename, + const unsigned char* image, unsigned w, unsigned h); + +/*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.*/ +unsigned lodepng_encode24_file(const char* filename, + const unsigned char* image, unsigned w, unsigned h); +#endif /*LODEPNG_COMPILE_DISK*/ +#endif /*LODEPNG_COMPILE_ENCODER*/ + + +#ifdef LODEPNG_COMPILE_CPP +namespace lodepng +{ +#ifdef LODEPNG_COMPILE_DECODER +/*Same as lodepng_decode_memory, but decodes to an std::vector. The colortype +is the format to output the pixels to. Default is RGBA 8-bit per channel.*/ +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + const unsigned char* in, size_t insize, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + const std::vector& in, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#ifdef LODEPNG_COMPILE_DISK +/* +Converts PNG file from disk to raw pixel data in memory. +Same as the other decode functions, but instead takes a filename as input. +*/ +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + const std::string& filename, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_DECODER */ + +#ifdef LODEPNG_COMPILE_ENCODER +/*Same as lodepng_encode_memory, but encodes to an std::vector. colortype +is that of the raw input data. The output PNG color type will be auto chosen.*/ +unsigned encode(std::vector& out, + const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +unsigned encode(std::vector& out, + const std::vector& in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#ifdef LODEPNG_COMPILE_DISK +/* +Converts 32-bit RGBA raw pixel data into a PNG file on disk. +Same as the other encode functions, but instead takes a filename as output. +NOTE: This overwrites existing files without warning! +*/ +unsigned encode(const std::string& filename, + const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +unsigned encode(const std::string& filename, + const std::vector& in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_ENCODER */ +} /* namespace lodepng */ +#endif /*LODEPNG_COMPILE_CPP*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ERROR_TEXT +/*Returns an English description of the numerical error code.*/ +const char* lodepng_error_text(unsigned code); +#endif /*LODEPNG_COMPILE_ERROR_TEXT*/ + +#ifdef LODEPNG_COMPILE_DECODER +/*Settings for zlib decompression*/ +typedef struct LodePNGDecompressSettings LodePNGDecompressSettings; +struct LodePNGDecompressSettings +{ + /* Check LodePNGDecoderSettings for more ignorable errors */ + unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/ + + /*use custom zlib decoder instead of built in one (default: null)*/ + unsigned (*custom_zlib)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGDecompressSettings*); + /*use custom deflate decoder instead of built in one (default: null) + if custom_zlib is used, custom_deflate is ignored since only the built in + zlib function will call custom_deflate*/ + unsigned (*custom_inflate)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGDecompressSettings*); + + const void* custom_context; /*optional custom settings for custom functions*/ +}; + +extern const LodePNGDecompressSettings lodepng_default_decompress_settings; +void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/* +Settings for zlib compression. Tweaking these settings tweaks the balance +between speed and compression ratio. +*/ +typedef struct LodePNGCompressSettings LodePNGCompressSettings; +struct LodePNGCompressSettings /*deflate = compress*/ +{ + /*LZ77 related settings*/ + unsigned btype; /*the block type for LZ (0, 1, 2 or 3, see zlib standard). Should be 2 for proper compression.*/ + unsigned use_lz77; /*whether or not to use LZ77. Should be 1 for proper compression.*/ + unsigned windowsize; /*must be a power of two <= 32768. higher compresses more but is slower. Default value: 2048.*/ + unsigned minmatch; /*mininum lz77 length. 3 is normally best, 6 can be better for some PNGs. Default: 0*/ + unsigned nicematch; /*stop searching if >= this length found. Set to 258 for best compression. Default: 128*/ + unsigned lazymatching; /*use lazy matching: better compression but a bit slower. Default: true*/ + + /*use custom zlib encoder instead of built in one (default: null)*/ + unsigned (*custom_zlib)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGCompressSettings*); + /*use custom deflate encoder instead of built in one (default: null) + if custom_zlib is used, custom_deflate is ignored since only the built in + zlib function will call custom_deflate*/ + unsigned (*custom_deflate)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGCompressSettings*); + + const void* custom_context; /*optional custom settings for custom functions*/ +}; + +extern const LodePNGCompressSettings lodepng_default_compress_settings; +void lodepng_compress_settings_init(LodePNGCompressSettings* settings); +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_PNG +/* +Color mode of an image. Contains all information required to decode the pixel +bits to RGBA colors. This information is the same as used in the PNG file +format, and is used both for PNG and raw image data in LodePNG. +*/ +typedef struct LodePNGColorMode +{ + /*header (IHDR)*/ + LodePNGColorType colortype; /*color type, see PNG standard or documentation further in this header file*/ + unsigned bitdepth; /*bits per sample, see PNG standard or documentation further in this header file*/ + + /* + palette (PLTE and tRNS) + + Dynamically allocated with the colors of the palette, including alpha. + When encoding a PNG, to store your colors in the palette of the LodePNGColorMode, first use + lodepng_palette_clear, then for each color use lodepng_palette_add. + If you encode an image without alpha with palette, don't forget to put value 255 in each A byte of the palette. + + When decoding, by default you can ignore this palette, since LodePNG already + fills the palette colors in the pixels of the raw RGBA output. + + The palette is only supported for color type 3. + */ + unsigned char* palette; /*palette in RGBARGBA... order. When allocated, must be either 0, or have size 1024*/ + size_t palettesize; /*palette size in number of colors (amount of bytes is 4 * palettesize)*/ + + /* + transparent color key (tRNS) + + This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit. + For greyscale PNGs, r, g and b will all 3 be set to the same. + + When decoding, by default you can ignore this information, since LodePNG sets + pixels with this key to transparent already in the raw RGBA output. + + The color key is only supported for color types 0 and 2. + */ + unsigned key_defined; /*is a transparent color key given? 0 = false, 1 = true*/ + unsigned key_r; /*red/greyscale component of color key*/ + unsigned key_g; /*green component of color key*/ + unsigned key_b; /*blue component of color key*/ +} LodePNGColorMode; + +/*init, cleanup and copy functions to use with this struct*/ +void lodepng_color_mode_init(LodePNGColorMode* info); +void lodepng_color_mode_cleanup(LodePNGColorMode* info); +/*return value is error code (0 means no error)*/ +unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source); + +void lodepng_palette_clear(LodePNGColorMode* info); +/*add 1 color to the palette*/ +unsigned lodepng_palette_add(LodePNGColorMode* info, + unsigned char r, unsigned char g, unsigned char b, unsigned char a); + +/*get the total amount of bits per pixel, based on colortype and bitdepth in the struct*/ +unsigned lodepng_get_bpp(const LodePNGColorMode* info); +/*get the amount of color channels used, based on colortype in the struct. +If a palette is used, it counts as 1 channel.*/ +unsigned lodepng_get_channels(const LodePNGColorMode* info); +/*is it a greyscale type? (only colortype 0 or 4)*/ +unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info); +/*has it got an alpha channel? (only colortype 2 or 6)*/ +unsigned lodepng_is_alpha_type(const LodePNGColorMode* info); +/*has it got a palette? (only colortype 3)*/ +unsigned lodepng_is_palette_type(const LodePNGColorMode* info); +/*only returns true if there is a palette and there is a value in the palette with alpha < 255. +Loops through the palette to check this.*/ +unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info); +/* +Check if the given color info indicates the possibility of having non-opaque pixels in the PNG image. +Returns true if the image can have translucent or invisible pixels (it still be opaque if it doesn't use such pixels). +Returns false if the image can only have opaque pixels. +In detail, it returns true only if it's a color type with alpha, or has a palette with non-opaque values, +or if "key_defined" is true. +*/ +unsigned lodepng_can_have_alpha(const LodePNGColorMode* info); +/*Returns the byte size of a raw image buffer with given width, height and color mode*/ +size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color); + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +/*The information of a Time chunk in PNG.*/ +typedef struct LodePNGTime +{ + unsigned year; /*2 bytes used (0-65535)*/ + unsigned month; /*1-12*/ + unsigned day; /*1-31*/ + unsigned hour; /*0-23*/ + unsigned minute; /*0-59*/ + unsigned second; /*0-60 (to allow for leap seconds)*/ +} LodePNGTime; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +/*Information about the PNG image, except pixels, width and height.*/ +typedef struct LodePNGInfo +{ + /*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/ + unsigned compression_method;/*compression method of the original file. Always 0.*/ + unsigned filter_method; /*filter method of the original file*/ + unsigned interlace_method; /*interlace method of the original file: 0=none, 1=Adam7*/ + LodePNGColorMode color; /*color type and bits, palette and transparency of the PNG file*/ + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /* + suggested background color chunk (bKGD) + This color uses the same color mode as the PNG (except alpha channel), which can be 1-bit to 16-bit. + + For greyscale PNGs, r, g and b will all 3 be set to the same. When encoding + the encoder writes the red one. For palette PNGs: When decoding, the RGB value + will be stored, not a palette index. But when encoding, specify the index of + the palette in background_r, the other two are then ignored. + + The decoder does not use this background color to edit the color of pixels. + */ + unsigned background_defined; /*is a suggested background color given?*/ + unsigned background_r; /*red component of suggested background color*/ + unsigned background_g; /*green component of suggested background color*/ + unsigned background_b; /*blue component of suggested background color*/ + + /* + non-international text chunks (tEXt and zTXt) + + The char** arrays each contain num strings. The actual messages are in + text_strings, while text_keys are keywords that give a short description what + the actual text represents, e.g. Title, Author, Description, or anything else. + + A keyword is minimum 1 character and maximum 79 characters long. It's + discouraged to use a single line length longer than 79 characters for texts. + + Don't allocate these text buffers yourself. Use the init/cleanup functions + correctly and use lodepng_add_text and lodepng_clear_text. + */ + size_t text_num; /*the amount of texts in these char** buffers (there may be more texts in itext)*/ + char** text_keys; /*the keyword of a text chunk (e.g. "Comment")*/ + char** text_strings; /*the actual text*/ + + /* + international text chunks (iTXt) + Similar to the non-international text chunks, but with additional strings + "langtags" and "transkeys". + */ + size_t itext_num; /*the amount of international texts in this PNG*/ + char** itext_keys; /*the English keyword of the text chunk (e.g. "Comment")*/ + char** itext_langtags; /*language tag for this text's language, ISO/IEC 646 string, e.g. ISO 639 language tag*/ + char** itext_transkeys; /*keyword translated to the international language - UTF-8 string*/ + char** itext_strings; /*the actual international text - UTF-8 string*/ + + /*time chunk (tIME)*/ + unsigned time_defined; /*set to 1 to make the encoder generate a tIME chunk*/ + LodePNGTime time; + + /*phys chunk (pHYs)*/ + unsigned phys_defined; /*if 0, there is no pHYs chunk and the values below are undefined, if 1 else there is one*/ + unsigned phys_x; /*pixels per unit in x direction*/ + unsigned phys_y; /*pixels per unit in y direction*/ + unsigned phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/ + + /* + unknown chunks + There are 3 buffers, one for each position in the PNG where unknown chunks can appear + each buffer contains all unknown chunks for that position consecutively + The 3 buffers are the unknown chunks between certain critical chunks: + 0: IHDR-PLTE, 1: PLTE-IDAT, 2: IDAT-IEND + Do not allocate or traverse this data yourself. Use the chunk traversing functions declared + later, such as lodepng_chunk_next and lodepng_chunk_append, to read/write this struct. + */ + unsigned char* unknown_chunks_data[3]; + size_t unknown_chunks_size[3]; /*size in bytes of the unknown chunks, given for protection*/ +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} LodePNGInfo; + +/*init, cleanup and copy functions to use with this struct*/ +void lodepng_info_init(LodePNGInfo* info); +void lodepng_info_cleanup(LodePNGInfo* info); +/*return value is error code (0 means no error)*/ +unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source); + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +void lodepng_clear_text(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/ +unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str); /*push back both texts at once*/ + +void lodepng_clear_itext(LodePNGInfo* info); /*use this to clear the itexts again after you filled them in*/ +unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, + const char* transkey, const char* str); /*push back the 4 texts of 1 chunk at once*/ +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +/* +Converts raw buffer from one color type to another color type, based on +LodePNGColorMode structs to describe the input and output color type. +See the reference manual at the end of this header file to see which color conversions are supported. +return value = LodePNG error code (0 if all went ok, an error if the conversion isn't supported) +The out buffer must have size (w * h * bpp + 7) / 8, where bpp is the bits per pixel +of the output color type (lodepng_get_bpp). +For < 8 bpp images, there should not be padding bits at the end of scanlines. +For 16-bit per channel colors, uses big endian format like PNG does. +Return value is LodePNG error code +*/ +unsigned lodepng_convert(unsigned char* out, const unsigned char* in, + const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, + unsigned w, unsigned h); + +#ifdef LODEPNG_COMPILE_DECODER +/* +Settings for the decoder. This contains settings for the PNG and the Zlib +decoder, but not the Info settings from the Info structs. +*/ +typedef struct LodePNGDecoderSettings +{ + LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/ + + /* Check LodePNGDecompressSettings for more ignorable errors */ + unsigned ignore_crc; /*ignore CRC checksums*/ + unsigned ignore_critical; /*ignore unknown critical chunks*/ + unsigned ignore_end; /*ignore issues at end of file if possible (missing IEND chunk, too large chunk, ...)*/ + + unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/ + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + unsigned read_text_chunks; /*if false but remember_unknown_chunks is true, they're stored in the unknown chunks*/ + /*store all bytes from unknown chunks in the LodePNGInfo (off by default, useful for a png editor)*/ + unsigned remember_unknown_chunks; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} LodePNGDecoderSettings; + +void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/*automatically use color type with less bits per pixel if losslessly possible. Default: AUTO*/ +typedef enum LodePNGFilterStrategy +{ + /*every filter at zero*/ + LFS_ZERO, + /*Use filter that gives minimum sum, as described in the official PNG filter heuristic.*/ + LFS_MINSUM, + /*Use the filter type that gives smallest Shannon entropy for this scanline. Depending + on the image, this is better or worse than minsum.*/ + LFS_ENTROPY, + /* + Brute-force-search PNG filters by compressing each filter for each scanline. + Experimental, very slow, and only rarely gives better compression than MINSUM. + */ + LFS_BRUTE_FORCE, + /*use predefined_filters buffer: you specify the filter type for each scanline*/ + LFS_PREDEFINED +} LodePNGFilterStrategy; + +/*Gives characteristics about the colors of the image, which helps decide which color model to use for encoding. +Used internally by default if "auto_convert" is enabled. Public because it's useful for custom algorithms.*/ +typedef struct LodePNGColorProfile +{ + unsigned colored; /*not greyscale*/ + unsigned key; /*image is not opaque and color key is possible instead of full alpha*/ + unsigned short key_r; /*key values, always as 16-bit, in 8-bit case the byte is duplicated, e.g. 65535 means 255*/ + unsigned short key_g; + unsigned short key_b; + unsigned alpha; /*image is not opaque and alpha channel or alpha palette required*/ + unsigned numcolors; /*amount of colors, up to 257. Not valid if bits == 16.*/ + unsigned char palette[1024]; /*Remembers up to the first 256 RGBA colors, in no particular order*/ + unsigned bits; /*bits per channel (not for palette). 1,2 or 4 for greyscale only. 16 if 16-bit per channel required.*/ +} LodePNGColorProfile; + +void lodepng_color_profile_init(LodePNGColorProfile* profile); + +/*Get a LodePNGColorProfile of the image.*/ +unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, + const unsigned char* image, unsigned w, unsigned h, + const LodePNGColorMode* mode_in); +/*The function LodePNG uses internally to decide the PNG color with auto_convert. +Chooses an optimal color model, e.g. grey if only grey pixels, palette if < 256 colors, ...*/ +unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, + const unsigned char* image, unsigned w, unsigned h, + const LodePNGColorMode* mode_in); + +/*Settings for the encoder.*/ +typedef struct LodePNGEncoderSettings +{ + LodePNGCompressSettings zlibsettings; /*settings for the zlib encoder, such as window size, ...*/ + + unsigned auto_convert; /*automatically choose output PNG color type. Default: true*/ + + /*If true, follows the official PNG heuristic: if the PNG uses a palette or lower than + 8 bit depth, set all filters to zero. Otherwise use the filter_strategy. Note that to + completely follow the official PNG heuristic, filter_palette_zero must be true and + filter_strategy must be LFS_MINSUM*/ + unsigned filter_palette_zero; + /*Which filter strategy to use when not using zeroes due to filter_palette_zero. + Set filter_palette_zero to 0 to ensure always using your chosen strategy. Default: LFS_MINSUM*/ + LodePNGFilterStrategy filter_strategy; + /*used if filter_strategy is LFS_PREDEFINED. In that case, this must point to a buffer with + the same length as the amount of scanlines in the image, and each value must <= 5. You + have to cleanup this buffer, LodePNG will never free it. Don't forget that filter_palette_zero + must be set to 0 to ensure this is also used on palette or low bitdepth images.*/ + const unsigned char* predefined_filters; + + /*force creating a PLTE chunk if colortype is 2 or 6 (= a suggested palette). + If colortype is 3, PLTE is _always_ created.*/ + unsigned force_palette; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*add LodePNG identifier and version as a text chunk, for debugging*/ + unsigned add_id; + /*encode text chunks as zTXt chunks instead of tEXt chunks, and use compression in iTXt chunks*/ + unsigned text_compression; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} LodePNGEncoderSettings; + +void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings); +#endif /*LODEPNG_COMPILE_ENCODER*/ + + +#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) +/*The settings, state and information for extended encoding and decoding.*/ +typedef struct LodePNGState +{ +#ifdef LODEPNG_COMPILE_DECODER + LodePNGDecoderSettings decoder; /*the decoding settings*/ +#endif /*LODEPNG_COMPILE_DECODER*/ +#ifdef LODEPNG_COMPILE_ENCODER + LodePNGEncoderSettings encoder; /*the encoding settings*/ +#endif /*LODEPNG_COMPILE_ENCODER*/ + LodePNGColorMode info_raw; /*specifies the format in which you would like to get the raw pixel buffer*/ + LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/ + unsigned error; +#ifdef LODEPNG_COMPILE_CPP + /* For the lodepng::State subclass. */ + virtual ~LodePNGState(){} +#endif +} LodePNGState; + +/*init, cleanup and copy functions to use with this struct*/ +void lodepng_state_init(LodePNGState* state); +void lodepng_state_cleanup(LodePNGState* state); +void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source); +#endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */ + +#ifdef LODEPNG_COMPILE_DECODER +/* +Same as lodepng_decode_memory, but uses a LodePNGState to allow custom settings and +getting much more information about the PNG image and color mode. +*/ +unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize); + +/* +Read the PNG header, but not the actual data. This returns only the information +that is in the header chunk of the PNG, such as width, height and color type. The +information is placed in the info_png field of the LodePNGState. +*/ +unsigned lodepng_inspect(unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize); +#endif /*LODEPNG_COMPILE_DECODER*/ + + +#ifdef LODEPNG_COMPILE_ENCODER +/*This function allocates the out buffer with standard malloc and stores the size in *outsize.*/ +unsigned lodepng_encode(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h, + LodePNGState* state); +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/* +The lodepng_chunk functions are normally not needed, except to traverse the +unknown chunks stored in the LodePNGInfo struct, or add new ones to it. +It also allows traversing the chunks of an encoded PNG file yourself. + +PNG standard chunk naming conventions: +First byte: uppercase = critical, lowercase = ancillary +Second byte: uppercase = public, lowercase = private +Third byte: must be uppercase +Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy +*/ + +/* +Gets the length of the data of the chunk. Total chunk length has 12 bytes more. +There must be at least 4 bytes to read from. If the result value is too large, +it may be corrupt data. +*/ +unsigned lodepng_chunk_length(const unsigned char* chunk); + +/*puts the 4-byte type in null terminated string*/ +void lodepng_chunk_type(char type[5], const unsigned char* chunk); + +/*check if the type is the given type*/ +unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type); + +/*0: it's one of the critical chunk types, 1: it's an ancillary chunk (see PNG standard)*/ +unsigned char lodepng_chunk_ancillary(const unsigned char* chunk); + +/*0: public, 1: private (see PNG standard)*/ +unsigned char lodepng_chunk_private(const unsigned char* chunk); + +/*0: the chunk is unsafe to copy, 1: the chunk is safe to copy (see PNG standard)*/ +unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk); + +/*get pointer to the data of the chunk, where the input points to the header of the chunk*/ +unsigned char* lodepng_chunk_data(unsigned char* chunk); +const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk); + +/*returns 0 if the crc is correct, 1 if it's incorrect (0 for OK as usual!)*/ +unsigned lodepng_chunk_check_crc(const unsigned char* chunk); + +/*generates the correct CRC from the data and puts it in the last 4 bytes of the chunk*/ +void lodepng_chunk_generate_crc(unsigned char* chunk); + +/*iterate to next chunks. don't use on IEND chunk, as there is no next chunk then*/ +unsigned char* lodepng_chunk_next(unsigned char* chunk); +const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk); + +/* +Appends chunk to the data in out. The given chunk should already have its chunk header. +The out variable and outlength are updated to reflect the new reallocated buffer. +Returns error code (0 if it went ok) +*/ +unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk); + +/* +Appends new chunk to out. The chunk to append is given by giving its length, type +and data separately. The type is a 4-letter string. +The out variable and outlength are updated to reflect the new reallocated buffer. +Returne error code (0 if it went ok) +*/ +unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, + const char* type, const unsigned char* data); + + +/*Calculate CRC32 of buffer*/ +unsigned lodepng_crc32(const unsigned char* buf, size_t len); +#endif /*LODEPNG_COMPILE_PNG*/ + + +#ifdef LODEPNG_COMPILE_ZLIB +/* +This zlib part can be used independently to zlib compress and decompress a +buffer. It cannot be used to create gzip files however, and it only supports the +part of zlib that is required for PNG, it does not support dictionaries. +*/ + +#ifdef LODEPNG_COMPILE_DECODER +/*Inflate a buffer. Inflate is the decompression step of deflate. Out buffer must be freed after use.*/ +unsigned lodepng_inflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings); + +/* +Decompresses Zlib data. Reallocates the out buffer and appends the data. The +data must be according to the zlib specification. +Either, *out must be NULL and *outsize must be 0, or, *out must be a valid +buffer and *outsize its size in bytes. out must be freed by user after usage. +*/ +unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/* +Compresses data with Zlib. Reallocates the out buffer and appends the data. +Zlib adds a small header and trailer around the deflate data. +The data is output in the format of the zlib specification. +Either, *out must be NULL and *outsize must be 0, or, *out must be a valid +buffer and *outsize its size in bytes. out must be freed by user after usage. +*/ +unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings); + +/* +Find length-limited Huffman code for given frequencies. This function is in the +public interface only for tests, it's used internally by lodepng_deflate. +*/ +unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies, + size_t numcodes, unsigned maxbitlen); + +/*Compress a buffer with deflate. See RFC 1951. Out buffer must be freed after use.*/ +unsigned lodepng_deflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings); + +#endif /*LODEPNG_COMPILE_ENCODER*/ +#endif /*LODEPNG_COMPILE_ZLIB*/ + +#ifdef LODEPNG_COMPILE_DISK +/* +Load a file from disk into buffer. The function allocates the out buffer, and +after usage you should free it. +out: output parameter, contains pointer to loaded buffer. +outsize: output parameter, size of the allocated out buffer +filename: the path to the file to load +return value: error code (0 means ok) +*/ +unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename); + +/* +Save a file from buffer to disk. Warning, if it exists, this function overwrites +the file without warning! +buffer: the buffer to write +buffersize: size of the buffer to write +filename: the path to the file to save to +return value: error code (0 means ok) +*/ +unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename); +#endif /*LODEPNG_COMPILE_DISK*/ + +#ifdef LODEPNG_COMPILE_CPP +/* The LodePNG C++ wrapper uses std::vectors instead of manually allocated memory buffers. */ +namespace lodepng +{ +#ifdef LODEPNG_COMPILE_PNG +class State : public LodePNGState +{ + public: + State(); + State(const State& other); + virtual ~State(); + State& operator=(const State& other); +}; + +#ifdef LODEPNG_COMPILE_DECODER +/* Same as other lodepng::decode, but using a State for more settings and information. */ +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + State& state, + const unsigned char* in, size_t insize); +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + State& state, + const std::vector& in); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/* Same as other lodepng::encode, but using a State for more settings and information. */ +unsigned encode(std::vector& out, + const unsigned char* in, unsigned w, unsigned h, + State& state); +unsigned encode(std::vector& out, + const std::vector& in, unsigned w, unsigned h, + State& state); +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_DISK +/* +Load a file from disk into an std::vector. +return value: error code (0 means ok) +*/ +unsigned load_file(std::vector& buffer, const std::string& filename); + +/* +Save the binary data in an std::vector to a file on disk. The file is overwritten +without warning. +*/ +unsigned save_file(const std::vector& buffer, const std::string& filename); +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_PNG */ + +#ifdef LODEPNG_COMPILE_ZLIB +#ifdef LODEPNG_COMPILE_DECODER +/* Zlib-decompress an unsigned char buffer */ +unsigned decompress(std::vector& out, const unsigned char* in, size_t insize, + const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); + +/* Zlib-decompress an std::vector */ +unsigned decompress(std::vector& out, const std::vector& in, + const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); +#endif /* LODEPNG_COMPILE_DECODER */ + +#ifdef LODEPNG_COMPILE_ENCODER +/* Zlib-compress an unsigned char buffer */ +unsigned compress(std::vector& out, const unsigned char* in, size_t insize, + const LodePNGCompressSettings& settings = lodepng_default_compress_settings); + +/* Zlib-compress an std::vector */ +unsigned compress(std::vector& out, const std::vector& in, + const LodePNGCompressSettings& settings = lodepng_default_compress_settings); +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_ZLIB */ +} /* namespace lodepng */ +#endif /*LODEPNG_COMPILE_CPP*/ + +/* +TODO: +[.] test if there are no memory leaks or security exploits - done a lot but needs to be checked often +[.] check compatibility with various compilers - done but needs to be redone for every newer version +[X] converting color to 16-bit per channel types +[ ] read all public PNG chunk types (but never let the color profile and gamma ones touch RGB values) +[ ] make sure encoder generates no chunks with size > (2^31)-1 +[ ] partial decoding (stream processing) +[X] let the "isFullyOpaque" function check color keys and transparent palettes too +[X] better name for the variables "codes", "codesD", "codelengthcodes", "clcl" and "lldl" +[ ] don't stop decoding on errors like 69, 57, 58 (make warnings) +[ ] make warnings like: oob palette, checksum fail, data after iend, wrong/unknown crit chunk, no null terminator in text, ... +[ ] let the C++ wrapper catch exceptions coming from the standard library and return LodePNG error codes +[ ] allow user to provide custom color conversion functions, e.g. for premultiplied alpha, padding bits or not, ... +[ ] allow user to give data (void*) to custom allocator +*/ + +#endif /*LODEPNG_H inclusion guard*/ + +/* +LodePNG Documentation +--------------------- + +0. table of contents +-------------------- + + 1. about + 1.1. supported features + 1.2. features not supported + 2. C and C++ version + 3. security + 4. decoding + 5. encoding + 6. color conversions + 6.1. PNG color types + 6.2. color conversions + 6.3. padding bits + 6.4. A note about 16-bits per channel and endianness + 7. error values + 8. chunks and PNG editing + 9. compiler support + 10. examples + 10.1. decoder C++ example + 10.2. decoder C example + 11. state settings reference + 12. changes + 13. contact information + + +1. about +-------- + +PNG is a file format to store raster images losslessly with good compression, +supporting different color types and alpha channel. + +LodePNG is a PNG codec according to the Portable Network Graphics (PNG) +Specification (Second Edition) - W3C Recommendation 10 November 2003. + +The specifications used are: + +*) Portable Network Graphics (PNG) Specification (Second Edition): + http://www.w3.org/TR/2003/REC-PNG-20031110 +*) RFC 1950 ZLIB Compressed Data Format version 3.3: + http://www.gzip.org/zlib/rfc-zlib.html +*) RFC 1951 DEFLATE Compressed Data Format Specification ver 1.3: + http://www.gzip.org/zlib/rfc-deflate.html + +The most recent version of LodePNG can currently be found at +http://lodev.org/lodepng/ + +LodePNG works both in C (ISO C90) and C++, with a C++ wrapper that adds +extra functionality. + +LodePNG exists out of two files: +-lodepng.h: the header file for both C and C++ +-lodepng.c(pp): give it the name lodepng.c or lodepng.cpp (or .cc) depending on your usage + +If you want to start using LodePNG right away without reading this doc, get the +examples from the LodePNG website to see how to use it in code, or check the +smaller examples in chapter 13 here. + +LodePNG is simple but only supports the basic requirements. To achieve +simplicity, the following design choices were made: There are no dependencies +on any external library. There are functions to decode and encode a PNG with +a single function call, and extended versions of these functions taking a +LodePNGState struct allowing to specify or get more information. By default +the colors of the raw image are always RGB or RGBA, no matter what color type +the PNG file uses. To read and write files, there are simple functions to +convert the files to/from buffers in memory. + +This all makes LodePNG suitable for loading textures in games, demos and small +programs, ... It's less suitable for full fledged image editors, loading PNGs +over network (it requires all the image data to be available before decoding can +begin), life-critical systems, ... + +1.1. supported features +----------------------- + +The following features are supported by the decoder: + +*) decoding of PNGs with any color type, bit depth and interlace mode, to a 24- or 32-bit color raw image, + or the same color type as the PNG +*) encoding of PNGs, from any raw image to 24- or 32-bit color, or the same color type as the raw image +*) Adam7 interlace and deinterlace for any color type +*) loading the image from harddisk or decoding it from a buffer from other sources than harddisk +*) support for alpha channels, including RGBA color model, translucent palettes and color keying +*) zlib decompression (inflate) +*) zlib compression (deflate) +*) CRC32 and ADLER32 checksums +*) handling of unknown chunks, allowing making a PNG editor that stores custom and unknown chunks. +*) the following chunks are supported (generated/interpreted) by both encoder and decoder: + IHDR: header information + PLTE: color palette + IDAT: pixel data + IEND: the final chunk + tRNS: transparency for palettized images + tEXt: textual information + zTXt: compressed textual information + iTXt: international textual information + bKGD: suggested background color + pHYs: physical dimensions + tIME: modification time + +1.2. features not supported +--------------------------- + +The following features are _not_ supported: + +*) some features needed to make a conformant PNG-Editor might be still missing. +*) partial loading/stream processing. All data must be available and is processed in one call. +*) The following public chunks are not supported but treated as unknown chunks by LodePNG + cHRM, gAMA, iCCP, sRGB, sBIT, hIST, sPLT + Some of these are not supported on purpose: LodePNG wants to provide the RGB values + stored in the pixels, not values modified by system dependent gamma or color models. + + +2. C and C++ version +-------------------- + +The C version uses buffers allocated with alloc that you need to free() +yourself. You need to use init and cleanup functions for each struct whenever +using a struct from the C version to avoid exploits and memory leaks. + +The C++ version has extra functions with std::vectors in the interface and the +lodepng::State class which is a LodePNGState with constructor and destructor. + +These files work without modification for both C and C++ compilers because all +the additional C++ code is in "#ifdef __cplusplus" blocks that make C-compilers +ignore it, and the C code is made to compile both with strict ISO C90 and C++. + +To use the C++ version, you need to rename the source file to lodepng.cpp +(instead of lodepng.c), and compile it with a C++ compiler. + +To use the C version, you need to rename the source file to lodepng.c (instead +of lodepng.cpp), and compile it with a C compiler. + + +3. Security +----------- + +Even if carefully designed, it's always possible that LodePNG contains possible +exploits. If you discover one, please let me know, and it will be fixed. + +When using LodePNG, care has to be taken with the C version of LodePNG, as well +as the C-style structs when working with C++. The following conventions are used +for all C-style structs: + +-if a struct has a corresponding init function, always call the init function when making a new one +-if a struct has a corresponding cleanup function, call it before the struct disappears to avoid memory leaks +-if a struct has a corresponding copy function, use the copy function instead of "=". + The destination must also be inited already. + + +4. Decoding +----------- + +Decoding converts a PNG compressed image to a raw pixel buffer. + +Most documentation on using the decoder is at its declarations in the header +above. For C, simple decoding can be done with functions such as +lodepng_decode32, and more advanced decoding can be done with the struct +LodePNGState and lodepng_decode. For C++, all decoding can be done with the +various lodepng::decode functions, and lodepng::State can be used for advanced +features. + +When using the LodePNGState, it uses the following fields for decoding: +*) LodePNGInfo info_png: it stores extra information about the PNG (the input) in here +*) LodePNGColorMode info_raw: here you can say what color mode of the raw image (the output) you want to get +*) LodePNGDecoderSettings decoder: you can specify a few extra settings for the decoder to use + +LodePNGInfo info_png +-------------------- + +After decoding, this contains extra information of the PNG image, except the actual +pixels, width and height because these are already gotten directly from the decoder +functions. + +It contains for example the original color type of the PNG image, text comments, +suggested background color, etc... More details about the LodePNGInfo struct are +at its declaration documentation. + +LodePNGColorMode info_raw +------------------------- + +When decoding, here you can specify which color type you want +the resulting raw image to be. If this is different from the colortype of the +PNG, then the decoder will automatically convert the result. This conversion +always works, except if you want it to convert a color PNG to greyscale or to +a palette with missing colors. + +By default, 32-bit color is used for the result. + +LodePNGDecoderSettings decoder +------------------------------ + +The settings can be used to ignore the errors created by invalid CRC and Adler32 +chunks, and to disable the decoding of tEXt chunks. + +There's also a setting color_convert, true by default. If false, no conversion +is done, the resulting data will be as it was in the PNG (after decompression) +and you'll have to puzzle the colors of the pixels together yourself using the +color type information in the LodePNGInfo. + + +5. Encoding +----------- + +Encoding converts a raw pixel buffer to a PNG compressed image. + +Most documentation on using the encoder is at its declarations in the header +above. For C, simple encoding can be done with functions such as +lodepng_encode32, and more advanced decoding can be done with the struct +LodePNGState and lodepng_encode. For C++, all encoding can be done with the +various lodepng::encode functions, and lodepng::State can be used for advanced +features. + +Like the decoder, the encoder can also give errors. However it gives less errors +since the encoder input is trusted, the decoder input (a PNG image that could +be forged by anyone) is not trusted. + +When using the LodePNGState, it uses the following fields for encoding: +*) LodePNGInfo info_png: here you specify how you want the PNG (the output) to be. +*) LodePNGColorMode info_raw: here you say what color type of the raw image (the input) has +*) LodePNGEncoderSettings encoder: you can specify a few settings for the encoder to use + +LodePNGInfo info_png +-------------------- + +When encoding, you use this the opposite way as when decoding: for encoding, +you fill in the values you want the PNG to have before encoding. By default it's +not needed to specify a color type for the PNG since it's automatically chosen, +but it's possible to choose it yourself given the right settings. + +The encoder will not always exactly match the LodePNGInfo struct you give, +it tries as close as possible. Some things are ignored by the encoder. The +encoder uses, for example, the following settings from it when applicable: +colortype and bitdepth, text chunks, time chunk, the color key, the palette, the +background color, the interlace method, unknown chunks, ... + +When encoding to a PNG with colortype 3, the encoder will generate a PLTE chunk. +If the palette contains any colors for which the alpha channel is not 255 (so +there are translucent colors in the palette), it'll add a tRNS chunk. + +LodePNGColorMode info_raw +------------------------- + +You specify the color type of the raw image that you give to the input here, +including a possible transparent color key and palette you happen to be using in +your raw image data. + +By default, 32-bit color is assumed, meaning your input has to be in RGBA +format with 4 bytes (unsigned chars) per pixel. + +LodePNGEncoderSettings encoder +------------------------------ + +The following settings are supported (some are in sub-structs): +*) auto_convert: when this option is enabled, the encoder will +automatically choose the smallest possible color mode (including color key) that +can encode the colors of all pixels without information loss. +*) btype: the block type for LZ77. 0 = uncompressed, 1 = fixed huffman tree, + 2 = dynamic huffman tree (best compression). Should be 2 for proper + compression. +*) use_lz77: whether or not to use LZ77 for compressed block types. Should be + true for proper compression. +*) windowsize: the window size used by the LZ77 encoder (1 - 32768). Has value + 2048 by default, but can be set to 32768 for better, but slow, compression. +*) force_palette: if colortype is 2 or 6, you can make the encoder write a PLTE + chunk if force_palette is true. This can used as suggested palette to convert + to by viewers that don't support more than 256 colors (if those still exist) +*) add_id: add text chunk "Encoder: LodePNG " to the image. +*) text_compression: default 1. If 1, it'll store texts as zTXt instead of tEXt chunks. + zTXt chunks use zlib compression on the text. This gives a smaller result on + large texts but a larger result on small texts (such as a single program name). + It's all tEXt or all zTXt though, there's no separate setting per text yet. + + +6. color conversions +-------------------- + +An important thing to note about LodePNG, is that the color type of the PNG, and +the color type of the raw image, are completely independent. By default, when +you decode a PNG, you get the result as a raw image in the color type you want, +no matter whether the PNG was encoded with a palette, greyscale or RGBA color. +And if you encode an image, by default LodePNG will automatically choose the PNG +color type that gives good compression based on the values of colors and amount +of colors in the image. It can be configured to let you control it instead as +well, though. + +To be able to do this, LodePNG does conversions from one color mode to another. +It can convert from almost any color type to any other color type, except the +following conversions: RGB to greyscale is not supported, and converting to a +palette when the palette doesn't have a required color is not supported. This is +not supported on purpose: this is information loss which requires a color +reduction algorithm that is beyong the scope of a PNG encoder (yes, RGB to grey +is easy, but there are multiple ways if you want to give some channels more +weight). + +By default, when decoding, you get the raw image in 32-bit RGBA or 24-bit RGB +color, no matter what color type the PNG has. And by default when encoding, +LodePNG automatically picks the best color model for the output PNG, and expects +the input image to be 32-bit RGBA or 24-bit RGB. So, unless you want to control +the color format of the images yourself, you can skip this chapter. + +6.1. PNG color types +-------------------- + +A PNG image can have many color types, ranging from 1-bit color to 64-bit color, +as well as palettized color modes. After the zlib decompression and unfiltering +in the PNG image is done, the raw pixel data will have that color type and thus +a certain amount of bits per pixel. If you want the output raw image after +decoding to have another color type, a conversion is done by LodePNG. + +The PNG specification gives the following color types: + +0: greyscale, bit depths 1, 2, 4, 8, 16 +2: RGB, bit depths 8 and 16 +3: palette, bit depths 1, 2, 4 and 8 +4: greyscale with alpha, bit depths 8 and 16 +6: RGBA, bit depths 8 and 16 + +Bit depth is the amount of bits per pixel per color channel. So the total amount +of bits per pixel is: amount of channels * bitdepth. + +6.2. color conversions +---------------------- + +As explained in the sections about the encoder and decoder, you can specify +color types and bit depths in info_png and info_raw to change the default +behaviour. + +If, when decoding, you want the raw image to be something else than the default, +you need to set the color type and bit depth you want in the LodePNGColorMode, +or the parameters colortype and bitdepth of the simple decoding function. + +If, when encoding, you use another color type than the default in the raw input +image, you need to specify its color type and bit depth in the LodePNGColorMode +of the raw image, or use the parameters colortype and bitdepth of the simple +encoding function. + +If, when encoding, you don't want LodePNG to choose the output PNG color type +but control it yourself, you need to set auto_convert in the encoder settings +to false, and specify the color type you want in the LodePNGInfo of the +encoder (including palette: it can generate a palette if auto_convert is true, +otherwise not). + +If the input and output color type differ (whether user chosen or auto chosen), +LodePNG will do a color conversion, which follows the rules below, and may +sometimes result in an error. + +To avoid some confusion: +-the decoder converts from PNG to raw image +-the encoder converts from raw image to PNG +-the colortype and bitdepth in LodePNGColorMode info_raw, are those of the raw image +-the colortype and bitdepth in the color field of LodePNGInfo info_png, are those of the PNG +-when encoding, the color type in LodePNGInfo is ignored if auto_convert + is enabled, it is automatically generated instead +-when decoding, the color type in LodePNGInfo is set by the decoder to that of the original + PNG image, but it can be ignored since the raw image has the color type you requested instead +-if the color type of the LodePNGColorMode and PNG image aren't the same, a conversion + between the color types is done if the color types are supported. If it is not + supported, an error is returned. If the types are the same, no conversion is done. +-even though some conversions aren't supported, LodePNG supports loading PNGs from any + colortype and saving PNGs to any colortype, sometimes it just requires preparing + the raw image correctly before encoding. +-both encoder and decoder use the same color converter. + +Non supported color conversions: +-color to greyscale: no error is thrown, but the result will look ugly because +only the red channel is taken +-anything to palette when that palette does not have that color in it: in this +case an error is thrown + +Supported color conversions: +-anything to 8-bit RGB, 8-bit RGBA, 16-bit RGB, 16-bit RGBA +-any grey or grey+alpha, to grey or grey+alpha +-anything to a palette, as long as the palette has the requested colors in it +-removing alpha channel +-higher to smaller bitdepth, and vice versa + +If you want no color conversion to be done (e.g. for speed or control): +-In the encoder, you can make it save a PNG with any color type by giving the +raw color mode and LodePNGInfo the same color mode, and setting auto_convert to +false. +-In the decoder, you can make it store the pixel data in the same color type +as the PNG has, by setting the color_convert setting to false. Settings in +info_raw are then ignored. + +The function lodepng_convert does the color conversion. It is available in the +interface but normally isn't needed since the encoder and decoder already call +it. + +6.3. padding bits +----------------- + +In the PNG file format, if a less than 8-bit per pixel color type is used and the scanlines +have a bit amount that isn't a multiple of 8, then padding bits are used so that each +scanline starts at a fresh byte. But that is NOT true for the LodePNG raw input and output. +The raw input image you give to the encoder, and the raw output image you get from the decoder +will NOT have these padding bits, e.g. in the case of a 1-bit image with a width +of 7 pixels, the first pixel of the second scanline will the the 8th bit of the first byte, +not the first bit of a new byte. + +6.4. A note about 16-bits per channel and endianness +---------------------------------------------------- + +LodePNG uses unsigned char arrays for 16-bit per channel colors too, just like +for any other color format. The 16-bit values are stored in big endian (most +significant byte first) in these arrays. This is the opposite order of the +little endian used by x86 CPU's. + +LodePNG always uses big endian because the PNG file format does so internally. +Conversions to other formats than PNG uses internally are not supported by +LodePNG on purpose, there are myriads of formats, including endianness of 16-bit +colors, the order in which you store R, G, B and A, and so on. Supporting and +converting to/from all that is outside the scope of LodePNG. + +This may mean that, depending on your use case, you may want to convert the big +endian output of LodePNG to little endian with a for loop. This is certainly not +always needed, many applications and libraries support big endian 16-bit colors +anyway, but it means you cannot simply cast the unsigned char* buffer to an +unsigned short* buffer on x86 CPUs. + + +7. error values +--------------- + +All functions in LodePNG that return an error code, return 0 if everything went +OK, or a non-zero code if there was an error. + +The meaning of the LodePNG error values can be retrieved with the function +lodepng_error_text: given the numerical error code, it returns a description +of the error in English as a string. + +Check the implementation of lodepng_error_text to see the meaning of each code. + + +8. chunks and PNG editing +------------------------- + +If you want to add extra chunks to a PNG you encode, or use LodePNG for a PNG +editor that should follow the rules about handling of unknown chunks, or if your +program is able to read other types of chunks than the ones handled by LodePNG, +then that's possible with the chunk functions of LodePNG. + +A PNG chunk has the following layout: + +4 bytes length +4 bytes type name +length bytes data +4 bytes CRC + +8.1. iterating through chunks +----------------------------- + +If you have a buffer containing the PNG image data, then the first chunk (the +IHDR chunk) starts at byte number 8 of that buffer. The first 8 bytes are the +signature of the PNG and are not part of a chunk. But if you start at byte 8 +then you have a chunk, and can check the following things of it. + +NOTE: none of these functions check for memory buffer boundaries. To avoid +exploits, always make sure the buffer contains all the data of the chunks. +When using lodepng_chunk_next, make sure the returned value is within the +allocated memory. + +unsigned lodepng_chunk_length(const unsigned char* chunk): + +Get the length of the chunk's data. The total chunk length is this length + 12. + +void lodepng_chunk_type(char type[5], const unsigned char* chunk): +unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type): + +Get the type of the chunk or compare if it's a certain type + +unsigned char lodepng_chunk_critical(const unsigned char* chunk): +unsigned char lodepng_chunk_private(const unsigned char* chunk): +unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk): + +Check if the chunk is critical in the PNG standard (only IHDR, PLTE, IDAT and IEND are). +Check if the chunk is private (public chunks are part of the standard, private ones not). +Check if the chunk is safe to copy. If it's not, then, when modifying data in a critical +chunk, unsafe to copy chunks of the old image may NOT be saved in the new one if your +program doesn't handle that type of unknown chunk. + +unsigned char* lodepng_chunk_data(unsigned char* chunk): +const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk): + +Get a pointer to the start of the data of the chunk. + +unsigned lodepng_chunk_check_crc(const unsigned char* chunk): +void lodepng_chunk_generate_crc(unsigned char* chunk): + +Check if the crc is correct or generate a correct one. + +unsigned char* lodepng_chunk_next(unsigned char* chunk): +const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk): + +Iterate to the next chunk. This works if you have a buffer with consecutive chunks. Note that these +functions do no boundary checking of the allocated data whatsoever, so make sure there is enough +data available in the buffer to be able to go to the next chunk. + +unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk): +unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, + const char* type, const unsigned char* data): + +These functions are used to create new chunks that are appended to the data in *out that has +length *outlength. The append function appends an existing chunk to the new data. The create +function creates a new chunk with the given parameters and appends it. Type is the 4-letter +name of the chunk. + +8.2. chunks in info_png +----------------------- + +The LodePNGInfo struct contains fields with the unknown chunk in it. It has 3 +buffers (each with size) to contain 3 types of unknown chunks: +the ones that come before the PLTE chunk, the ones that come between the PLTE +and the IDAT chunks, and the ones that come after the IDAT chunks. +It's necessary to make the distionction between these 3 cases because the PNG +standard forces to keep the ordering of unknown chunks compared to the critical +chunks, but does not force any other ordering rules. + +info_png.unknown_chunks_data[0] is the chunks before PLTE +info_png.unknown_chunks_data[1] is the chunks after PLTE, before IDAT +info_png.unknown_chunks_data[2] is the chunks after IDAT + +The chunks in these 3 buffers can be iterated through and read by using the same +way described in the previous subchapter. + +When using the decoder to decode a PNG, you can make it store all unknown chunks +if you set the option settings.remember_unknown_chunks to 1. By default, this +option is off (0). + +The encoder will always encode unknown chunks that are stored in the info_png. +If you need it to add a particular chunk that isn't known by LodePNG, you can +use lodepng_chunk_append or lodepng_chunk_create to the chunk data in +info_png.unknown_chunks_data[x]. + +Chunks that are known by LodePNG should not be added in that way. E.g. to make +LodePNG add a bKGD chunk, set background_defined to true and add the correct +parameters there instead. + + +9. compiler support +------------------- + +No libraries other than the current standard C library are needed to compile +LodePNG. For the C++ version, only the standard C++ library is needed on top. +Add the files lodepng.c(pp) and lodepng.h to your project, include +lodepng.h where needed, and your program can read/write PNG files. + +It is compatible with C90 and up, and C++03 and up. + +If performance is important, use optimization when compiling! For both the +encoder and decoder, this makes a large difference. + +Make sure that LodePNG is compiled with the same compiler of the same version +and with the same settings as the rest of the program, or the interfaces with +std::vectors and std::strings in C++ can be incompatible. + +CHAR_BITS must be 8 or higher, because LodePNG uses unsigned chars for octets. + +*) gcc and g++ + +LodePNG is developed in gcc so this compiler is natively supported. It gives no +warnings with compiler options "-Wall -Wextra -pedantic -ansi", with gcc and g++ +version 4.7.1 on Linux, 32-bit and 64-bit. + +*) Clang + +Fully supported and warning-free. + +*) Mingw + +The Mingw compiler (a port of gcc for Windows) should be fully supported by +LodePNG. + +*) Visual Studio and Visual C++ Express Edition + +LodePNG should be warning-free with warning level W4. Two warnings were disabled +with pragmas though: warning 4244 about implicit conversions, and warning 4996 +where it wants to use a non-standard function fopen_s instead of the standard C +fopen. + +Visual Studio may want "stdafx.h" files to be included in each source file and +give an error "unexpected end of file while looking for precompiled header". +This is not standard C++ and will not be added to the stock LodePNG. You can +disable it for lodepng.cpp only by right clicking it, Properties, C/C++, +Precompiled Headers, and set it to Not Using Precompiled Headers there. + +NOTE: Modern versions of VS should be fully supported, but old versions, e.g. +VS6, are not guaranteed to work. + +*) Compilers on Macintosh + +LodePNG has been reported to work both with gcc and LLVM for Macintosh, both for +C and C++. + +*) Other Compilers + +If you encounter problems on any compilers, feel free to let me know and I may +try to fix it if the compiler is modern and standards complient. + + +10. examples +------------ + +This decoder example shows the most basic usage of LodePNG. More complex +examples can be found on the LodePNG website. + +10.1. decoder C++ example +------------------------- + +#include "lodepng.h" +#include + +int main(int argc, char *argv[]) +{ + const char* filename = argc > 1 ? argv[1] : "test.png"; + + //load and decode + std::vector image; + unsigned width, height; + unsigned error = lodepng::decode(image, width, height, filename); + + //if there's an error, display it + if(error) std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl; + + //the pixels are now in the vector "image", 4 bytes per pixel, ordered RGBARGBA..., use it as texture, draw it, ... +} + +10.2. decoder C example +----------------------- + +#include "lodepng.h" + +int main(int argc, char *argv[]) +{ + unsigned error; + unsigned char* image; + size_t width, height; + const char* filename = argc > 1 ? argv[1] : "test.png"; + + error = lodepng_decode32_file(&image, &width, &height, filename); + + if(error) printf("decoder error %u: %s\n", error, lodepng_error_text(error)); + + / * use image here * / + + free(image); + return 0; +} + +11. state settings reference +---------------------------- + +A quick reference of some settings to set on the LodePNGState + +For decoding: + +state.decoder.zlibsettings.ignore_adler32: ignore ADLER32 checksums +state.decoder.zlibsettings.custom_...: use custom inflate function +state.decoder.ignore_crc: ignore CRC checksums +state.decoder.ignore_critical: ignore unknown critical chunks +state.decoder.ignore_end: ignore missing IEND chunk. May fail if this corruption causes other errors +state.decoder.color_convert: convert internal PNG color to chosen one +state.decoder.read_text_chunks: whether to read in text metadata chunks +state.decoder.remember_unknown_chunks: whether to read in unknown chunks +state.info_raw.colortype: desired color type for decoded image +state.info_raw.bitdepth: desired bit depth for decoded image +state.info_raw....: more color settings, see struct LodePNGColorMode +state.info_png....: no settings for decoder but ouput, see struct LodePNGInfo + +For encoding: + +state.encoder.zlibsettings.btype: disable compression by setting it to 0 +state.encoder.zlibsettings.use_lz77: use LZ77 in compression +state.encoder.zlibsettings.windowsize: tweak LZ77 windowsize +state.encoder.zlibsettings.minmatch: tweak min LZ77 length to match +state.encoder.zlibsettings.nicematch: tweak LZ77 match where to stop searching +state.encoder.zlibsettings.lazymatching: try one more LZ77 matching +state.encoder.zlibsettings.custom_...: use custom deflate function +state.encoder.auto_convert: choose optimal PNG color type, if 0 uses info_png +state.encoder.filter_palette_zero: PNG filter strategy for palette +state.encoder.filter_strategy: PNG filter strategy to encode with +state.encoder.force_palette: add palette even if not encoding to one +state.encoder.add_id: add LodePNG identifier and version as a text chunk +state.encoder.text_compression: use compressed text chunks for metadata +state.info_raw.colortype: color type of raw input image you provide +state.info_raw.bitdepth: bit depth of raw input image you provide +state.info_raw: more color settings, see struct LodePNGColorMode +state.info_png.color.colortype: desired color type if auto_convert is false +state.info_png.color.bitdepth: desired bit depth if auto_convert is false +state.info_png.color....: more color settings, see struct LodePNGColorMode +state.info_png....: more PNG related settings, see struct LodePNGInfo + + +12. changes +----------- + +The version number of LodePNG is the date of the change given in the format +yyyymmdd. + +Some changes aren't backwards compatible. Those are indicated with a (!) +symbol. + +*) 11 jun 2018: less restrictive check for pixel size integer overflow +*) 14 jan 2018: allow optionally ignoring a few more recoverable errors +*) 17 sep 2017: fix memory leak for some encoder input error cases +*) 27 nov 2016: grey+alpha auto color model detection bugfix +*) 18 apr 2016: Changed qsort to custom stable sort (for platforms w/o qsort). +*) 09 apr 2016: Fixed colorkey usage detection, and better file loading (within + the limits of pure C90). +*) 08 dec 2015: Made load_file function return error if file can't be opened. +*) 24 okt 2015: Bugfix with decoding to palette output. +*) 18 apr 2015: Boundary PM instead of just package-merge for faster encoding. +*) 23 aug 2014: Reduced needless memory usage of decoder. +*) 28 jun 2014: Removed fix_png setting, always support palette OOB for + simplicity. Made ColorProfile public. +*) 09 jun 2014: Faster encoder by fixing hash bug and more zeros optimization. +*) 22 dec 2013: Power of two windowsize required for optimization. +*) 15 apr 2013: Fixed bug with LAC_ALPHA and color key. +*) 25 mar 2013: Added an optional feature to ignore some PNG errors (fix_png). +*) 11 mar 2013 (!): Bugfix with custom free. Changed from "my" to "lodepng_" + prefix for the custom allocators and made it possible with a new #define to + use custom ones in your project without needing to change lodepng's code. +*) 28 jan 2013: Bugfix with color key. +*) 27 okt 2012: Tweaks in text chunk keyword length error handling. +*) 8 okt 2012 (!): Added new filter strategy (entropy) and new auto color mode. + (no palette). Better deflate tree encoding. New compression tweak settings. + Faster color conversions while decoding. Some internal cleanups. +*) 23 sep 2012: Reduced warnings in Visual Studio a little bit. +*) 1 sep 2012 (!): Removed #define's for giving custom (de)compression functions + and made it work with function pointers instead. +*) 23 jun 2012: Added more filter strategies. Made it easier to use custom alloc + and free functions and toggle #defines from compiler flags. Small fixes. +*) 6 may 2012 (!): Made plugging in custom zlib/deflate functions more flexible. +*) 22 apr 2012 (!): Made interface more consistent, renaming a lot. Removed + redundant C++ codec classes. Reduced amount of structs. Everything changed, + but it is cleaner now imho and functionality remains the same. Also fixed + several bugs and shrunk the implementation code. Made new samples. +*) 6 nov 2011 (!): By default, the encoder now automatically chooses the best + PNG color model and bit depth, based on the amount and type of colors of the + raw image. For this, autoLeaveOutAlphaChannel replaced by auto_choose_color. +*) 9 okt 2011: simpler hash chain implementation for the encoder. +*) 8 sep 2011: lz77 encoder lazy matching instead of greedy matching. +*) 23 aug 2011: tweaked the zlib compression parameters after benchmarking. + A bug with the PNG filtertype heuristic was fixed, so that it chooses much + better ones (it's quite significant). A setting to do an experimental, slow, + brute force search for PNG filter types is added. +*) 17 aug 2011 (!): changed some C zlib related function names. +*) 16 aug 2011: made the code less wide (max 120 characters per line). +*) 17 apr 2011: code cleanup. Bugfixes. Convert low to 16-bit per sample colors. +*) 21 feb 2011: fixed compiling for C90. Fixed compiling with sections disabled. +*) 11 dec 2010: encoding is made faster, based on suggestion by Peter Eastman + to optimize long sequences of zeros. +*) 13 nov 2010: added LodePNG_InfoColor_hasPaletteAlpha and + LodePNG_InfoColor_canHaveAlpha functions for convenience. +*) 7 nov 2010: added LodePNG_error_text function to get error code description. +*) 30 okt 2010: made decoding slightly faster +*) 26 okt 2010: (!) changed some C function and struct names (more consistent). + Reorganized the documentation and the declaration order in the header. +*) 08 aug 2010: only changed some comments and external samples. +*) 05 jul 2010: fixed bug thanks to warnings in the new gcc version. +*) 14 mar 2010: fixed bug where too much memory was allocated for char buffers. +*) 02 sep 2008: fixed bug where it could create empty tree that linux apps could + read by ignoring the problem but windows apps couldn't. +*) 06 jun 2008: added more error checks for out of memory cases. +*) 26 apr 2008: added a few more checks here and there to ensure more safety. +*) 06 mar 2008: crash with encoding of strings fixed +*) 02 feb 2008: support for international text chunks added (iTXt) +*) 23 jan 2008: small cleanups, and #defines to divide code in sections +*) 20 jan 2008: support for unknown chunks allowing using LodePNG for an editor. +*) 18 jan 2008: support for tIME and pHYs chunks added to encoder and decoder. +*) 17 jan 2008: ability to encode and decode compressed zTXt chunks added + Also various fixes, such as in the deflate and the padding bits code. +*) 13 jan 2008: Added ability to encode Adam7-interlaced images. Improved + filtering code of encoder. +*) 07 jan 2008: (!) changed LodePNG to use ISO C90 instead of C++. A + C++ wrapper around this provides an interface almost identical to before. + Having LodePNG be pure ISO C90 makes it more portable. The C and C++ code + are together in these files but it works both for C and C++ compilers. +*) 29 dec 2007: (!) changed most integer types to unsigned int + other tweaks +*) 30 aug 2007: bug fixed which makes this Borland C++ compatible +*) 09 aug 2007: some VS2005 warnings removed again +*) 21 jul 2007: deflate code placed in new namespace separate from zlib code +*) 08 jun 2007: fixed bug with 2- and 4-bit color, and small interlaced images +*) 04 jun 2007: improved support for Visual Studio 2005: crash with accessing + invalid std::vector element [0] fixed, and level 3 and 4 warnings removed +*) 02 jun 2007: made the encoder add a tag with version by default +*) 27 may 2007: zlib and png code separated (but still in the same file), + simple encoder/decoder functions added for more simple usage cases +*) 19 may 2007: minor fixes, some code cleaning, new error added (error 69), + moved some examples from here to lodepng_examples.cpp +*) 12 may 2007: palette decoding bug fixed +*) 24 apr 2007: changed the license from BSD to the zlib license +*) 11 mar 2007: very simple addition: ability to encode bKGD chunks. +*) 04 mar 2007: (!) tEXt chunk related fixes, and support for encoding + palettized PNG images. Plus little interface change with palette and texts. +*) 03 mar 2007: Made it encode dynamic Huffman shorter with repeat codes. + Fixed a bug where the end code of a block had length 0 in the Huffman tree. +*) 26 feb 2007: Huffman compression with dynamic trees (BTYPE 2) now implemented + and supported by the encoder, resulting in smaller PNGs at the output. +*) 27 jan 2007: Made the Adler-32 test faster so that a timewaste is gone. +*) 24 jan 2007: gave encoder an error interface. Added color conversion from any + greyscale type to 8-bit greyscale with or without alpha. +*) 21 jan 2007: (!) Totally changed the interface. It allows more color types + to convert to and is more uniform. See the manual for how it works now. +*) 07 jan 2007: Some cleanup & fixes, and a few changes over the last days: + encode/decode custom tEXt chunks, separate classes for zlib & deflate, and + at last made the decoder give errors for incorrect Adler32 or Crc. +*) 01 jan 2007: Fixed bug with encoding PNGs with less than 8 bits per channel. +*) 29 dec 2006: Added support for encoding images without alpha channel, and + cleaned out code as well as making certain parts faster. +*) 28 dec 2006: Added "Settings" to the encoder. +*) 26 dec 2006: The encoder now does LZ77 encoding and produces much smaller files now. + Removed some code duplication in the decoder. Fixed little bug in an example. +*) 09 dec 2006: (!) Placed output parameters of public functions as first parameter. + Fixed a bug of the decoder with 16-bit per color. +*) 15 okt 2006: Changed documentation structure +*) 09 okt 2006: Encoder class added. It encodes a valid PNG image from the + given image buffer, however for now it's not compressed. +*) 08 sep 2006: (!) Changed to interface with a Decoder class +*) 30 jul 2006: (!) LodePNG_InfoPng , width and height are now retrieved in different + way. Renamed decodePNG to decodePNGGeneric. +*) 29 jul 2006: (!) Changed the interface: image info is now returned as a + struct of type LodePNG::LodePNG_Info, instead of a vector, which was a bit clumsy. +*) 28 jul 2006: Cleaned the code and added new error checks. + Corrected terminology "deflate" into "inflate". +*) 23 jun 2006: Added SDL example in the documentation in the header, this + example allows easy debugging by displaying the PNG and its transparency. +*) 22 jun 2006: (!) Changed way to obtain error value. Added + loadFile function for convenience. Made decodePNG32 faster. +*) 21 jun 2006: (!) Changed type of info vector to unsigned. + Changed position of palette in info vector. Fixed an important bug that + happened on PNGs with an uncompressed block. +*) 16 jun 2006: Internally changed unsigned into unsigned where + needed, and performed some optimizations. +*) 07 jun 2006: (!) Renamed functions to decodePNG and placed them + in LodePNG namespace. Changed the order of the parameters. Rewrote the + documentation in the header. Renamed files to lodepng.cpp and lodepng.h +*) 22 apr 2006: Optimized and improved some code +*) 07 sep 2005: (!) Changed to std::vector interface +*) 12 aug 2005: Initial release (C++, decoder only) + + +13. contact information +----------------------- + +Feel free to contact me with suggestions, problems, comments, ... concerning +LodePNG. If you encounter a PNG image that doesn't work properly with this +decoder, feel free to send it and I'll use it to find and fix the problem. + +My email address is (puzzle the account and domain together with an @ symbol): +Domain: gmail dot com. +Account: lode dot vandevenne. + + +Copyright (c) 2005-2018 Lode Vandevenne +*/ diff --git a/include/menus/menu_fileoptions.h b/include/menus/menu_fileoptions.h new file mode 100644 index 0000000..8cda47e --- /dev/null +++ b/include/menus/menu_fileoptions.h @@ -0,0 +1,13 @@ +#ifndef _3D_SHELL_MENU_FILEOPTIONS_H +#define _3D_SHELL_MENU_FILEOPTIONS_H + +#include <3ds.h> + +void Menu_DisplayDeleteDialog(void); +void Menu_ControlDeleteDialog(u32 input); +void Menu_DisplayProperties(void); +void Menu_ControlProperties(u32 input); +void Menu_DisplayFileOptions(void); +void Menu_ControlFileOptions(u32 input); + +#endif \ No newline at end of file diff --git a/include/menus/menu_main.h b/include/menus/menu_main.h index e451bd7..4afa478 100644 --- a/include/menus/menu_main.h +++ b/include/menus/menu_main.h @@ -1,6 +1,8 @@ #ifndef _3D_SHELL_MENU_MAIN_H #define _3D_SHELL_MENU_MAIN_H +void Menu_DrawMenuBar(void); +void Menu_ControlMenuBar(u32 input); void Menu_Main(void); #endif \ No newline at end of file diff --git a/include/menus/menu_settings.h b/include/menus/menu_settings.h new file mode 100644 index 0000000..a47812b --- /dev/null +++ b/include/menus/menu_settings.h @@ -0,0 +1,11 @@ +#ifndef _3D_SHELL_MENU_SETTINGS_H +#define _3D_SHELL_MENU_SETTINGS_H + +#include <3ds.h> + +void Menu_DisplaySortSettings(void); +void Menu_ControlSortSettings(u32 input); +void Menu_DisplaySettings(void); +void Menu_ControlSettings(u32 input); + +#endif \ No newline at end of file diff --git a/include/touch.h b/include/touch.h index eca3239..2892b67 100644 --- a/include/touch.h +++ b/include/touch.h @@ -1,6 +1,8 @@ #ifndef _3D_SHELL_TOUCH_H #define _3D_SHELL_TOUCH_H +#include <3ds.h> + #define TouchInRect(x1, y1, x2, y2) ((Touch_GetX() >= (x1) && Touch_GetX() <= (x2)) && (Touch_GetY() >= (y1) && Touch_GetY() <= (y2))) u16 Touch_GetX(void); diff --git a/res/drawable/btn_material_light_radio_off_normal.png b/res/drawable/btn_material_light_radio_off_normal.png index 97ad84ba56b1cab12d205c654b94fe939fd63b34..2bc4cac63fe3c224f8c2662fafdb5bb47f3a9b8f 100644 GIT binary patch literal 535 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX3?zBp#Z3TG>jHd2T!FNeSLkPxVpMJkP9>o2!J9$60QtxKC&%9Hc$=3K-tY( z-T_@~RTAVE%)rR6X2PzX)HZp>q*gWt!N`s&)q%-1!D-VQY#7;ursYZ(MKUQe+6b{R zvZn~uGAT6%D^>&vG77POSZ%x=Xv#fL7srr_Iaep$_UfDNyPD+|pxxlaP@WU>bIo9o+QaV>^S?W!`tJ0!3Q*`xWw3c2M+jDr9 z>e*d&0-M}Kj_x(^*tI5P=hN?(nJzWjhrE7m>WQHp`mhD!PrhVJ1wr#mcyiRU}jP+Z&E33CMyZ`>Mu|{_`liBULyTnA^ zPd>P?;jN+NRHxI*mdB@i|Fyaqs#MAL%5!H7cik$Mt_yGPT0Z$~-=|rvS^w@X literal 824 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=3?wxlRx|^tKLI`=u0UE_Tiejk(Ae162nNu& zFkvHoeSJ6^$S^Q40E$4g1GOMaA+wQ91G0g-AS6%?#8M!MtOlq6XePu2p!3w#)qx6t zg2*m3F)@K~_4M>0=IH9`0u2GOfg}(B#erHNhUn<%Km;Kc0og!tpaL!5c|Jf3LP~=C zf*F|kG#mq?WrGV_`=-upVwVq_HcO6~&$DL6q=xKBzv(qr%n1{-%%%9*7<($Um@;HF zm@2CnnX~yUCf8>PkunVtXc?%mz$JjK^I zPba13mbD+wOI|zezTlk8M|ZRKyBAc)`n`MpzV=^3N4^mALGENFkv^Bk2%CvhmZUWG zGp;OAOL0kgDx>g(`)E*P;3?I06T}<3=9sQfd_6bh>aBY#SBCEOpBZ&r{G4J~zRaxN zT^%tu+#UZ2c6?n{mXURKT1Ux*CYD7j%(f(l2jBK!J;9>x6u?>ME~+5(e)(&bPo|f= z*0NjdZfV`ey^(3>zo!iA><*?^#)zKuFyx!3`oqk8op;KUXJUEM2e)%e+?zPDNT_M< z18bWJB1VfmK3M!?H@rBb^X=xi4^zxfeEcwp@8*%vSrQV$$tMkzYk#~ye*9g9(NaEp zz9cizCv+L{h}X-yYs^A={yDkzoA7FEKG|MN{`a zimR&9(Cu~dT>p7j_r9|MliH-~J_y#bnQp6^-P^}JZCC%^e^QU5OMlO|@3#AR?!1-g ZUgmkqMvp&PX@T+xgQu&X%Q~loCIEvU7vKN@ diff --git a/res/drawable/btn_material_light_radio_off_normal_dark.png b/res/drawable/btn_material_light_radio_off_normal_dark.png index 72ab5b1e93676c4d4848806d5169673e46f01b1a..3a0a28c2c6cc5ea7638f20dbdee943b2b8d903b9 100644 GIT binary patch literal 447 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX3?zBp#Z3TG)d4;su0Z<#0D-ejZXbZwYnBB0 z1v4=6tC_H?C$&wUF{zb}K`^poN_AjzO>o-u1{c*AtKdpLO zet}=KocDZ$#@!B~i0}M+kK71Tm%F0yY2(J8WpcU z^JWLHh~91SM&R_G85<)^@^>Y^R-Ezopr0B+O%r2qf` literal 683 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=3?wxlRx|^tbpbvhu0Z<#&;hz#U$lTOwkiqo z3ua*E({K!omJKd!?VCEYiCsQy+AKNGni-QCvLpSbTO~})GMD0KW9+HaV#=tpm|QQA z8xgKut=?_s_1Z9?pMim~$kW9!q+-t7sgcD^4gxJZrzZz46cr3~VPWmBnEmI!z1W3S zDzjLXr%qoo=eXXks`T<_&5!4O?e7+M@eC7*6k`3C&bKIQm!t1wDPCXI@B8z&=}5<) z+IFjaQcK@u);sgAfB(C0`R)S;B)O)>vM4vSJcZqHhtl@(uV;F=y{EX3eZ`t+VUr!nW&es;drLDH^-b@{?X0@s+NLMTp`LTPirG2o zh0#&7wCG26E6!XH6|Ot9O~uaVX}OLmU)bW>jf{I!*gN~!UQf!lN$qW1X*f-0dout3 ze*sp1ri)tNTX*B5fkwPxorP}L|5vt?|1Rvlyg@`FJw|6;;l%Wi2alM&)?eJ!aA}s# zUKy|DS{hF+^Tog9Zriv2?u*sGUuZMzt`^wfYTNa(*q#5s&b+VYuVs7psj=-(Ruuxq OA%mx@pUXO@geCx&*@K?| diff --git a/res/drawable/btn_material_light_radio_on_normal.png b/res/drawable/btn_material_light_radio_on_normal.png index c98957d001a946d412cad0d65f491a0233cf2594..a2893d5db3dd2bd8a028b55ea010f8a251923ac4 100644 GIT binary patch literal 625 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX3?zBp#Z3TG=L38~T!FNeSLjo?LY&8Ty=GIG-W_RpcF(FNCI^MNw^xg8{kGk z)B{PN6pLN27SKzsB|(0{42=A0ChY1-ZIfqAYGq>(jO>_F9hh7boHo6|hLKHZTCQ|a zBom{Ug}JCAqhj5xS{rdOAxkz!_7qVeCZ)#UiXcHoA$Hq%{bU9PMjlTW$B>FSS0_d1 zH#rEjx;u!86|jnFn`T~<}w>`f5o;*1-hw+E_tHM1B zA7b7sz3KLtWEd8rk|k@Q;C$CWaOt%Z7uKIx(0cMlS{K6MUl1IsdlGX)DNb zfBtXLyOzyA(&SuED$ToVwd=$s5lOuxk$v4v(<6DjnV0fBQ<;1qHZJ5;ctO6GOmn4V z=7$D1}l{M7Tcu lylWlu)BpUGxR!dqc=b1m>5U;Vhkyai;OXk;vd$@?2>_h=&?x`_ literal 1007 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=3?wxlRx~p(Fd7B;gt!8^+S=NNhK9z*#zruJ z#)SzR>Fevm*+7PYfdNniq8+FOSqhnrY#NXa)CD1dY9N*ZNn|xZ1wb=_vZkh{5Ob`p zt$_@n8X$n^0ynGivs zN}xudG9b{=(SZm;oCi?`R8Y?Py%lJET1k*!FatB6hGSr~Y;a*~-_)5+?DApLX2~)0 zdDhIB)Q}zNH@(J+IbmX!xfDMeV^5_PQ--VtQ>B1L5LXqKeOD_Z^Mq_Zi^=tZU0GTJ zxe;u^+-=+r+~KlJ+ST&v%vM_6d}ab{7GAmRC$>83F)%Q(dAc};RLprhH9Pp0gGk$A zMkYnZldF{^nxxiEWQbcJ#Oe}Vy)axPFgkzj-~aUsci(WhbaP3`viUOSr{CQ(IsM!` z+k8KZcb4-C&Yb<+n4lrT^Wk&xW`MEv;u`(tYhleRmfQ`%jNH8 zs?R&47`uA+(JEg@J<0D5VqpueY}`nf(>Gx;st+8X9`0dAIb@ieT zGd-F*BwkNaU;D?XSX;eK(t_9cK@E@b)~5%oHs*SA=0_LZ(KP&jbHcm2+4HX*OwT|aG4JmufN7Ay)78&qol`;+0P(D9*#H0l diff --git a/res/drawable/btn_material_light_radio_on_normal_dark.png b/res/drawable/btn_material_light_radio_on_normal_dark.png index d025a387caf74864173a1e784aff0af68b047f5c..741ec5d551abc0727b2cfcec393f4afd72d648e4 100644 GIT binary patch literal 519 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX3?zBp#Z3TGlLLH0T!HleK?W{btHuGHWmppA z7tFxOuV%uop42vZ#-vs@2EoXVDb<0=HNk1q8*GH8&>v+EIn^0V@ulbtUYTlBZsFD|_j?7M7wvz42*Bpr(Hx@=LzjChsug`&O`#V?V z52n~{GvL%{H~-dole0ojfZN(fal&C0P7lN9-)FQ~wm(a}*HF!uTP%~yvn6=TMpk3D zvMuV}y61{^o;%M}6a9Ag%Jv_h;_iwqtqpx7RT~>>BsxpGRPOSVZCYPWTHpEn|BFw( apZsx0NzocPR%M{?89ZJ6T-G@yGywoc$tb)4 delta 627 zcmV-(0*w8K1gQoriBL{Q4GJ0x0000DNk~Le0000o0000o2m=5B04d>kg8%>k$xuvG zMF0Q*kui&rKs$eC?teG{002XDQchC<0}d!bQCl5TafObRnSuu&R+gF`Nqm@+fNNP! zmpWjPYBw4W1_6wDDgtN_QVM!MijX*ye-Uk13x+^fD|;u4HcACG>CpfH0nAB6K~#90 z?N!-wfNRs~nkNXHBDRw9T-jftA5IsX5nvZJMd1s;D@c}XhsGSt*|&DGOL(!c#- zOUNvlN~cpvCMT{#C~AghHC@-Txs0k@exJW97zmhAxWAA|E|$tbrE1nJt7h^FP%ky) zvt2Y>fY$DGdwtvP_moas10I@Z^2kk5$-r^_i0ceQoz2)h`CI8Qt_i47QTjOw`aA$8%TsmuYbOQ%{7tRd5yo zNoLXR3Ge7Zh)z$v1+vKqNJ_)9FT9G7jYOzlgO-1afTVR;w(w>cV$#ek=;@z<910s< zU)Z62c76LS2TnU)y#^tCB|-{EE^5ffRlZ%l$|GS`LBddySi<gV^}Ukjx%y`6s6CuN225@ zPo_H%rH^x>B=UUirEjsctmT5wFH0W@?D|H5CA(F|cOBjQv-r2)Y2R%$JYScb|6Kq8 N002ovPDHLkV1kkD6x09! diff --git a/res/drawable/btn_material_light_toggle_off_normal.png b/res/drawable/btn_material_light_toggle_off_normal.png index a3c43d513b6a0d60238ec8955b7428f3258edbdd..e59e6cf65226ec5ad792502803a46b9426369ec2 100644 GIT binary patch literal 15590 zcmeI3Yj6|S6~`A!2$+Y-;F2_TQ&lDtwRE++dTWtIZOdR2WP~x)rVzZ^U0GWst*n)> zOjBSmP2&cVnK&Ul0*TGTA>mOV9w2tG8G=0>L%}5^6DMG3$3rQ2GSE&3S~~7s$(H16 z`;KRlPQR>X^f>o_&pCI0_nfQ!vbCU62{#1OACYE3vx=`AX=RZAJI7MFF;qcNR;!gDb%aib zgBiF~9SAc~JRoJogAB%T@Ddvmf?+`nsN}eeTa1LW)oM9W;x%wx{$L_eKpJ2NC=yX7 zNKjgm7|Dra6F$L6$QN50#}d4c_w#|U1pKHG{eoUGEJ|K+l#~g1)Wl#EDV0kp$WmajN3@Zd=u0OUBuv2o!lks>56Xxx} z*MgH~oV1isX4**7dcz#KG|7}8@tIHzr^pGO>R~Y~w9!ga#?%-oGvOGZgE%J4j44X$ z+>|&ROMAqSp9$LqKU2XI!9azLNDijVOw<*fC;G$?SQc;B+lY~dQ@moeMi&O8FcV<; zLWdm;Xa#|z4HTs}@+5;>tt5#XIF7`vI;#;k8?7GJZQ}HL9T$V>bf&rw3+)iuh}7ckE{o;NAeVv1S-nYzQsUJj3cK9d6)R4{o45dfZA`INW6LxXq-2(OE3I!4xBe8J4O*kU+gwr_?d1 z=GfV`l&>5*Iw<&r*claMLJ}``V77X&1rvu_;>?nLVx3GgtbDrJS-C-YfP2Vja#;z_ z0XJdzc8(1}JbsG%=sBvqd;ry6@ldf@qX&^hPq>N+@i`TsLdMqn@wh`B;RH59{y*>u z|AVcNn6=+*BgWQO-((}8GwT1j5y?tsy-c8j=j_B_5f2WGZSUecI<&h6J9cvQ1a)1YpM*pmZ4o!W!N?8xhx=r?cf}W zEfg9~@sj^X<462baLdQrO-7^MXd~cZ*uy16d0(kR0B5lzpD76+SQycJf;6n3(R0S6V1Z^s~ z(0m|O!36<9n+h&8A4pYjK|s)^f(y+DQWabf5VWb_Li2%C1s4PaZ7R6Xd>~c91pz^u z3NADsNL6q_K+vXw3(W^o6SyTSt zB6!48#TFDhF>Kwl7`Cwn!yfj7@7ov_Au;T4^EG{!5AF)qxaiv5o3)XHLDyeb23rogy!AD%`c*9#zyIc} zeRVaOgQq5Ka(!4|*VNwb(!Twq^P^AHKdN1l`D#}B^%>FC4|;!9-QSl>Foe-#PVITl&Uhg+Ig=_FJBxczpV?&kp>owWmMh zmGgVbJ}&>R?b4LY4(Fz`@8(YG+Ty=z=qp}!Rmz!hrfJ3DOK&dh{cVG0CiYglX~)@U z(c;DLcyv4TjVI?mHI>Yqp0niB_U`lUdyOv~ZPmFXde7$y=Bz`@6I!;H5m*4wLzHt7ollI zNAuM2oyT3vu2%lqlDYnkqQ#P!lnl=i>ORDV4vt`3C<5 zwWUGRdjH|0=U1KeH{Nde{LaTc6X(yE@Zd%Jf~L;iPq{Jx2pGurd=Cq&fYkEU+%Xp+FzXB-S^Kg11JA|?8@zyPS1(-alilQVc*3= zhr79^&PVfFFYRvsRCBHA%5N%}mD}%3|GS6<%tf>3ROLqGCy@*DiyTLC%QyWOPmo)h literal 1568 zcmV+*2H*LKP)RJ*&gSn+W zobQ}_hY$)13JMAe3JMAe3JMAe3JQvsiZ^szw~YF8YZ^Gg^}r240dNb@9_XMuZU(-k zyS|>RF+uci{7u45ROI91v%9&u`OxXprxUxoyAwM*JLR>twK8XC=Us4pE6`T=|1}{7 zPJR<`OMHC%$CZ_p5jSq!Xw_&mBU-I?abaQMzK(zV4#4wxy|lD6?AWnm62e6Y-me$K zRK7Iez%lH1G&D3Ex^w4F-{RupgM0VxF)t$g@2~0U>5-b68adqm0q`B54dK!Sx#vA^!I-eU^!=JXEm2DU8;|Yijw*J`}+k2 z1xXJcJm?u79_|qq7A9VS9(WBXf&WS1C=Lk;*&7fL;07t^B9%&aiA17pg!3aIdFDU7 z163A8L`1l%)#^r=%u{10Z%BngF%MBOR$gANs;{sArLnP5+0@jeFeIGEqf*Ba-MJx% z#4D#xo$3eQ`LVFD@I+c#+82?Lk-pZ}*6$K7fGGi<8g6u!S?p{+jQgRMFSLg3B9^z`)ockkXE<5K`Vp@6W1 zhtOs^Iy!g}0tw~%W>Qj8yg(qp^e`(T*eHNd`x>I)PfLh^yng*UD*|89pi7TO;5ip$ zGT8wuD=SBshn1-!pn~r~C786707!T~8wFr`W_yDntE#Gcf`fy-2v<4Q87lyr3RziM z2VkI2EGYtd!BE298@&03=i#roX?R0rO?r z+uL~+0n?(mxcGbMNA?n~;WEk_Py|OPi^Y}}0eSiIWv05i8r_{&5r9uBDk{38qoel| zE+vc-0gdGC(b3WB)evEDaL{5RfG?br%jE|M*QT2!Le;At39!gJ3k}q#rig&)V1M=M z)x6aZ0oD@?lr-K&08e;aR#sMz>kwjsCtx|Tf$l2`dW46T6acHk`Sa)5&XI3pgx+H| zBO~J*CnqOtqc<2MfF9U#I*pHyU$LYJQ&UrH1Yn=QbBSbA!JIw;!TJJ(DyD?lE60B$ zt{-5qympKz0EntSa^%S8LZMJZxE{fz)){ZI&CAOR)M~X0mJk4YgucE$rmn7z*C+r% zXL53KzO}Qnb0XX(j9H5)%%iQ)%w)iTdF>p*YnWTNZZQoF4cOW7GI{WnMG*Ao)YR12 z9RR}9a+)>B#Kc68$;rtIbOX!QS59{(gRbp8&#5w029NG!C+ zxLgR^%*;$Ls2Eu@Gcz}h=pmls7JPGa^Ork!?sO*H+rqM?-#K2sdu$9h=*G1wyUlv6 z*A3n=J3HGOnw(THx3lx}^S|Nu1{4Li;c$x`cpuKsOiWB%?CI%ADJUrTjBsD4R{HMn z$^li!y32|Y7#QdZu% diff --git a/res/drawable/btn_material_light_toggle_off_normalx.png b/res/drawable/btn_material_light_toggle_off_normalx.png new file mode 100644 index 0000000000000000000000000000000000000000..18b8202fe24345adb0ebd1fe927af02be213a0e7 GIT binary patch literal 15698 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 literal 1694 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 diff --git a/res/drawable/btn_material_light_toggle_on_normal_darkx.png b/res/drawable/btn_material_light_toggle_on_normal_darkx.png new file mode 100644 index 0000000000000000000000000000000000000000..5067cc644a485a23a7179e1190e552723b40b04b GIT binary patch literal 15698 zcmeI3Yj6|S6~`A02D`jpc3j42LGFGIqV$>GdT6QimE_A^ zDj>*DCd%(2$ZI=O5ybqED=8Dp90erJdo&coJ6TQ8rAh+2Xo|C3M{i+RUiuSd%S5QVQTTEfos?1TS%HjR2O0O}XG@&!8jAjj?H=DGXIGVZd-RMr5Km zLE>f-Csf@pwKZA#W$`mEvdA{SJDp@@sVha%M`BnY5YPm48s1-l5oWU)!?l=Ji%K(4 zq1G!>LDVZuj|Az7V`Bx{&-p}-_p0Q$l#{O(v(#!iQS{ZlE{`vo$SZWSlPF?A%7+me z9P7`Cp`$*&YQH;tX$+0AZq~zkMM3f-`t|cw@**!(@&lxd$^#~r_Qc_c=I-};J)VA3 z3!=S7LeP!0Ur(W=*2iK+tiV_MY1UpNEoFLts~0&}Y;$@=l9d6koegpWxXDVyEnEtb z9kNJkC3E~NCG!3fo_AX#RaV%8P?eJ-SCvsU%R$kcSLT{w!9b_jj>qD$HcDizlCKHH zjVNv^A&jIRC$+}ea%tjmK_WAO7!ID{T($kh$Rzc>V#Lh^V@MsuP$D&`DDiXS;xIJn z;{6^8?7^M2{FSgX#0^*0>n6|OZh&nt+OmuB;9R%t-P zaSX{aE}dD&QmD~vHlUd}O`&F|!HnVtigvjSPNR#V!w?;gc=tY`Z9H8q_lp!daExr_adYIg zFj6u-rF9YnjiZE1I#V+BE()bwgj5v-W74yf6W1A|Yw0IUpH#V=Ak}MaTpdf*96sBM z*{a^7J%XDHpHV)_FR*e4W~qBxFnXv(&n(#|+{q+G%cq-_mK%g+ELgA6*kwgMyWP0H z+c`J{k@#`$1Lp`-vR+twMM8yV4ID(^U1EUpv)L6=g$%CuBXN5>f(a~`{D0u){s&tj zP&GenBL>%3KX4=B?BM#L(auB5ybEMvubinwQBaC;Zo(cazFb13xfMvMFxMM|A4 zRTUNPAi{$68H;6(%rBEW(p?>%Z`9(_3nS#uZr6AjbX^K}jh8{!m}jY%6Rq0rY`}1w zm;65(IpT|@TRzrm(Cc-23kD8@9)(ss^9{Ez)b}goDZNXxBwtlv00`VvaKZTis)7pu0yh<0a6W*l z-~xcaO$8U651=Zz03dKv!3F07s0uCs2;5X~!TA8Hf(rlwHx*oPK7gv=0)W6x1s9wT zpenckAaGN`1?K~(3N8Q$+*EME`2ebd3jhK)6ss^9{Ez)dKwd*pueW>xO}gfpXr;EkJKt=j#{;~ATY z{Zl@9Ytw$Dcy-39e{aL2H!OQ;_|Qp+>CT&aJ-cD-x2E4> zixcNBE3m%!Wc;bAi2kRO@`~;hu1}adk>48pXxEIcXJ@x>Idbj>x@+gGhPC5{AVcUh z-KZIlUhF*CRri=h<_x*_SJQ0fZV zSBW);4u;;79@__^oP6KS~4Eo@Ey2c zUw1x)*;-BS~Ku6)24A3U*GreGP0*+y_otE|6S5b;np^sJUer)_UC7idj|_= zmD`22cl7yZe>ZdAxetyw+Wz^)vtzG3`5&NNMLKpKTlr1v)iKSX&V^fwQnv5= zsC@nSjTb(A^nLK-Kt`sC-1^$=#pT;|hhLbCI!3?VeB#%>hHIy9{cGch3pX_%W?X(X z^k~89pZzMdtK#eWPoFA!chQvcWPJFJuB5xykbOm}l>6Uao-IH1oM&HXJ20<&?SBD+ C8+^9_ literal 0 HcmV?d00001 diff --git a/res/drawable/ic_material_dialog.png b/res/drawable/ic_material_dialog.png index 0c6df96deeec2b8c6e6415332f150ed02174b5b7..afb9ae3421e299f242a2022133396c13a047ccc1 100644 GIT binary patch literal 553 zcmeAS@N?(olHy`uVBq!ia0y~yV3Yu|(>R!cq&k1yZXi_@;1l8sq}?&Vj2SbYK7INP z48DH-3S_|Ni~EbLS3_U0PZSQ~=}x z*@1z9TefVubm`LN%a?&%AUNV=AP=-vtt7}Vn1O*+KwN^|arKpJmshblKmISc{;Jr8 zi@cNnPG(l${hLWTs-=yQlV{)YwLlLzc)B=-RLpsM;dB|JqXfgl`WK3AiXFdWEB=S4 zSe^`g{m!-4KVZ4isyO?ruRQ+7i3akv#V>Y~eSESYz}0$g?d$nBxAgv0DtGM^`|NT0 zj-YDPvK6y|R94pUnkNzuh8+AL_j&Uv*}z4mU1!Ut?_OlH`>z_`@ynLw?wRM7KUREH swD)fP)x!O;`X9e#%>T8x)@2WaNhxb>Uv0^MVCXY=y85}Sb4q9e0QSHJ>i_@% literal 20552 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 V^j!b9+!GkK44$rjF6*2UngDsRxRC$= literal 5594 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 literal 268 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQa-T^)#u0Yz@*x1O($iTn=NFp;J zTwJ7~p&^>ov^L)tK;tA!g8YIR8048udl|WWyJy*RL`<8)7Cea~WTstX_0!3>{s9$* zd%8G=RLt3X=C+W7f=KJb9MxRc=T-mJuSy8MJM$sFZgZO8r$kwY^6m4C+}c-v3{>dq z+^%-DXQ}JS%Ud??NbOzstmeSp2X_s7MUy{A8Za(lPjmVd{b!-*i6WL8Pa0I~fmSeh My85}Sb4q9e07mmxSpWb4 diff --git a/res/drawable/ic_material_light_home_dark.png b/res/drawable/ic_material_light_home_dark.png index 05b8dfdc605f7fa39aede7b58104e7c5d7d92af0..6d44947e86736b2a613e44cc897ffdc64619376d 100644 GIT binary patch literal 289 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~%3?x6Bmj(i5?G7 zU_a-wO_|?yw@+9zwOPfziNP&s> z$2Qmb3=6(YdUMRGqUhS4O}{31e8{fSoA`$P`j_1~8=tS-D=RI;x{pC5(hq1KgQu&X J%Q~loCIDrCo7?~Z literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQa9sxcfu0Z-fHNf+ViVZ-sBuaw( zf*Ba(nM`{bxqQ24*>glpo5B`6DP*QyWA&vIrGh{;VV*9IAr*7>p7G>sP!MprxQun8 zn26;2`ryt$$GbCM#P18?dT>}mX?OJT)(3yDK3ULlMI-uh+M-K2C8oPAZWVRgmfKy3 r`%o&)-*YzoV9N}-8H}Hz|11<;XT-8aTSa3N&;|xiS3j3^P6dPZ$qBpW8O49DbE1>RUC6nmE;X zkD*@Kb2ml#uFK6WdP{gYFRF37AgK{xPsI~(HbwcXQwc#_2Xt!$w8=`qcIEg zSO$OjlTws#3N6j1gI2)SuImY&VY<4y>|IrMHQB~+KA(?a9gM?4!x^;Jt>{9ER!n!_&3c>x?dg;7N@&o#x7L_Vq}To*T&NReu*z7(mbVwCDjDCTkGe?W0iZFcPvb>&1lmjeBi&bux z<~UD;_D08Rv%(@vKQ z(1O>)(h<%Tae5*ifpd6rDT;&{ma1OXV7+#m>KImY`fOVX)(;)c2{Ac+MkR!#28<32 z*m5nHKh*MPmf@4`WL^-B(=8|(4FV)TGi0=IS$WT_n>>6wM~5I2-{d}Wj;?l~;Myw_ zDm`oDAetK0yM!dDX@eCqy57&k9qNcC@H58$K}`M+wn7s+Z*3z+*H^c=5#}@M|J;Z| zC5!EX(gvg;lPluf!07fav!g@1E7!5ZyU}m_Me(qcVO0s~4&waGu(3ks%={LkBV!%u z`5p%gUkqn_W?juP>e`BTHOr`L!E==&>p@308!~M2GX6(1NBl~7%LjsPm#fO~%LGvfs z?fg+#(d~?i#3D%$Ab6AD!t+5?f(rqHHwi90A4Da%5FmJy;KK7kRDufuf;R~+JRd|Q zxDX(Cli_{cQ1U9KY|`YbB@uZo2o_pZz~|T=M#^RBW}(IkM@#ed8*}Znv z*A491e`mw`-CI|ER(fJGX4yrmy;%yK2TBxp>z7 wnC-fKP+t3O^`!;BD_5hQt55zoSZ$$pO>2F3n`cLj@ziZY-HOl$OV)1w7h}P?K>z>% literal 351 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQa)d4;su0Z<#0D-ejZXbZwYnBB0 z1v6a!_x~!>`$Ih^zOyE)x+!!YT=jUK&4lL;XHLqSJT=u=`fZs?|KEyV*Prn{lLBhW z@^oRiR0EfOmyPmi_tXkqZ#^4LUQ;{206d0dmcr{{)0 f^L_8PvEP~@D1nVl+D9N3Xe)!KtDnm{r-UW|z2eZ? diff --git a/res/drawable/ic_material_options_dialog.png b/res/drawable/ic_material_options_dialog.png index a573a8179ee296fc16d1da1f32c26094984f3b45..7d77d090c73591bdd717052938930e1a10f4396b 100644 GIT binary patch literal 963 zcmeAS@N?(olHy`uVBq!ia0vp^mx1^M2Q!d#J)yE0Nc|7+32_C|?s&oO-MjDHxpN;3 zu3x`CZ{9qh^5e&kfBW|B&!0bk|Ni~;>(~4D@4LIZf#Q4i?0NnAb$NODzJ2?ET%ekx zVDRI|kI$b!fB*jdeZ7cPj>9s@$=`;uV24@`0(NP@81_MUVQuZ z?XzdkcJADH`SN9;$uD2Ngt!4{!l6Tl{{8z0H0QyC2mk;72O4|q*s+HX9|GM26ah*B zMGhW3xPJY5pn*Wc3kwUEELj4y6e#}m=~G`{U!bcxIyz3AI04jHP*8CARIMj42trGO z{DK)6+4%$ocvu*i_=Edq&S)^>$bS6$|F_eoY$3aq0$5+f!~(4Zf|>tcfLS0KjDg%2 zAjXWCj$aSGZLZE0w>k23o+PtMXpHzNx!-OK3`}yKE{-7;bKc(W3=$3$aC>O&ae~G1 zs6=T@0rx@GcfX}v4#pN%{!cGFq+H@(`eXOG?@V`>=V_I$&hyn=8fCR@j!XX1`03XY zzW%sXC8X{*@79W2!LR1Z&67+m{C4N(GCgncw(htk!Q%S7r(Q+P3!Wt%cTeZ^>+XAt zqQlpj{hYP#;*th6UX#oPp3J^d8J8R;v&}NOu%r>2(EQitNzYu&dbcM%yYR$$vaGM2 z$<2%S8L=ssQ>@eY@=c}qX6N1WdirhdlIJd$C%R8Q|0VB?%sWBCCV*{eHM`qAQ}|{{ zy_9kLb}8O=Uw*k|pI#JRDqi&R#Nw0b)iTEIyC?GdmYMv#km5cWKVwhe@034&xh9>G zlfV6adGd0|Jlp))aX!J7Keb-w_p5F0`Ttb&y`afVsLwX~u6OPM`gqSw^S!m=NhX~$ zjc<2mcRAq+Cs=4c^WFO$6waIPE?ZOcW5)|qtF7Db>;LtiFTU4p`M#R9UVA^j{oNla zR&8G|J0)n_l;2_OIf literal 959 zcmeAS@N?(olHy`uVBq!ia0vp^AAtA?2Q!eIcqUR8NPQ0Q32_C|?l^##m)FdhGxzM- zvkwfmY}ry=Tntg$+uQr>*|Q%%e*F3K=kMRYzkmP!^5x6fvuC5DqhG#!3FIC)aA5ZA z*+8jx@7@8m07W1gU%h(u{P}aB#+^HNe*5+fXeLm>=g*(tzkk1R<3^xqKx1FLcmY(u zdGqGCZ{P0Tz5Dz3@BjY&d;R+L{{8#^|Njp(`QX8WKoOvUJv}`@D}ZJK*|%@s1_}aA zUb%8*YHI4GOP4-<`UKPq0YEit*RJ*S^aQHhGUKuw(A$9}L4Lsu42-OtTpY}7lCD_= z`SE)EOxD}p|NZ~-L8J7MA4P8eE=&AURt4Hx0E8eCiJ71NTi*27KA~xUp8LpDoqPP` z;U-UB!CMAmpBWgKv^-rLLn`LHy%ioNk|^T(aQ~{^jx1Bn(p??SL>eZYEBv>3Vw%A3 zb*2CQ`aZa`ca_|$g;CjuEoPqk19aE?K+t;7uOC^a88q z%aU`>Jt$#YZfr64;7dG0Jva3tb$Ry1ZOSq@p3rtsqFItHnI}!6$8biX!7-cyWnN)_ zpDNx{`f==RpH5T$-FF_-*Kav^lIPeR&B(8pCnXykPUtzv(`?9Q%yUfQjDZAhfxE2t zbtAt{Z?A}DyMG&KjQ@=XDQwB_rmqoWv)73H8a)fQRt&@Wy~DVh<)^Os)$hK?^}}Oz z*+>@qyDz~`>qu8!U6)yi@R{5EgS$oNO<(g)uPJ`dn-08TgcOohJoEOgvTNERzvs)Y zUmO=^@7<`ozc##6XF&UvGc+&zJr2_4oPv3*SmFm!2Ih qd*ipc<%_%ZCmvh8KTt8@w>(Fa#*yca(_RCUB!j1`pUXO@geCw75Byrkv&-0w-JO2lQW1ka`eV*qSu=m3xV~;)knwsu={(tEA)CxcFak^#l ziU0qO(dPA+_5Tr;gqZZ7#hA7e@yN2S>**f9E@V;<_7b`)~WX?@ZhMeOFCd zb?(fPD@Dt;PU_wK{MIe&An)|>a{qk2bKhfM?4O}m%F7dX?%33Y4_99gdi{0cCU(s$ zFK+aPywt4jO6tA8W|>$1&0vx2KViLUj3${2Jehr^GA=nxW}9VlVM(JJFIJ(BoL#l* z&*ofM);Xg%EeQNTv-Vk8uZCV-EZ>fGe_UzrNFg5?#%a*BcGAe#wSM_1T8-MSO zY4Wyy{`02Ux^7IHZ+m{jOIBaL!2A9aeFOI^6EGNVxn2poK*@Zm-BP?~mrY;)Y}2P- zzVohkF8eRH zU)t5I)oyo-nla4R^G}oq>gd+GZVQrj!JSB({G_#ub#lUrN1{8{?O zkn3v1`enUm*QfReu2}na?w8#1{QVPq-$&c?@m)S2?|wY{X2gBFAGMSIZQ1^J?X{x7 fyOuUDf3R=StnCQCH*YpD*)n*#`njxgN@xNArLV#g literal 1036 zcmeAS@N?(olHy`uVBq!ia0vp^AAtA?2Q!eIcqUSpfq_vuz$e5t1q_mtljGy#qoSfB zA|fIqBV%G>5)%_4Qb|cko}QkTmX_w`=5}^=0RaI(l|c1CSs)420u(ebFwlbmV`F14 zFE5}PpaKsM4}E=oph#$FD9{`$D=VNXplJyS3GVLhKvG>@T}@3*M@Prh)Ko)518B8{ zg#}O-PzuNfS_(7@=n|l-fDE9ZuC6Z789)&;Gc%w$KsL}EZEbBJ1Ly``Wp%`sZ=f7FgeRbh*3Id+S5P(zn!eG;;wpsI8k-k_e%3R ztN;K1Z}=a$qSV-FVYZTemi+&P$}39O*Z$xBzbSOZry9$`2fsEchEypLcYuAHf)zxBb^YtUICiETTX*Ogt<~b&D#y}$3;BZ0@PJtMW$gA4? zAFnm*$Gtic_2JLcSF!4`ks0CZL-`$d)h)jJM(fA5+~&M{_K7D{XS*eh>T$V)#Y=ySmZf3PX;>g?=+zE_~ejXb1doR;vfB9VSoQ? z{Clh}8);-Qhu9#k;;H+!ri$aj=cj?yA3ueDm6&lozfJq?+uh&g?do0~dafV8JpA^X y?eFdD^hy&ef4nZw&MkX(eh2^JsaL;!mw&SB#-FH)EPh~mW$<+Mb6Mw<&;$U;3Z`uU diff --git a/res/drawable/ic_material_properties_dialog.png b/res/drawable/ic_material_properties_dialog.png index 57d3c8c4266cb7c836a110e71da44eeb1afbfb98..65c8dc525b1cb64d014f0c2a32048722ea1fc035 100644 GIT binary patch literal 748 zcmeAS@N?(olHy`uVBq!ia0vp^mx1^M2Q!d#J)yE0NUaO-32_C|?s&nICr=(ddUPKQ zo<4ng?%X+`@^|mv{r>&?@87>ae*Cz0?HW)7$T)H0#P{#tPn|ji0hYyb*KmPpr^PfL|Ubt}K?AfzGH6K2FShj50 z_3PJxf!8{jpiknd;o{`?m|MYCXEt>C3d= zr8A`0tXa2KJSn+2d-tsPmD@L4>m0vV7u?=@b<(7^!_9{sBO+f{x$f@k^FMT{>bmx= z(yD7yu9xHqH~R9J8QwUOAlythMnKAC*G6B}dneUe*7z>AIe%lyiDemPs+%-t`F7@X zr3EX^UUU=Rvi$|H}%+7N53xAM@8V_I`?? z+_TE`qS@;;#bFBID$yAa*RL$U{5 z?`2s1?+1@!$k~V2zvoIWi+_u=>`Auv*bSs z?N6Bb@X3?M*JA|*ujUnS0QEiaba4!+nDh2dc95{6gxkY;^E`KGwRB%p;W`z{D0=7r zJ)ejpw?mho-`x1#_GW5_C@u3L|r$s&a+j@+-xx8Y=TwuOP*!MGtLoaSUk&~ zyqfK~#GJ%}o`aHX$BAK-h~McrSi*K$(kIF@LrFZol=(<7m^Y_3MCXg~8L+&t;ucLK6U&cz`JY diff --git a/res/drawable/ic_material_properties_dialog_dark.png b/res/drawable/ic_material_properties_dialog_dark.png index c01894028af41e0717907ce214a2b4c7f2e5d16f..3a3541892451068a9609f0508e2eac109c175cf5 100644 GIT binary patch literal 899 zcmeAS@N?(olHy`uVBq!ia0vp^mx1^M2Q!d#J)yFhfq_vlz$e5tB_$;}IXNLAAtE9o zC@3g6I5;XQDk&)mCto#0-$LSE|3A_0`&ql0!c$dLt|rOcXxN7)j+2L)mU3w0|kL< zoSdA1b^(0{1VHzPg@rjfIszRG^bQcj#>T$qOzsATQFKX=UoaD|W>8Xsn>0IvdCKym z#|};kQf1ZL`St(bw-bHER(yTZVD@>Q(|qSd$AG;bCK-F|>DSbB_tXkM@Nv3j@`?Xn z7p0t9{BNP(ypOjkjg{h-y#3PQBFL!~(kI2tXtTWT*Ii&79P@N>45^s&_U>;pkpPLd zk7s$@`aCtgq%Ly^o|>ts>CApMWX13O6RzDk9G6|a@9Up+4{EP{|9?;O>(}sAi99@f zSKET`H&48{@uP;Cn!KN_*Jb7Y`Da=_e!2PP&gYOy!}uzXFn#g3rLQ7hidJ3~mtJWV zSLGV_^}6qDhMc4god?y}W=l}acwwsRnjKX$d0M#F`V-k--$q5%hXZX{Ej^oY_O=`* z!qzM@+sl7L_8%Gj^n|*okF!nCve>iK~<#TyJKfQe`f8>v_MO5(nSf<(U zY+=4>TjAU8eth8;zgaK8Bm5)%_4Qb|cko}QkTmX_w`=5}^=0RaI(l|c1CSs)420u(ebFwlbmV`F14 zFE5}PpaKsM4}E=oph#$FD9{`$D=VNXplJyS3GVLhKoV$|g@pxB8H5eA1*jKjHP9JA z22c>l2C4^I4O9;#ffl*Cx<*Gw1C0W*Ax8Q7`WhM<0=Ym1KqmnK(EUzMP9`QMKye5F z+5%JtadTW;Twq|Jo0}WNk3fe3!=#sS|4$$lQ4-`A%)lz^5E>cjZz#Z|QnhTuAE6wYy0#}q8J1xvsvd@xNE?r-{{eM&Ficd9`g%5sh zQV{j*KJ)3z%k@PZVy;<@CS1Aa^MOZGfzftJky;}!} zcYFVv=^$eK{K|T1$8aXuz~JccQ$YduFWd;}Tal@^b;~x@g?kroUTs}f_4^k)+hhY1 z8zU>R(#OKC{3p*pxl%GEtt)Qg;+s3YQgU6_pSWCeHNmX;CC@VB8RrtLsLAj)tVrDW zLa;I_s&DGmMea{Bb8XEgaoxLPG~+CFErPrJH^1K-!(8i){(I&=U2$Kx%4OcSRTCwB z(hTMvEMdDW>62~{JL%xc+9T<|**PsuB#86xE|KQ`zHYgRizi?9nlm?E#8%mSdic%K zJ9qBgwYO1w`10q{kEW;c-^Z=dp_J70o*ouB?Pt^3cQVkYoO%VBmdFv&1@ My85}Sb4q9e0DI15MgRZ+ diff --git a/source/C2D_helper.c b/source/C2D_helper.c index c8368e0..a0e2c4d 100644 --- a/source/C2D_helper.c +++ b/source/C2D_helper.c @@ -1,3 +1,5 @@ +#include + #include "common.h" #include "C2D_helper.h" @@ -8,12 +10,22 @@ void Draw_EndFrame(void) C3D_FrameEnd(0); } -void Draw_Text(float x, float y, float size, Color color, const char *text) +void Draw_Text(float x, float y, float size, Colour colour, const char *text) { C2D_Text c2d_text; C2D_TextParse(&c2d_text, dynamicBuf, text); C2D_TextOptimize(&c2d_text); - C2D_DrawText(&c2d_text, C2D_WithColor, x, y, 0.5f, size, size, color); + C2D_DrawText(&c2d_text, C2D_WithColor, x, y, 0.5f, size, size, colour); +} + +void Draw_Textf(float x, float y, float size, Colour colour, const char* text, ...) +{ + char buffer[256]; + va_list args; + va_start(args, text); + vsnprintf(buffer, 256, text, args); + Draw_Text(x, y, size, colour, buffer); + va_end(args); } void Draw_GetTextSize(float size, float *width, float *height, const char *text) @@ -37,9 +49,9 @@ float Draw_GetTextHeight(float size, const char *text) return height; } -bool Draw_Rect(float x, float y, float w, float h, Color color) +bool Draw_Rect(float x, float y, float w, float h, Colour colour) { - return C2D_DrawRectSolid(x, y, 0.5f, w, h, color); + return C2D_DrawRectSolid(x, y, 0.5f, w, h, colour); } bool Draw_Image(C2D_Image image, float x, float y) diff --git a/source/dirbrowse.c b/source/dirbrowse.c index 830732c..e483d46 100644 --- a/source/dirbrowse.c +++ b/source/dirbrowse.c @@ -1,6 +1,6 @@ #include -//#include "archive.h" +#include "archive.h" #include "C2D_helper.h" #include "common.h" #include "config.h" @@ -299,11 +299,12 @@ void Dirbrowse_OpenFile(void) else if ((strncasecmp(file->ext, "png", 3) == 0) || (strncasecmp(file->ext, "jpg", 3) == 0) || (strncasecmp(file->ext, "bmp", 3) == 0) || (strncasecmp(file->ext, "gif", 3) == 0)) Gallery_DisplayImage(path); - /*else if (strncasecmp(file->ext, "zip", 3) == 0) + else if (strncasecmp(file->ext, "zip", 3) == 0) { Archive_ExtractZip(path, cwd); Dirbrowse_PopulateFiles(true); } + /* else if ((strncasecmp(file->ext, "mp3", 3) == 0) || (strncasecmp(file->ext, "ogg", 3) == 0) || (strncasecmp(file->ext, "wav", 3) == 0) || (strncasecmp(file->ext, "mod", 3) == 0)) Menu_PlayMusic(path); diff --git a/source/main.c b/source/main.c index 3ec3d34..27ca794 100644 --- a/source/main.c +++ b/source/main.c @@ -82,7 +82,8 @@ int main(int argc, char *argv[]) return 0; } - MENU_DEFAULT_STATE = MENU_STATE_HOME; + MENU_STATE = MENU_STATE_HOME; + BROWSE_STATE = BROWSE_STATE_SD; Menu_Main(); Term_Services(); diff --git a/source/menus/lodepng.c b/source/menus/lodepng.c new file mode 100644 index 0000000..98a8376 --- /dev/null +++ b/source/menus/lodepng.c @@ -0,0 +1,6245 @@ +/* +LodePNG version 20180114 + +Copyright (c) 2005-2018 Lode Vandevenne + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +/* +The manual and changelog are in the header file "lodepng.h" +Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for C. +*/ + +#include "lodepng.h" + +#include +#include +#include + +#if defined(_MSC_VER) && (_MSC_VER >= 1310) /*Visual Studio: A few warning types are not desired here.*/ +#pragma warning( disable : 4244 ) /*implicit conversions: not warned by gcc -Wall -Wextra and requires too much casts*/ +#pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/ +#endif /*_MSC_VER */ + +const char* LODEPNG_VERSION_STRING = "20180114"; + +/* +This source file is built up in the following large parts. The code sections +with the "LODEPNG_COMPILE_" #defines divide this up further in an intermixed way. +-Tools for C and common code for PNG and Zlib +-C Code for Zlib (huffman, deflate, ...) +-C Code for PNG (file format chunks, adam7, PNG filters, color conversions, ...) +-The C++ wrapper around all of the above +*/ + +/*The malloc, realloc and free functions defined here with "lodepng_" in front +of the name, so that you can easily change them to others related to your +platform if needed. Everything else in the code calls these. Pass +-DLODEPNG_NO_COMPILE_ALLOCATORS to the compiler, or comment out +#define LODEPNG_COMPILE_ALLOCATORS in the header, to disable the ones here and +define them in your own project's source files without needing to change +lodepng source code. Don't forget to remove "static" if you copypaste them +from here.*/ + +#ifdef LODEPNG_COMPILE_ALLOCATORS +static void* lodepng_malloc(size_t size) +{ + return malloc(size); +} + +static void* lodepng_realloc(void* ptr, size_t new_size) +{ + return realloc(ptr, new_size); +} + +static void lodepng_free(void* ptr) +{ + free(ptr); +} +#else /*LODEPNG_COMPILE_ALLOCATORS*/ +void* lodepng_malloc(size_t size); +void* lodepng_realloc(void* ptr, size_t new_size); +void lodepng_free(void* ptr); +#endif /*LODEPNG_COMPILE_ALLOCATORS*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // Tools for C, and common code for PNG and Zlib. // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/* +Often in case of an error a value is assigned to a variable and then it breaks +out of a loop (to go to the cleanup phase of a function). This macro does that. +It makes the error handling code shorter and more readable. + +Example: if(!uivector_resizev(&frequencies_ll, 286, 0)) ERROR_BREAK(83); +*/ +#define CERROR_BREAK(errorvar, code)\ +{\ + errorvar = code;\ + break;\ +} + +/*version of CERROR_BREAK that assumes the common case where the error variable is named "error"*/ +#define ERROR_BREAK(code) CERROR_BREAK(error, code) + +/*Set error var to the error code, and return it.*/ +#define CERROR_RETURN_ERROR(errorvar, code)\ +{\ + errorvar = code;\ + return code;\ +} + +/*Try the code, if it returns error, also return the error.*/ +#define CERROR_TRY_RETURN(call)\ +{\ + unsigned error = call;\ + if(error) return error;\ +} + +/*Set error var to the error code, and return from the void function.*/ +#define CERROR_RETURN(errorvar, code)\ +{\ + errorvar = code;\ + return;\ +} + +/* +About uivector, ucvector and string: +-All of them wrap dynamic arrays or text strings in a similar way. +-LodePNG was originally written in C++. The vectors replace the std::vectors that were used in the C++ version. +-The string tools are made to avoid problems with compilers that declare things like strncat as deprecated. +-They're not used in the interface, only internally in this file as static functions. +-As with many other structs in this file, the init and cleanup functions serve as ctor and dtor. +*/ + +#ifdef LODEPNG_COMPILE_ZLIB +/*dynamic vector of unsigned ints*/ +typedef struct uivector +{ + unsigned* data; + size_t size; /*size in number of unsigned longs*/ + size_t allocsize; /*allocated size in bytes*/ +} uivector; + +static void uivector_cleanup(void* p) +{ + ((uivector*)p)->size = ((uivector*)p)->allocsize = 0; + lodepng_free(((uivector*)p)->data); + ((uivector*)p)->data = NULL; +} + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned uivector_reserve(uivector* p, size_t allocsize) +{ + if(allocsize > p->allocsize) + { + size_t newsize = (allocsize > p->allocsize * 2) ? allocsize : (allocsize * 3 / 2); + void* data = lodepng_realloc(p->data, newsize); + if(data) + { + p->allocsize = newsize; + p->data = (unsigned*)data; + } + else return 0; /*error: not enough memory*/ + } + return 1; +} + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned uivector_resize(uivector* p, size_t size) +{ + if(!uivector_reserve(p, size * sizeof(unsigned))) return 0; + p->size = size; + return 1; /*success*/ +} + +/*resize and give all new elements the value*/ +static unsigned uivector_resizev(uivector* p, size_t size, unsigned value) +{ + size_t oldsize = p->size, i; + if(!uivector_resize(p, size)) return 0; + for(i = oldsize; i < size; ++i) p->data[i] = value; + return 1; +} + +static void uivector_init(uivector* p) +{ + p->data = NULL; + p->size = p->allocsize = 0; +} + +#ifdef LODEPNG_COMPILE_ENCODER +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned uivector_push_back(uivector* p, unsigned c) +{ + if(!uivector_resize(p, p->size + 1)) return 0; + p->data[p->size - 1] = c; + return 1; +} +#endif /*LODEPNG_COMPILE_ENCODER*/ +#endif /*LODEPNG_COMPILE_ZLIB*/ + +/* /////////////////////////////////////////////////////////////////////////// */ + +/*dynamic vector of unsigned chars*/ +typedef struct ucvector +{ + unsigned char* data; + size_t size; /*used size*/ + size_t allocsize; /*allocated size*/ +} ucvector; + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned ucvector_reserve(ucvector* p, size_t allocsize) +{ + if(allocsize > p->allocsize) + { + size_t newsize = (allocsize > p->allocsize * 2) ? allocsize : (allocsize * 3 / 2); + void* data = lodepng_realloc(p->data, newsize); + if(data) + { + p->allocsize = newsize; + p->data = (unsigned char*)data; + } + else return 0; /*error: not enough memory*/ + } + return 1; +} + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned ucvector_resize(ucvector* p, size_t size) +{ + if(!ucvector_reserve(p, size * sizeof(unsigned char))) return 0; + p->size = size; + return 1; /*success*/ +} + +#ifdef LODEPNG_COMPILE_PNG + +static void ucvector_cleanup(void* p) +{ + ((ucvector*)p)->size = ((ucvector*)p)->allocsize = 0; + lodepng_free(((ucvector*)p)->data); + ((ucvector*)p)->data = NULL; +} + +static void ucvector_init(ucvector* p) +{ + p->data = NULL; + p->size = p->allocsize = 0; +} +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ZLIB +/*you can both convert from vector to buffer&size and vica versa. If you use +init_buffer to take over a buffer and size, it is not needed to use cleanup*/ +static void ucvector_init_buffer(ucvector* p, unsigned char* buffer, size_t size) +{ + p->data = buffer; + p->allocsize = p->size = size; +} +#endif /*LODEPNG_COMPILE_ZLIB*/ + +#if (defined(LODEPNG_COMPILE_PNG) && defined(LODEPNG_COMPILE_ANCILLARY_CHUNKS)) || defined(LODEPNG_COMPILE_ENCODER) +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned ucvector_push_back(ucvector* p, unsigned char c) +{ + if(!ucvector_resize(p, p->size + 1)) return 0; + p->data[p->size - 1] = c; + return 1; +} +#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/ + + +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_PNG +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned string_resize(char** out, size_t size) +{ + char* data = (char*)lodepng_realloc(*out, size + 1); + if(data) + { + data[size] = 0; /*null termination char*/ + *out = data; + } + return data != 0; +} + +/*init a {char*, size_t} pair for use as string*/ +static void string_init(char** out) +{ + *out = NULL; + string_resize(out, 0); +} + +/*free the above pair again*/ +static void string_cleanup(char** out) +{ + lodepng_free(*out); + *out = NULL; +} + +static void string_set(char** out, const char* in) +{ + size_t insize = strlen(in), i; + if(string_resize(out, insize)) + { + for(i = 0; i != insize; ++i) + { + (*out)[i] = in[i]; + } + } +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +/* ////////////////////////////////////////////////////////////////////////// */ + +unsigned lodepng_read32bitInt(const unsigned char* buffer) +{ + return (unsigned)((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]); +} + +#if defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER) +/*buffer must have at least 4 allocated bytes available*/ +static void lodepng_set32bitInt(unsigned char* buffer, unsigned value) +{ + buffer[0] = (unsigned char)((value >> 24) & 0xff); + buffer[1] = (unsigned char)((value >> 16) & 0xff); + buffer[2] = (unsigned char)((value >> 8) & 0xff); + buffer[3] = (unsigned char)((value ) & 0xff); +} +#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/ + +#ifdef LODEPNG_COMPILE_ENCODER +static void lodepng_add32bitInt(ucvector* buffer, unsigned value) +{ + ucvector_resize(buffer, buffer->size + 4); /*todo: give error if resize failed*/ + lodepng_set32bitInt(&buffer->data[buffer->size - 4], value); +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / File IO / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_DISK + +/* returns negative value on error. This should be pure C compatible, so no fstat. */ +static long lodepng_filesize(const char* filename) +{ + FILE* file; + long size; + file = fopen(filename, "rb"); + if(!file) return -1; + + if(fseek(file, 0, SEEK_END) != 0) + { + fclose(file); + return -1; + } + + size = ftell(file); + /* It may give LONG_MAX as directory size, this is invalid for us. */ + if(size == LONG_MAX) size = -1; + + fclose(file); + return size; +} + +/* load file into buffer that already has the correct allocated size. Returns error code.*/ +static unsigned lodepng_buffer_file(unsigned char* out, size_t size, const char* filename) +{ + FILE* file; + size_t readsize; + file = fopen(filename, "rb"); + if(!file) return 78; + + readsize = fread(out, 1, size, file); + fclose(file); + + if (readsize != size) return 78; + return 0; +} + +unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename) +{ + long size = lodepng_filesize(filename); + if (size < 0) return 78; + *outsize = (size_t)size; + + *out = (unsigned char*)lodepng_malloc((size_t)size); + if(!(*out) && size > 0) return 83; /*the above malloc failed*/ + + return lodepng_buffer_file(*out, (size_t)size, filename); +} + +/*write given buffer to the file, overwriting the file, it doesn't append to it.*/ +unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename) +{ + FILE* file; + file = fopen(filename, "wb" ); + if(!file) return 79; + fwrite((char*)buffer , 1 , buffersize, file); + fclose(file); + return 0; +} + +#endif /*LODEPNG_COMPILE_DISK*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // End of common code and tools. Begin of Zlib related code. // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_ZLIB +#ifdef LODEPNG_COMPILE_ENCODER +/*TODO: this ignores potential out of memory errors*/ +#define addBitToStream(/*size_t**/ bitpointer, /*ucvector**/ bitstream, /*unsigned char*/ bit)\ +{\ + /*add a new byte at the end*/\ + if(((*bitpointer) & 7) == 0) ucvector_push_back(bitstream, (unsigned char)0);\ + /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/\ + (bitstream->data[bitstream->size - 1]) |= (bit << ((*bitpointer) & 0x7));\ + ++(*bitpointer);\ +} + +static void addBitsToStream(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits) +{ + size_t i; + for(i = 0; i != nbits; ++i) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> i) & 1)); +} + +static void addBitsToStreamReversed(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits) +{ + size_t i; + for(i = 0; i != nbits; ++i) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> (nbits - 1 - i)) & 1)); +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_DECODER + +#define READBIT(bitpointer, bitstream) ((bitstream[bitpointer >> 3] >> (bitpointer & 0x7)) & (unsigned char)1) + +static unsigned char readBitFromStream(size_t* bitpointer, const unsigned char* bitstream) +{ + unsigned char result = (unsigned char)(READBIT(*bitpointer, bitstream)); + ++(*bitpointer); + return result; +} + +static unsigned readBitsFromStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) +{ + unsigned result = 0, i; + for(i = 0; i != nbits; ++i) + { + result += ((unsigned)READBIT(*bitpointer, bitstream)) << i; + ++(*bitpointer); + } + return result; +} +#endif /*LODEPNG_COMPILE_DECODER*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Deflate - Huffman / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#define FIRST_LENGTH_CODE_INDEX 257 +#define LAST_LENGTH_CODE_INDEX 285 +/*256 literals, the end code, some length codes, and 2 unused codes*/ +#define NUM_DEFLATE_CODE_SYMBOLS 288 +/*the distance codes have their own symbols, 30 used, 2 unused*/ +#define NUM_DISTANCE_SYMBOLS 32 +/*the code length codes. 0-15: code lengths, 16: copy previous 3-6 times, 17: 3-10 zeros, 18: 11-138 zeros*/ +#define NUM_CODE_LENGTH_CODES 19 + +/*the base lengths represented by codes 257-285*/ +static const unsigned LENGTHBASE[29] + = {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}; + +/*the extra bits used by codes 257-285 (added to base length)*/ +static const unsigned LENGTHEXTRA[29] + = {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}; + +/*the base backwards distances (the bits of distance codes appear after length codes and use their own huffman tree)*/ +static const unsigned DISTANCEBASE[30] + = {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}; + +/*the extra bits of backwards distances (added to base)*/ +static const unsigned DISTANCEEXTRA[30] + = {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}; + +/*the order in which "code length alphabet code lengths" are stored, out of this +the huffman tree of the dynamic huffman tree lengths is generated*/ +static const unsigned CLCL_ORDER[NUM_CODE_LENGTH_CODES] + = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +/* ////////////////////////////////////////////////////////////////////////// */ + +/* +Huffman tree struct, containing multiple representations of the tree +*/ +typedef struct HuffmanTree +{ + unsigned* tree2d; + unsigned* tree1d; + unsigned* lengths; /*the lengths of the codes of the 1d-tree*/ + unsigned maxbitlen; /*maximum number of bits a single code can get*/ + unsigned numcodes; /*number of symbols in the alphabet = number of codes*/ +} HuffmanTree; + +/*function used for debug purposes to draw the tree in ascii art with C++*/ +/* +static void HuffmanTree_draw(HuffmanTree* tree) +{ + std::cout << "tree. length: " << tree->numcodes << " maxbitlen: " << tree->maxbitlen << std::endl; + for(size_t i = 0; i != tree->tree1d.size; ++i) + { + if(tree->lengths.data[i]) + std::cout << i << " " << tree->tree1d.data[i] << " " << tree->lengths.data[i] << std::endl; + } + std::cout << std::endl; +}*/ + +static void HuffmanTree_init(HuffmanTree* tree) +{ + tree->tree2d = 0; + tree->tree1d = 0; + tree->lengths = 0; +} + +static void HuffmanTree_cleanup(HuffmanTree* tree) +{ + lodepng_free(tree->tree2d); + lodepng_free(tree->tree1d); + lodepng_free(tree->lengths); +} + +/*the tree representation used by the decoder. return value is error*/ +static unsigned HuffmanTree_make2DTree(HuffmanTree* tree) +{ + unsigned nodefilled = 0; /*up to which node it is filled*/ + unsigned treepos = 0; /*position in the tree (1 of the numcodes columns)*/ + unsigned n, i; + + tree->tree2d = (unsigned*)lodepng_malloc(tree->numcodes * 2 * sizeof(unsigned)); + if(!tree->tree2d) return 83; /*alloc fail*/ + + /* + convert tree1d[] to tree2d[][]. In the 2D array, a value of 32767 means + uninited, a value >= numcodes is an address to another bit, a value < numcodes + is a code. The 2 rows are the 2 possible bit values (0 or 1), there are as + many columns as codes - 1. + A good huffman tree has N * 2 - 1 nodes, of which N - 1 are internal nodes. + Here, the internal nodes are stored (what their 0 and 1 option point to). + There is only memory for such good tree currently, if there are more nodes + (due to too long length codes), error 55 will happen + */ + for(n = 0; n < tree->numcodes * 2; ++n) + { + tree->tree2d[n] = 32767; /*32767 here means the tree2d isn't filled there yet*/ + } + + for(n = 0; n < tree->numcodes; ++n) /*the codes*/ + { + for(i = 0; i != tree->lengths[n]; ++i) /*the bits for this code*/ + { + unsigned char bit = (unsigned char)((tree->tree1d[n] >> (tree->lengths[n] - i - 1)) & 1); + /*oversubscribed, see comment in lodepng_error_text*/ + if(treepos > 2147483647 || treepos + 2 > tree->numcodes) return 55; + if(tree->tree2d[2 * treepos + bit] == 32767) /*not yet filled in*/ + { + if(i + 1 == tree->lengths[n]) /*last bit*/ + { + tree->tree2d[2 * treepos + bit] = n; /*put the current code in it*/ + treepos = 0; + } + else + { + /*put address of the next step in here, first that address has to be found of course + (it's just nodefilled + 1)...*/ + ++nodefilled; + /*addresses encoded with numcodes added to it*/ + tree->tree2d[2 * treepos + bit] = nodefilled + tree->numcodes; + treepos = nodefilled; + } + } + else treepos = tree->tree2d[2 * treepos + bit] - tree->numcodes; + } + } + + for(n = 0; n < tree->numcodes * 2; ++n) + { + if(tree->tree2d[n] == 32767) tree->tree2d[n] = 0; /*remove possible remaining 32767's*/ + } + + return 0; +} + +/* +Second step for the ...makeFromLengths and ...makeFromFrequencies functions. +numcodes, lengths and maxbitlen must already be filled in correctly. return +value is error. +*/ +static unsigned HuffmanTree_makeFromLengths2(HuffmanTree* tree) +{ + uivector blcount; + uivector nextcode; + unsigned error = 0; + unsigned bits, n; + + uivector_init(&blcount); + uivector_init(&nextcode); + + tree->tree1d = (unsigned*)lodepng_malloc(tree->numcodes * sizeof(unsigned)); + if(!tree->tree1d) error = 83; /*alloc fail*/ + + if(!uivector_resizev(&blcount, tree->maxbitlen + 1, 0) + || !uivector_resizev(&nextcode, tree->maxbitlen + 1, 0)) + error = 83; /*alloc fail*/ + + if(!error) + { + /*step 1: count number of instances of each code length*/ + for(bits = 0; bits != tree->numcodes; ++bits) ++blcount.data[tree->lengths[bits]]; + /*step 2: generate the nextcode values*/ + for(bits = 1; bits <= tree->maxbitlen; ++bits) + { + nextcode.data[bits] = (nextcode.data[bits - 1] + blcount.data[bits - 1]) << 1; + } + /*step 3: generate all the codes*/ + for(n = 0; n != tree->numcodes; ++n) + { + if(tree->lengths[n] != 0) tree->tree1d[n] = nextcode.data[tree->lengths[n]]++; + } + } + + uivector_cleanup(&blcount); + uivector_cleanup(&nextcode); + + if(!error) return HuffmanTree_make2DTree(tree); + else return error; +} + +/* +given the code lengths (as stored in the PNG file), generate the tree as defined +by Deflate. maxbitlen is the maximum bits that a code in the tree can have. +return value is error. +*/ +static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* bitlen, + size_t numcodes, unsigned maxbitlen) +{ + unsigned i; + tree->lengths = (unsigned*)lodepng_malloc(numcodes * sizeof(unsigned)); + if(!tree->lengths) return 83; /*alloc fail*/ + for(i = 0; i != numcodes; ++i) tree->lengths[i] = bitlen[i]; + tree->numcodes = (unsigned)numcodes; /*number of symbols*/ + tree->maxbitlen = maxbitlen; + return HuffmanTree_makeFromLengths2(tree); +} + +#ifdef LODEPNG_COMPILE_ENCODER + +/*BPM: Boundary Package Merge, see "A Fast and Space-Economical Algorithm for Length-Limited Coding", +Jyrki Katajainen, Alistair Moffat, Andrew Turpin, 1995.*/ + +/*chain node for boundary package merge*/ +typedef struct BPMNode +{ + int weight; /*the sum of all weights in this chain*/ + unsigned index; /*index of this leaf node (called "count" in the paper)*/ + struct BPMNode* tail; /*the next nodes in this chain (null if last)*/ + int in_use; +} BPMNode; + +/*lists of chains*/ +typedef struct BPMLists +{ + /*memory pool*/ + unsigned memsize; + BPMNode* memory; + unsigned numfree; + unsigned nextfree; + BPMNode** freelist; + /*two heads of lookahead chains per list*/ + unsigned listsize; + BPMNode** chains0; + BPMNode** chains1; +} BPMLists; + +/*creates a new chain node with the given parameters, from the memory in the lists */ +static BPMNode* bpmnode_create(BPMLists* lists, int weight, unsigned index, BPMNode* tail) +{ + unsigned i; + BPMNode* result; + + /*memory full, so garbage collect*/ + if(lists->nextfree >= lists->numfree) + { + /*mark only those that are in use*/ + for(i = 0; i != lists->memsize; ++i) lists->memory[i].in_use = 0; + for(i = 0; i != lists->listsize; ++i) + { + BPMNode* node; + for(node = lists->chains0[i]; node != 0; node = node->tail) node->in_use = 1; + for(node = lists->chains1[i]; node != 0; node = node->tail) node->in_use = 1; + } + /*collect those that are free*/ + lists->numfree = 0; + for(i = 0; i != lists->memsize; ++i) + { + if(!lists->memory[i].in_use) lists->freelist[lists->numfree++] = &lists->memory[i]; + } + lists->nextfree = 0; + } + + result = lists->freelist[lists->nextfree++]; + result->weight = weight; + result->index = index; + result->tail = tail; + return result; +} + +/*sort the leaves with stable mergesort*/ +static void bpmnode_sort(BPMNode* leaves, size_t num) +{ + BPMNode* mem = (BPMNode*)lodepng_malloc(sizeof(*leaves) * num); + size_t width, counter = 0; + for(width = 1; width < num; width *= 2) + { + BPMNode* a = (counter & 1) ? mem : leaves; + BPMNode* b = (counter & 1) ? leaves : mem; + size_t p; + for(p = 0; p < num; p += 2 * width) + { + size_t q = (p + width > num) ? num : (p + width); + size_t r = (p + 2 * width > num) ? num : (p + 2 * width); + size_t i = p, j = q, k; + for(k = p; k < r; k++) + { + if(i < q && (j >= r || a[i].weight <= a[j].weight)) b[k] = a[i++]; + else b[k] = a[j++]; + } + } + counter++; + } + if(counter & 1) memcpy(leaves, mem, sizeof(*leaves) * num); + lodepng_free(mem); +} + +/*Boundary Package Merge step, numpresent is the amount of leaves, and c is the current chain.*/ +static void boundaryPM(BPMLists* lists, BPMNode* leaves, size_t numpresent, int c, int num) +{ + unsigned lastindex = lists->chains1[c]->index; + + if(c == 0) + { + if(lastindex >= numpresent) return; + lists->chains0[c] = lists->chains1[c]; + lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, 0); + } + else + { + /*sum of the weights of the head nodes of the previous lookahead chains.*/ + int sum = lists->chains0[c - 1]->weight + lists->chains1[c - 1]->weight; + lists->chains0[c] = lists->chains1[c]; + if(lastindex < numpresent && sum > leaves[lastindex].weight) + { + lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, lists->chains1[c]->tail); + return; + } + lists->chains1[c] = bpmnode_create(lists, sum, lastindex, lists->chains1[c - 1]); + /*in the end we are only interested in the chain of the last list, so no + need to recurse if we're at the last one (this gives measurable speedup)*/ + if(num + 1 < (int)(2 * numpresent - 2)) + { + boundaryPM(lists, leaves, numpresent, c - 1, num); + boundaryPM(lists, leaves, numpresent, c - 1, num); + } + } +} + +unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies, + size_t numcodes, unsigned maxbitlen) +{ + unsigned error = 0; + unsigned i; + size_t numpresent = 0; /*number of symbols with non-zero frequency*/ + BPMNode* leaves; /*the symbols, only those with > 0 frequency*/ + + if(numcodes == 0) return 80; /*error: a tree of 0 symbols is not supposed to be made*/ + if((1u << maxbitlen) < (unsigned)numcodes) return 80; /*error: represent all symbols*/ + + leaves = (BPMNode*)lodepng_malloc(numcodes * sizeof(*leaves)); + if(!leaves) return 83; /*alloc fail*/ + + for(i = 0; i != numcodes; ++i) + { + if(frequencies[i] > 0) + { + leaves[numpresent].weight = (int)frequencies[i]; + leaves[numpresent].index = i; + ++numpresent; + } + } + + for(i = 0; i != numcodes; ++i) lengths[i] = 0; + + /*ensure at least two present symbols. There should be at least one symbol + according to RFC 1951 section 3.2.7. Some decoders incorrectly require two. To + make these work as well ensure there are at least two symbols. The + Package-Merge code below also doesn't work correctly if there's only one + symbol, it'd give it the theoritical 0 bits but in practice zlib wants 1 bit*/ + if(numpresent == 0) + { + lengths[0] = lengths[1] = 1; /*note that for RFC 1951 section 3.2.7, only lengths[0] = 1 is needed*/ + } + else if(numpresent == 1) + { + lengths[leaves[0].index] = 1; + lengths[leaves[0].index == 0 ? 1 : 0] = 1; + } + else + { + BPMLists lists; + BPMNode* node; + + bpmnode_sort(leaves, numpresent); + + lists.listsize = maxbitlen; + lists.memsize = 2 * maxbitlen * (maxbitlen + 1); + lists.nextfree = 0; + lists.numfree = lists.memsize; + lists.memory = (BPMNode*)lodepng_malloc(lists.memsize * sizeof(*lists.memory)); + lists.freelist = (BPMNode**)lodepng_malloc(lists.memsize * sizeof(BPMNode*)); + lists.chains0 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); + lists.chains1 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); + if(!lists.memory || !lists.freelist || !lists.chains0 || !lists.chains1) error = 83; /*alloc fail*/ + + if(!error) + { + for(i = 0; i != lists.memsize; ++i) lists.freelist[i] = &lists.memory[i]; + + bpmnode_create(&lists, leaves[0].weight, 1, 0); + bpmnode_create(&lists, leaves[1].weight, 2, 0); + + for(i = 0; i != lists.listsize; ++i) + { + lists.chains0[i] = &lists.memory[0]; + lists.chains1[i] = &lists.memory[1]; + } + + /*each boundaryPM call adds one chain to the last list, and we need 2 * numpresent - 2 chains.*/ + for(i = 2; i != 2 * numpresent - 2; ++i) boundaryPM(&lists, leaves, numpresent, (int)maxbitlen - 1, (int)i); + + for(node = lists.chains1[maxbitlen - 1]; node; node = node->tail) + { + for(i = 0; i != node->index; ++i) ++lengths[leaves[i].index]; + } + } + + lodepng_free(lists.memory); + lodepng_free(lists.freelist); + lodepng_free(lists.chains0); + lodepng_free(lists.chains1); + } + + lodepng_free(leaves); + return error; +} + +/*Create the Huffman tree given the symbol frequencies*/ +static unsigned HuffmanTree_makeFromFrequencies(HuffmanTree* tree, const unsigned* frequencies, + size_t mincodes, size_t numcodes, unsigned maxbitlen) +{ + unsigned error = 0; + while(!frequencies[numcodes - 1] && numcodes > mincodes) --numcodes; /*trim zeroes*/ + tree->maxbitlen = maxbitlen; + tree->numcodes = (unsigned)numcodes; /*number of symbols*/ + tree->lengths = (unsigned*)lodepng_realloc(tree->lengths, numcodes * sizeof(unsigned)); + if(!tree->lengths) return 83; /*alloc fail*/ + /*initialize all lengths to 0*/ + memset(tree->lengths, 0, numcodes * sizeof(unsigned)); + + error = lodepng_huffman_code_lengths(tree->lengths, frequencies, numcodes, maxbitlen); + if(!error) error = HuffmanTree_makeFromLengths2(tree); + return error; +} + +static unsigned HuffmanTree_getCode(const HuffmanTree* tree, unsigned index) +{ + return tree->tree1d[index]; +} + +static unsigned HuffmanTree_getLength(const HuffmanTree* tree, unsigned index) +{ + return tree->lengths[index]; +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/*get the literal and length code tree of a deflated block with fixed tree, as per the deflate specification*/ +static unsigned generateFixedLitLenTree(HuffmanTree* tree) +{ + unsigned i, error = 0; + unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); + if(!bitlen) return 83; /*alloc fail*/ + + /*288 possible codes: 0-255=literals, 256=endcode, 257-285=lengthcodes, 286-287=unused*/ + for(i = 0; i <= 143; ++i) bitlen[i] = 8; + for(i = 144; i <= 255; ++i) bitlen[i] = 9; + for(i = 256; i <= 279; ++i) bitlen[i] = 7; + for(i = 280; i <= 287; ++i) bitlen[i] = 8; + + error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15); + + lodepng_free(bitlen); + return error; +} + +/*get the distance code tree of a deflated block with fixed tree, as specified in the deflate specification*/ +static unsigned generateFixedDistanceTree(HuffmanTree* tree) +{ + unsigned i, error = 0; + unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); + if(!bitlen) return 83; /*alloc fail*/ + + /*there are 32 distance codes, but 30-31 are unused*/ + for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen[i] = 5; + error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DISTANCE_SYMBOLS, 15); + + lodepng_free(bitlen); + return error; +} + +#ifdef LODEPNG_COMPILE_DECODER + +/* +returns the code, or (unsigned)(-1) if error happened +inbitlength is the length of the complete buffer, in bits (so its byte length times 8) +*/ +static unsigned huffmanDecodeSymbol(const unsigned char* in, size_t* bp, + const HuffmanTree* codetree, size_t inbitlength) +{ + unsigned treepos = 0, ct; + for(;;) + { + if(*bp >= inbitlength) return (unsigned)(-1); /*error: end of input memory reached without endcode*/ + /* + decode the symbol from the tree. The "readBitFromStream" code is inlined in + the expression below because this is the biggest bottleneck while decoding + */ + ct = codetree->tree2d[(treepos << 1) + READBIT(*bp, in)]; + ++(*bp); + if(ct < codetree->numcodes) return ct; /*the symbol is decoded, return it*/ + else treepos = ct - codetree->numcodes; /*symbol not yet decoded, instead move tree position*/ + + if(treepos >= codetree->numcodes) return (unsigned)(-1); /*error: it appeared outside the codetree*/ + } +} +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_DECODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Inflator (Decompressor) / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*get the tree of a deflated block with fixed tree, as specified in the deflate specification*/ +static void getTreeInflateFixed(HuffmanTree* tree_ll, HuffmanTree* tree_d) +{ + /*TODO: check for out of memory errors*/ + generateFixedLitLenTree(tree_ll); + generateFixedDistanceTree(tree_d); +} + +/*get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/ +static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d, + const unsigned char* in, size_t* bp, size_t inlength) +{ + /*make sure that length values that aren't filled in will be 0, or a wrong tree will be generated*/ + unsigned error = 0; + unsigned n, HLIT, HDIST, HCLEN, i; + size_t inbitlength = inlength * 8; + + /*see comments in deflateDynamic for explanation of the context and these variables, it is analogous*/ + unsigned* bitlen_ll = 0; /*lit,len code lengths*/ + unsigned* bitlen_d = 0; /*dist code lengths*/ + /*code length code lengths ("clcl"), the bit lengths of the huffman tree used to compress bitlen_ll and bitlen_d*/ + unsigned* bitlen_cl = 0; + HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/ + + if((*bp) + 14 > (inlength << 3)) return 49; /*error: the bit pointer is or will go past the memory*/ + + /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/ + HLIT = readBitsFromStream(bp, in, 5) + 257; + /*number of distance codes. Unlike the spec, the value 1 is added to it here already*/ + HDIST = readBitsFromStream(bp, in, 5) + 1; + /*number of code length codes. Unlike the spec, the value 4 is added to it here already*/ + HCLEN = readBitsFromStream(bp, in, 4) + 4; + + if((*bp) + HCLEN * 3 > (inlength << 3)) return 50; /*error: the bit pointer is or will go past the memory*/ + + HuffmanTree_init(&tree_cl); + + while(!error) + { + /*read the code length codes out of 3 * (amount of code length codes) bits*/ + + bitlen_cl = (unsigned*)lodepng_malloc(NUM_CODE_LENGTH_CODES * sizeof(unsigned)); + if(!bitlen_cl) ERROR_BREAK(83 /*alloc fail*/); + + for(i = 0; i != NUM_CODE_LENGTH_CODES; ++i) + { + if(i < HCLEN) bitlen_cl[CLCL_ORDER[i]] = readBitsFromStream(bp, in, 3); + else bitlen_cl[CLCL_ORDER[i]] = 0; /*if not, it must stay 0*/ + } + + error = HuffmanTree_makeFromLengths(&tree_cl, bitlen_cl, NUM_CODE_LENGTH_CODES, 7); + if(error) break; + + /*now we can use this tree to read the lengths for the tree that this function will return*/ + bitlen_ll = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); + bitlen_d = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); + if(!bitlen_ll || !bitlen_d) ERROR_BREAK(83 /*alloc fail*/); + for(i = 0; i != NUM_DEFLATE_CODE_SYMBOLS; ++i) bitlen_ll[i] = 0; + for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen_d[i] = 0; + + /*i is the current symbol we're reading in the part that contains the code lengths of lit/len and dist codes*/ + i = 0; + while(i < HLIT + HDIST) + { + unsigned code = huffmanDecodeSymbol(in, bp, &tree_cl, inbitlength); + if(code <= 15) /*a length code*/ + { + if(i < HLIT) bitlen_ll[i] = code; + else bitlen_d[i - HLIT] = code; + ++i; + } + else if(code == 16) /*repeat previous*/ + { + unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/ + unsigned value; /*set value to the previous code*/ + + if(i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/ + + if((*bp + 2) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ + replength += readBitsFromStream(bp, in, 2); + + if(i < HLIT + 1) value = bitlen_ll[i - 1]; + else value = bitlen_d[i - HLIT - 1]; + /*repeat this value in the next lengths*/ + for(n = 0; n < replength; ++n) + { + if(i >= HLIT + HDIST) ERROR_BREAK(13); /*error: i is larger than the amount of codes*/ + if(i < HLIT) bitlen_ll[i] = value; + else bitlen_d[i - HLIT] = value; + ++i; + } + } + else if(code == 17) /*repeat "0" 3-10 times*/ + { + unsigned replength = 3; /*read in the bits that indicate repeat length*/ + if((*bp + 3) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ + replength += readBitsFromStream(bp, in, 3); + + /*repeat this value in the next lengths*/ + for(n = 0; n < replength; ++n) + { + if(i >= HLIT + HDIST) ERROR_BREAK(14); /*error: i is larger than the amount of codes*/ + + if(i < HLIT) bitlen_ll[i] = 0; + else bitlen_d[i - HLIT] = 0; + ++i; + } + } + else if(code == 18) /*repeat "0" 11-138 times*/ + { + unsigned replength = 11; /*read in the bits that indicate repeat length*/ + if((*bp + 7) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ + replength += readBitsFromStream(bp, in, 7); + + /*repeat this value in the next lengths*/ + for(n = 0; n < replength; ++n) + { + if(i >= HLIT + HDIST) ERROR_BREAK(15); /*error: i is larger than the amount of codes*/ + + if(i < HLIT) bitlen_ll[i] = 0; + else bitlen_d[i - HLIT] = 0; + ++i; + } + } + else /*if(code == (unsigned)(-1))*/ /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ + { + if(code == (unsigned)(-1)) + { + /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol + (10=no endcode, 11=wrong jump outside of tree)*/ + error = (*bp) > inbitlength ? 10 : 11; + } + else error = 16; /*unexisting code, this can never happen*/ + break; + } + } + if(error) break; + + if(bitlen_ll[256] == 0) ERROR_BREAK(64); /*the length of the end code 256 must be larger than 0*/ + + /*now we've finally got HLIT and HDIST, so generate the code trees, and the function is done*/ + error = HuffmanTree_makeFromLengths(tree_ll, bitlen_ll, NUM_DEFLATE_CODE_SYMBOLS, 15); + if(error) break; + error = HuffmanTree_makeFromLengths(tree_d, bitlen_d, NUM_DISTANCE_SYMBOLS, 15); + + break; /*end of error-while*/ + } + + lodepng_free(bitlen_cl); + lodepng_free(bitlen_ll); + lodepng_free(bitlen_d); + HuffmanTree_cleanup(&tree_cl); + + return error; +} + +/*inflate a block with dynamic of fixed Huffman tree*/ +static unsigned inflateHuffmanBlock(ucvector* out, const unsigned char* in, size_t* bp, + size_t* pos, size_t inlength, unsigned btype) +{ + unsigned error = 0; + HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/ + HuffmanTree tree_d; /*the huffman tree for distance codes*/ + size_t inbitlength = inlength * 8; + + HuffmanTree_init(&tree_ll); + HuffmanTree_init(&tree_d); + + if(btype == 1) getTreeInflateFixed(&tree_ll, &tree_d); + else if(btype == 2) error = getTreeInflateDynamic(&tree_ll, &tree_d, in, bp, inlength); + + while(!error) /*decode all symbols until end reached, breaks at end code*/ + { + /*code_ll is literal, length or end code*/ + unsigned code_ll = huffmanDecodeSymbol(in, bp, &tree_ll, inbitlength); + if(code_ll <= 255) /*literal symbol*/ + { + /*ucvector_push_back would do the same, but for some reason the two lines below run 10% faster*/ + if(!ucvector_resize(out, (*pos) + 1)) ERROR_BREAK(83 /*alloc fail*/); + out->data[*pos] = (unsigned char)code_ll; + ++(*pos); + } + else if(code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ + { + unsigned code_d, distance; + unsigned numextrabits_l, numextrabits_d; /*extra bits for length and distance*/ + size_t start, forward, backward, length; + + /*part 1: get length base*/ + length = LENGTHBASE[code_ll - FIRST_LENGTH_CODE_INDEX]; + + /*part 2: get extra bits and add the value of that to length*/ + numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX]; + if((*bp + numextrabits_l) > inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ + length += readBitsFromStream(bp, in, numextrabits_l); + + /*part 3: get distance code*/ + code_d = huffmanDecodeSymbol(in, bp, &tree_d, inbitlength); + if(code_d > 29) + { + if(code_d == (unsigned)(-1)) /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ + { + /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol + (10=no endcode, 11=wrong jump outside of tree)*/ + error = (*bp) > inlength * 8 ? 10 : 11; + } + else error = 18; /*error: invalid distance code (30-31 are never used)*/ + break; + } + distance = DISTANCEBASE[code_d]; + + /*part 4: get extra bits from distance*/ + numextrabits_d = DISTANCEEXTRA[code_d]; + if((*bp + numextrabits_d) > inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ + distance += readBitsFromStream(bp, in, numextrabits_d); + + /*part 5: fill in all the out[n] values based on the length and dist*/ + start = (*pos); + if(distance > start) ERROR_BREAK(52); /*too long backward distance*/ + backward = start - distance; + + if(!ucvector_resize(out, (*pos) + length)) ERROR_BREAK(83 /*alloc fail*/); + if (distance < length) { + for(forward = 0; forward < length; ++forward) + { + out->data[(*pos)++] = out->data[backward++]; + } + } else { + memcpy(out->data + *pos, out->data + backward, length); + *pos += length; + } + } + else if(code_ll == 256) + { + break; /*end code, break the loop*/ + } + else /*if(code == (unsigned)(-1))*/ /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ + { + /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol + (10=no endcode, 11=wrong jump outside of tree)*/ + error = ((*bp) > inlength * 8) ? 10 : 11; + break; + } + } + + HuffmanTree_cleanup(&tree_ll); + HuffmanTree_cleanup(&tree_d); + + return error; +} + +static unsigned inflateNoCompression(ucvector* out, const unsigned char* in, size_t* bp, size_t* pos, size_t inlength) +{ + size_t p; + unsigned LEN, NLEN, n, error = 0; + + /*go to first boundary of byte*/ + while(((*bp) & 0x7) != 0) ++(*bp); + p = (*bp) / 8; /*byte position*/ + + /*read LEN (2 bytes) and NLEN (2 bytes)*/ + if(p + 4 >= inlength) return 52; /*error, bit pointer will jump past memory*/ + LEN = in[p] + 256u * in[p + 1]; p += 2; + NLEN = in[p] + 256u * in[p + 1]; p += 2; + + /*check if 16-bit NLEN is really the one's complement of LEN*/ + if(LEN + NLEN != 65535) return 21; /*error: NLEN is not one's complement of LEN*/ + + if(!ucvector_resize(out, (*pos) + LEN)) return 83; /*alloc fail*/ + + /*read the literal data: LEN bytes are now stored in the out buffer*/ + if(p + LEN > inlength) return 23; /*error: reading outside of in buffer*/ + for(n = 0; n < LEN; ++n) out->data[(*pos)++] = in[p++]; + + (*bp) = p * 8; + + return error; +} + +static unsigned lodepng_inflatev(ucvector* out, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings) +{ + /*bit pointer in the "in" data, current byte is bp >> 3, current bit is bp & 0x7 (from lsb to msb of the byte)*/ + size_t bp = 0; + unsigned BFINAL = 0; + size_t pos = 0; /*byte position in the out buffer*/ + unsigned error = 0; + + (void)settings; + + while(!BFINAL) + { + unsigned BTYPE; + if(bp + 2 >= insize * 8) return 52; /*error, bit pointer will jump past memory*/ + BFINAL = readBitFromStream(&bp, in); + BTYPE = 1u * readBitFromStream(&bp, in); + BTYPE += 2u * readBitFromStream(&bp, in); + + if(BTYPE == 3) return 20; /*error: invalid BTYPE*/ + else if(BTYPE == 0) error = inflateNoCompression(out, in, &bp, &pos, insize); /*no compression*/ + else error = inflateHuffmanBlock(out, in, &bp, &pos, insize, BTYPE); /*compression, BTYPE 01 or 10*/ + + if(error) return error; + } + + return error; +} + +unsigned lodepng_inflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings) +{ + unsigned error; + ucvector v; + ucvector_init_buffer(&v, *out, *outsize); + error = lodepng_inflatev(&v, in, insize, settings); + *out = v.data; + *outsize = v.size; + return error; +} + +static unsigned inflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings) +{ + if(settings->custom_inflate) + { + return settings->custom_inflate(out, outsize, in, insize, settings); + } + else + { + return lodepng_inflate(out, outsize, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Deflator (Compressor) / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +static const size_t MAX_SUPPORTED_DEFLATE_LENGTH = 258; + +/*bitlen is the size in bits of the code*/ +static void addHuffmanSymbol(size_t* bp, ucvector* compressed, unsigned code, unsigned bitlen) +{ + addBitsToStreamReversed(bp, compressed, code, bitlen); +} + +/*search the index in the array, that has the largest value smaller than or equal to the given value, +given array must be sorted (if no value is smaller, it returns the size of the given array)*/ +static size_t searchCodeIndex(const unsigned* array, size_t array_size, size_t value) +{ + /*binary search (only small gain over linear). TODO: use CPU log2 instruction for getting symbols instead*/ + size_t left = 1; + size_t right = array_size - 1; + + while(left <= right) { + size_t mid = (left + right) >> 1; + if (array[mid] >= value) right = mid - 1; + else left = mid + 1; + } + if(left >= array_size || array[left] > value) left--; + return left; +} + +static void addLengthDistance(uivector* values, size_t length, size_t distance) +{ + /*values in encoded vector are those used by deflate: + 0-255: literal bytes + 256: end + 257-285: length/distance pair (length code, followed by extra length bits, distance code, extra distance bits) + 286-287: invalid*/ + + unsigned length_code = (unsigned)searchCodeIndex(LENGTHBASE, 29, length); + unsigned extra_length = (unsigned)(length - LENGTHBASE[length_code]); + unsigned dist_code = (unsigned)searchCodeIndex(DISTANCEBASE, 30, distance); + unsigned extra_distance = (unsigned)(distance - DISTANCEBASE[dist_code]); + + uivector_push_back(values, length_code + FIRST_LENGTH_CODE_INDEX); + uivector_push_back(values, extra_length); + uivector_push_back(values, dist_code); + uivector_push_back(values, extra_distance); +} + +/*3 bytes of data get encoded into two bytes. The hash cannot use more than 3 +bytes as input because 3 is the minimum match length for deflate*/ +static const unsigned HASH_NUM_VALUES = 65536; +static const unsigned HASH_BIT_MASK = 65535; /*HASH_NUM_VALUES - 1, but C90 does not like that as initializer*/ + +typedef struct Hash +{ + int* head; /*hash value to head circular pos - can be outdated if went around window*/ + /*circular pos to prev circular pos*/ + unsigned short* chain; + int* val; /*circular pos to hash value*/ + + /*TODO: do this not only for zeros but for any repeated byte. However for PNG + it's always going to be the zeros that dominate, so not important for PNG*/ + int* headz; /*similar to head, but for chainz*/ + unsigned short* chainz; /*those with same amount of zeros*/ + unsigned short* zeros; /*length of zeros streak, used as a second hash chain*/ +} Hash; + +static unsigned hash_init(Hash* hash, unsigned windowsize) +{ + unsigned i; + hash->head = (int*)lodepng_malloc(sizeof(int) * HASH_NUM_VALUES); + hash->val = (int*)lodepng_malloc(sizeof(int) * windowsize); + hash->chain = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); + + hash->zeros = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); + hash->headz = (int*)lodepng_malloc(sizeof(int) * (MAX_SUPPORTED_DEFLATE_LENGTH + 1)); + hash->chainz = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); + + if(!hash->head || !hash->chain || !hash->val || !hash->headz|| !hash->chainz || !hash->zeros) + { + return 83; /*alloc fail*/ + } + + /*initialize hash table*/ + for(i = 0; i != HASH_NUM_VALUES; ++i) hash->head[i] = -1; + for(i = 0; i != windowsize; ++i) hash->val[i] = -1; + for(i = 0; i != windowsize; ++i) hash->chain[i] = i; /*same value as index indicates uninitialized*/ + + for(i = 0; i <= MAX_SUPPORTED_DEFLATE_LENGTH; ++i) hash->headz[i] = -1; + for(i = 0; i != windowsize; ++i) hash->chainz[i] = i; /*same value as index indicates uninitialized*/ + + return 0; +} + +static void hash_cleanup(Hash* hash) +{ + lodepng_free(hash->head); + lodepng_free(hash->val); + lodepng_free(hash->chain); + + lodepng_free(hash->zeros); + lodepng_free(hash->headz); + lodepng_free(hash->chainz); +} + + + +static unsigned getHash(const unsigned char* data, size_t size, size_t pos) +{ + unsigned result = 0; + if(pos + 2 < size) + { + /*A simple shift and xor hash is used. Since the data of PNGs is dominated + by zeroes due to the filters, a better hash does not have a significant + effect on speed in traversing the chain, and causes more time spend on + calculating the hash.*/ + result ^= (unsigned)(data[pos + 0] << 0u); + result ^= (unsigned)(data[pos + 1] << 4u); + result ^= (unsigned)(data[pos + 2] << 8u); + } else { + size_t amount, i; + if(pos >= size) return 0; + amount = size - pos; + for(i = 0; i != amount; ++i) result ^= (unsigned)(data[pos + i] << (i * 8u)); + } + return result & HASH_BIT_MASK; +} + +static unsigned countZeros(const unsigned char* data, size_t size, size_t pos) +{ + const unsigned char* start = data + pos; + const unsigned char* end = start + MAX_SUPPORTED_DEFLATE_LENGTH; + if(end > data + size) end = data + size; + data = start; + while(data != end && *data == 0) ++data; + /*subtracting two addresses returned as 32-bit number (max value is MAX_SUPPORTED_DEFLATE_LENGTH)*/ + return (unsigned)(data - start); +} + +/*wpos = pos & (windowsize - 1)*/ +static void updateHashChain(Hash* hash, size_t wpos, unsigned hashval, unsigned short numzeros) +{ + hash->val[wpos] = (int)hashval; + if(hash->head[hashval] != -1) hash->chain[wpos] = hash->head[hashval]; + hash->head[hashval] = (unsigned)wpos; + + hash->zeros[wpos] = numzeros; + if(hash->headz[numzeros] != -1) hash->chainz[wpos] = hash->headz[numzeros]; + hash->headz[numzeros] = (unsigned)wpos; +} + +/* +LZ77-encode the data. Return value is error code. The input are raw bytes, the output +is in the form of unsigned integers with codes representing for example literal bytes, or +length/distance pairs. +It uses a hash table technique to let it encode faster. When doing LZ77 encoding, a +sliding window (of windowsize) is used, and all past bytes in that window can be used as +the "dictionary". A brute force search through all possible distances would be slow, and +this hash technique is one out of several ways to speed this up. +*/ +static unsigned encodeLZ77(uivector* out, Hash* hash, + const unsigned char* in, size_t inpos, size_t insize, unsigned windowsize, + unsigned minmatch, unsigned nicematch, unsigned lazymatching) +{ + size_t pos; + unsigned i, error = 0; + /*for large window lengths, assume the user wants no compression loss. Otherwise, max hash chain length speedup.*/ + unsigned maxchainlength = windowsize >= 8192 ? windowsize : windowsize / 8; + unsigned maxlazymatch = windowsize >= 8192 ? MAX_SUPPORTED_DEFLATE_LENGTH : 64; + + unsigned usezeros = 1; /*not sure if setting it to false for windowsize < 8192 is better or worse*/ + unsigned numzeros = 0; + + unsigned offset; /*the offset represents the distance in LZ77 terminology*/ + unsigned length; + unsigned lazy = 0; + unsigned lazylength = 0, lazyoffset = 0; + unsigned hashval; + unsigned current_offset, current_length; + unsigned prev_offset; + const unsigned char *lastptr, *foreptr, *backptr; + unsigned hashpos; + + if(windowsize == 0 || windowsize > 32768) return 60; /*error: windowsize smaller/larger than allowed*/ + if((windowsize & (windowsize - 1)) != 0) return 90; /*error: must be power of two*/ + + if(nicematch > MAX_SUPPORTED_DEFLATE_LENGTH) nicematch = MAX_SUPPORTED_DEFLATE_LENGTH; + + for(pos = inpos; pos < insize; ++pos) + { + size_t wpos = pos & (windowsize - 1); /*position for in 'circular' hash buffers*/ + unsigned chainlength = 0; + + hashval = getHash(in, insize, pos); + + if(usezeros && hashval == 0) + { + if(numzeros == 0) numzeros = countZeros(in, insize, pos); + else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; + } + else + { + numzeros = 0; + } + + updateHashChain(hash, wpos, hashval, numzeros); + + /*the length and offset found for the current position*/ + length = 0; + offset = 0; + + hashpos = hash->chain[wpos]; + + lastptr = &in[insize < pos + MAX_SUPPORTED_DEFLATE_LENGTH ? insize : pos + MAX_SUPPORTED_DEFLATE_LENGTH]; + + /*search for the longest string*/ + prev_offset = 0; + for(;;) + { + if(chainlength++ >= maxchainlength) break; + current_offset = (unsigned)(hashpos <= wpos ? wpos - hashpos : wpos - hashpos + windowsize); + + if(current_offset < prev_offset) break; /*stop when went completely around the circular buffer*/ + prev_offset = current_offset; + if(current_offset > 0) + { + /*test the next characters*/ + foreptr = &in[pos]; + backptr = &in[pos - current_offset]; + + /*common case in PNGs is lots of zeros. Quickly skip over them as a speedup*/ + if(numzeros >= 3) + { + unsigned skip = hash->zeros[hashpos]; + if(skip > numzeros) skip = numzeros; + backptr += skip; + foreptr += skip; + } + + while(foreptr != lastptr && *backptr == *foreptr) /*maximum supported length by deflate is max length*/ + { + ++backptr; + ++foreptr; + } + current_length = (unsigned)(foreptr - &in[pos]); + + if(current_length > length) + { + length = current_length; /*the longest length*/ + offset = current_offset; /*the offset that is related to this longest length*/ + /*jump out once a length of max length is found (speed gain). This also jumps + out if length is MAX_SUPPORTED_DEFLATE_LENGTH*/ + if(current_length >= nicematch) break; + } + } + + if(hashpos == hash->chain[hashpos]) break; + + if(numzeros >= 3 && length > numzeros) + { + hashpos = hash->chainz[hashpos]; + if(hash->zeros[hashpos] != numzeros) break; + } + else + { + hashpos = hash->chain[hashpos]; + /*outdated hash value, happens if particular value was not encountered in whole last window*/ + if(hash->val[hashpos] != (int)hashval) break; + } + } + + if(lazymatching) + { + if(!lazy && length >= 3 && length <= maxlazymatch && length < MAX_SUPPORTED_DEFLATE_LENGTH) + { + lazy = 1; + lazylength = length; + lazyoffset = offset; + continue; /*try the next byte*/ + } + if(lazy) + { + lazy = 0; + if(pos == 0) ERROR_BREAK(81); + if(length > lazylength + 1) + { + /*push the previous character as literal*/ + if(!uivector_push_back(out, in[pos - 1])) ERROR_BREAK(83 /*alloc fail*/); + } + else + { + length = lazylength; + offset = lazyoffset; + hash->head[hashval] = -1; /*the same hashchain update will be done, this ensures no wrong alteration*/ + hash->headz[numzeros] = -1; /*idem*/ + --pos; + } + } + } + if(length >= 3 && offset > windowsize) ERROR_BREAK(86 /*too big (or overflown negative) offset*/); + + /*encode it as length/distance pair or literal value*/ + if(length < 3) /*only lengths of 3 or higher are supported as length/distance pair*/ + { + if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/); + } + else if(length < minmatch || (length == 3 && offset > 4096)) + { + /*compensate for the fact that longer offsets have more extra bits, a + length of only 3 may be not worth it then*/ + if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/); + } + else + { + addLengthDistance(out, length, offset); + for(i = 1; i < length; ++i) + { + ++pos; + wpos = pos & (windowsize - 1); + hashval = getHash(in, insize, pos); + if(usezeros && hashval == 0) + { + if(numzeros == 0) numzeros = countZeros(in, insize, pos); + else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; + } + else + { + numzeros = 0; + } + updateHashChain(hash, wpos, hashval, numzeros); + } + } + } /*end of the loop through each character of input*/ + + return error; +} + +/* /////////////////////////////////////////////////////////////////////////// */ + +static unsigned deflateNoCompression(ucvector* out, const unsigned char* data, size_t datasize) +{ + /*non compressed deflate block data: 1 bit BFINAL,2 bits BTYPE,(5 bits): it jumps to start of next byte, + 2 bytes LEN, 2 bytes NLEN, LEN bytes literal DATA*/ + + size_t i, j, numdeflateblocks = (datasize + 65534) / 65535; + unsigned datapos = 0; + for(i = 0; i != numdeflateblocks; ++i) + { + unsigned BFINAL, BTYPE, LEN, NLEN; + unsigned char firstbyte; + + BFINAL = (i == numdeflateblocks - 1); + BTYPE = 0; + + firstbyte = (unsigned char)(BFINAL + ((BTYPE & 1) << 1) + ((BTYPE & 2) << 1)); + ucvector_push_back(out, firstbyte); + + LEN = 65535; + if(datasize - datapos < 65535) LEN = (unsigned)datasize - datapos; + NLEN = 65535 - LEN; + + ucvector_push_back(out, (unsigned char)(LEN & 255)); + ucvector_push_back(out, (unsigned char)(LEN >> 8)); + ucvector_push_back(out, (unsigned char)(NLEN & 255)); + ucvector_push_back(out, (unsigned char)(NLEN >> 8)); + + /*Decompressed data*/ + for(j = 0; j < 65535 && datapos < datasize; ++j) + { + ucvector_push_back(out, data[datapos++]); + } + } + + return 0; +} + +/* +write the lz77-encoded data, which has lit, len and dist codes, to compressed stream using huffman trees. +tree_ll: the tree for lit and len codes. +tree_d: the tree for distance codes. +*/ +static void writeLZ77data(size_t* bp, ucvector* out, const uivector* lz77_encoded, + const HuffmanTree* tree_ll, const HuffmanTree* tree_d) +{ + size_t i = 0; + for(i = 0; i != lz77_encoded->size; ++i) + { + unsigned val = lz77_encoded->data[i]; + addHuffmanSymbol(bp, out, HuffmanTree_getCode(tree_ll, val), HuffmanTree_getLength(tree_ll, val)); + if(val > 256) /*for a length code, 3 more things have to be added*/ + { + unsigned length_index = val - FIRST_LENGTH_CODE_INDEX; + unsigned n_length_extra_bits = LENGTHEXTRA[length_index]; + unsigned length_extra_bits = lz77_encoded->data[++i]; + + unsigned distance_code = lz77_encoded->data[++i]; + + unsigned distance_index = distance_code; + unsigned n_distance_extra_bits = DISTANCEEXTRA[distance_index]; + unsigned distance_extra_bits = lz77_encoded->data[++i]; + + addBitsToStream(bp, out, length_extra_bits, n_length_extra_bits); + addHuffmanSymbol(bp, out, HuffmanTree_getCode(tree_d, distance_code), + HuffmanTree_getLength(tree_d, distance_code)); + addBitsToStream(bp, out, distance_extra_bits, n_distance_extra_bits); + } + } +} + +/*Deflate for a block of type "dynamic", that is, with freely, optimally, created huffman trees*/ +static unsigned deflateDynamic(ucvector* out, size_t* bp, Hash* hash, + const unsigned char* data, size_t datapos, size_t dataend, + const LodePNGCompressSettings* settings, unsigned final) +{ + unsigned error = 0; + + /* + A block is compressed as follows: The PNG data is lz77 encoded, resulting in + literal bytes and length/distance pairs. This is then huffman compressed with + two huffman trees. One huffman tree is used for the lit and len values ("ll"), + another huffman tree is used for the dist values ("d"). These two trees are + stored using their code lengths, and to compress even more these code lengths + are also run-length encoded and huffman compressed. This gives a huffman tree + of code lengths "cl". The code lenghts used to describe this third tree are + the code length code lengths ("clcl"). + */ + + /*The lz77 encoded data, represented with integers since there will also be length and distance codes in it*/ + uivector lz77_encoded; + HuffmanTree tree_ll; /*tree for lit,len values*/ + HuffmanTree tree_d; /*tree for distance codes*/ + HuffmanTree tree_cl; /*tree for encoding the code lengths representing tree_ll and tree_d*/ + uivector frequencies_ll; /*frequency of lit,len codes*/ + uivector frequencies_d; /*frequency of dist codes*/ + uivector frequencies_cl; /*frequency of code length codes*/ + uivector bitlen_lld; /*lit,len,dist code lenghts (int bits), literally (without repeat codes).*/ + uivector bitlen_lld_e; /*bitlen_lld encoded with repeat codes (this is a rudemtary run length compression)*/ + /*bitlen_cl is the code length code lengths ("clcl"). The bit lengths of codes to represent tree_cl + (these are written as is in the file, it would be crazy to compress these using yet another huffman + tree that needs to be represented by yet another set of code lengths)*/ + uivector bitlen_cl; + size_t datasize = dataend - datapos; + + /* + Due to the huffman compression of huffman tree representations ("two levels"), there are some anologies: + bitlen_lld is to tree_cl what data is to tree_ll and tree_d. + bitlen_lld_e is to bitlen_lld what lz77_encoded is to data. + bitlen_cl is to bitlen_lld_e what bitlen_lld is to lz77_encoded. + */ + + unsigned BFINAL = final; + size_t numcodes_ll, numcodes_d, i; + unsigned HLIT, HDIST, HCLEN; + + uivector_init(&lz77_encoded); + HuffmanTree_init(&tree_ll); + HuffmanTree_init(&tree_d); + HuffmanTree_init(&tree_cl); + uivector_init(&frequencies_ll); + uivector_init(&frequencies_d); + uivector_init(&frequencies_cl); + uivector_init(&bitlen_lld); + uivector_init(&bitlen_lld_e); + uivector_init(&bitlen_cl); + + /*This while loop never loops due to a break at the end, it is here to + allow breaking out of it to the cleanup phase on error conditions.*/ + while(!error) + { + if(settings->use_lz77) + { + error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, + settings->minmatch, settings->nicematch, settings->lazymatching); + if(error) break; + } + else + { + if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83 /*alloc fail*/); + for(i = datapos; i < dataend; ++i) lz77_encoded.data[i - datapos] = data[i]; /*no LZ77, but still will be Huffman compressed*/ + } + + if(!uivector_resizev(&frequencies_ll, 286, 0)) ERROR_BREAK(83 /*alloc fail*/); + if(!uivector_resizev(&frequencies_d, 30, 0)) ERROR_BREAK(83 /*alloc fail*/); + + /*Count the frequencies of lit, len and dist codes*/ + for(i = 0; i != lz77_encoded.size; ++i) + { + unsigned symbol = lz77_encoded.data[i]; + ++frequencies_ll.data[symbol]; + if(symbol > 256) + { + unsigned dist = lz77_encoded.data[i + 2]; + ++frequencies_d.data[dist]; + i += 3; + } + } + frequencies_ll.data[256] = 1; /*there will be exactly 1 end code, at the end of the block*/ + + /*Make both huffman trees, one for the lit and len codes, one for the dist codes*/ + error = HuffmanTree_makeFromFrequencies(&tree_ll, frequencies_ll.data, 257, frequencies_ll.size, 15); + if(error) break; + /*2, not 1, is chosen for mincodes: some buggy PNG decoders require at least 2 symbols in the dist tree*/ + error = HuffmanTree_makeFromFrequencies(&tree_d, frequencies_d.data, 2, frequencies_d.size, 15); + if(error) break; + + numcodes_ll = tree_ll.numcodes; if(numcodes_ll > 286) numcodes_ll = 286; + numcodes_d = tree_d.numcodes; if(numcodes_d > 30) numcodes_d = 30; + /*store the code lengths of both generated trees in bitlen_lld*/ + for(i = 0; i != numcodes_ll; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_ll, (unsigned)i)); + for(i = 0; i != numcodes_d; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_d, (unsigned)i)); + + /*run-length compress bitlen_ldd into bitlen_lld_e by using repeat codes 16 (copy length 3-6 times), + 17 (3-10 zeroes), 18 (11-138 zeroes)*/ + for(i = 0; i != (unsigned)bitlen_lld.size; ++i) + { + unsigned j = 0; /*amount of repititions*/ + while(i + j + 1 < (unsigned)bitlen_lld.size && bitlen_lld.data[i + j + 1] == bitlen_lld.data[i]) ++j; + + if(bitlen_lld.data[i] == 0 && j >= 2) /*repeat code for zeroes*/ + { + ++j; /*include the first zero*/ + if(j <= 10) /*repeat code 17 supports max 10 zeroes*/ + { + uivector_push_back(&bitlen_lld_e, 17); + uivector_push_back(&bitlen_lld_e, j - 3); + } + else /*repeat code 18 supports max 138 zeroes*/ + { + if(j > 138) j = 138; + uivector_push_back(&bitlen_lld_e, 18); + uivector_push_back(&bitlen_lld_e, j - 11); + } + i += (j - 1); + } + else if(j >= 3) /*repeat code for value other than zero*/ + { + size_t k; + unsigned num = j / 6, rest = j % 6; + uivector_push_back(&bitlen_lld_e, bitlen_lld.data[i]); + for(k = 0; k < num; ++k) + { + uivector_push_back(&bitlen_lld_e, 16); + uivector_push_back(&bitlen_lld_e, 6 - 3); + } + if(rest >= 3) + { + uivector_push_back(&bitlen_lld_e, 16); + uivector_push_back(&bitlen_lld_e, rest - 3); + } + else j -= rest; + i += j; + } + else /*too short to benefit from repeat code*/ + { + uivector_push_back(&bitlen_lld_e, bitlen_lld.data[i]); + } + } + + /*generate tree_cl, the huffmantree of huffmantrees*/ + + if(!uivector_resizev(&frequencies_cl, NUM_CODE_LENGTH_CODES, 0)) ERROR_BREAK(83 /*alloc fail*/); + for(i = 0; i != bitlen_lld_e.size; ++i) + { + ++frequencies_cl.data[bitlen_lld_e.data[i]]; + /*after a repeat code come the bits that specify the number of repetitions, + those don't need to be in the frequencies_cl calculation*/ + if(bitlen_lld_e.data[i] >= 16) ++i; + } + + error = HuffmanTree_makeFromFrequencies(&tree_cl, frequencies_cl.data, + frequencies_cl.size, frequencies_cl.size, 7); + if(error) break; + + if(!uivector_resize(&bitlen_cl, tree_cl.numcodes)) ERROR_BREAK(83 /*alloc fail*/); + for(i = 0; i != tree_cl.numcodes; ++i) + { + /*lenghts of code length tree is in the order as specified by deflate*/ + bitlen_cl.data[i] = HuffmanTree_getLength(&tree_cl, CLCL_ORDER[i]); + } + while(bitlen_cl.data[bitlen_cl.size - 1] == 0 && bitlen_cl.size > 4) + { + /*remove zeros at the end, but minimum size must be 4*/ + if(!uivector_resize(&bitlen_cl, bitlen_cl.size - 1)) ERROR_BREAK(83 /*alloc fail*/); + } + if(error) break; + + /* + Write everything into the output + + After the BFINAL and BTYPE, the dynamic block consists out of the following: + - 5 bits HLIT, 5 bits HDIST, 4 bits HCLEN + - (HCLEN+4)*3 bits code lengths of code length alphabet + - HLIT + 257 code lenghts of lit/length alphabet (encoded using the code length + alphabet, + possible repetition codes 16, 17, 18) + - HDIST + 1 code lengths of distance alphabet (encoded using the code length + alphabet, + possible repetition codes 16, 17, 18) + - compressed data + - 256 (end code) + */ + + /*Write block type*/ + addBitToStream(bp, out, BFINAL); + addBitToStream(bp, out, 0); /*first bit of BTYPE "dynamic"*/ + addBitToStream(bp, out, 1); /*second bit of BTYPE "dynamic"*/ + + /*write the HLIT, HDIST and HCLEN values*/ + HLIT = (unsigned)(numcodes_ll - 257); + HDIST = (unsigned)(numcodes_d - 1); + HCLEN = (unsigned)bitlen_cl.size - 4; + /*trim zeroes for HCLEN. HLIT and HDIST were already trimmed at tree creation*/ + while(!bitlen_cl.data[HCLEN + 4 - 1] && HCLEN > 0) --HCLEN; + addBitsToStream(bp, out, HLIT, 5); + addBitsToStream(bp, out, HDIST, 5); + addBitsToStream(bp, out, HCLEN, 4); + + /*write the code lenghts of the code length alphabet*/ + for(i = 0; i != HCLEN + 4; ++i) addBitsToStream(bp, out, bitlen_cl.data[i], 3); + + /*write the lenghts of the lit/len AND the dist alphabet*/ + for(i = 0; i != bitlen_lld_e.size; ++i) + { + addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_cl, bitlen_lld_e.data[i]), + HuffmanTree_getLength(&tree_cl, bitlen_lld_e.data[i])); + /*extra bits of repeat codes*/ + if(bitlen_lld_e.data[i] == 16) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 2); + else if(bitlen_lld_e.data[i] == 17) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 3); + else if(bitlen_lld_e.data[i] == 18) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 7); + } + + /*write the compressed data symbols*/ + writeLZ77data(bp, out, &lz77_encoded, &tree_ll, &tree_d); + /*error: the length of the end code 256 must be larger than 0*/ + if(HuffmanTree_getLength(&tree_ll, 256) == 0) ERROR_BREAK(64); + + /*write the end code*/ + addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, 256), HuffmanTree_getLength(&tree_ll, 256)); + + break; /*end of error-while*/ + } + + /*cleanup*/ + uivector_cleanup(&lz77_encoded); + HuffmanTree_cleanup(&tree_ll); + HuffmanTree_cleanup(&tree_d); + HuffmanTree_cleanup(&tree_cl); + uivector_cleanup(&frequencies_ll); + uivector_cleanup(&frequencies_d); + uivector_cleanup(&frequencies_cl); + uivector_cleanup(&bitlen_lld_e); + uivector_cleanup(&bitlen_lld); + uivector_cleanup(&bitlen_cl); + + return error; +} + +static unsigned deflateFixed(ucvector* out, size_t* bp, Hash* hash, + const unsigned char* data, + size_t datapos, size_t dataend, + const LodePNGCompressSettings* settings, unsigned final) +{ + HuffmanTree tree_ll; /*tree for literal values and length codes*/ + HuffmanTree tree_d; /*tree for distance codes*/ + + unsigned BFINAL = final; + unsigned error = 0; + size_t i; + + HuffmanTree_init(&tree_ll); + HuffmanTree_init(&tree_d); + + generateFixedLitLenTree(&tree_ll); + generateFixedDistanceTree(&tree_d); + + addBitToStream(bp, out, BFINAL); + addBitToStream(bp, out, 1); /*first bit of BTYPE*/ + addBitToStream(bp, out, 0); /*second bit of BTYPE*/ + + if(settings->use_lz77) /*LZ77 encoded*/ + { + uivector lz77_encoded; + uivector_init(&lz77_encoded); + error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, + settings->minmatch, settings->nicematch, settings->lazymatching); + if(!error) writeLZ77data(bp, out, &lz77_encoded, &tree_ll, &tree_d); + uivector_cleanup(&lz77_encoded); + } + else /*no LZ77, but still will be Huffman compressed*/ + { + for(i = datapos; i < dataend; ++i) + { + addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, data[i]), HuffmanTree_getLength(&tree_ll, data[i])); + } + } + /*add END code*/ + if(!error) addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, 256), HuffmanTree_getLength(&tree_ll, 256)); + + /*cleanup*/ + HuffmanTree_cleanup(&tree_ll); + HuffmanTree_cleanup(&tree_d); + + return error; +} + +static unsigned lodepng_deflatev(ucvector* out, const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings) +{ + unsigned error = 0; + size_t i, blocksize, numdeflateblocks; + size_t bp = 0; /*the bit pointer*/ + Hash hash; + + if(settings->btype > 2) return 61; + else if(settings->btype == 0) return deflateNoCompression(out, in, insize); + else if(settings->btype == 1) blocksize = insize; + else /*if(settings->btype == 2)*/ + { + /*on PNGs, deflate blocks of 65-262k seem to give most dense encoding*/ + blocksize = insize / 8 + 8; + if(blocksize < 65536) blocksize = 65536; + if(blocksize > 262144) blocksize = 262144; + } + + numdeflateblocks = (insize + blocksize - 1) / blocksize; + if(numdeflateblocks == 0) numdeflateblocks = 1; + + error = hash_init(&hash, settings->windowsize); + if(error) return error; + + for(i = 0; i != numdeflateblocks && !error; ++i) + { + unsigned final = (i == numdeflateblocks - 1); + size_t start = i * blocksize; + size_t end = start + blocksize; + if(end > insize) end = insize; + + if(settings->btype == 1) error = deflateFixed(out, &bp, &hash, in, start, end, settings, final); + else if(settings->btype == 2) error = deflateDynamic(out, &bp, &hash, in, start, end, settings, final); + } + + hash_cleanup(&hash); + + return error; +} + +unsigned lodepng_deflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings) +{ + unsigned error; + ucvector v; + ucvector_init_buffer(&v, *out, *outsize); + error = lodepng_deflatev(&v, in, insize, settings); + *out = v.data; + *outsize = v.size; + return error; +} + +static unsigned deflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings) +{ + if(settings->custom_deflate) + { + return settings->custom_deflate(out, outsize, in, insize, settings); + } + else + { + return lodepng_deflate(out, outsize, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Adler32 */ +/* ////////////////////////////////////////////////////////////////////////// */ + +static unsigned update_adler32(unsigned adler, const unsigned char* data, unsigned len) +{ + unsigned s1 = adler & 0xffff; + unsigned s2 = (adler >> 16) & 0xffff; + + while(len > 0) + { + /*at least 5550 sums can be done before the sums overflow, saving a lot of module divisions*/ + unsigned amount = len > 5550 ? 5550 : len; + len -= amount; + while(amount > 0) + { + s1 += (*data++); + s2 += s1; + --amount; + } + s1 %= 65521; + s2 %= 65521; + } + + return (s2 << 16) | s1; +} + +/*Return the adler32 of the bytes data[0..len-1]*/ +static unsigned adler32(const unsigned char* data, unsigned len) +{ + return update_adler32(1L, data, len); +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Zlib / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_DECODER + +unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGDecompressSettings* settings) +{ + unsigned error = 0; + unsigned CM, CINFO, FDICT; + + if(insize < 2) return 53; /*error, size of zlib data too small*/ + /*read information from zlib header*/ + if((in[0] * 256 + in[1]) % 31 != 0) + { + /*error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way*/ + return 24; + } + + CM = in[0] & 15; + CINFO = (in[0] >> 4) & 15; + /*FCHECK = in[1] & 31;*/ /*FCHECK is already tested above*/ + FDICT = (in[1] >> 5) & 1; + /*FLEVEL = (in[1] >> 6) & 3;*/ /*FLEVEL is not used here*/ + + if(CM != 8 || CINFO > 7) + { + /*error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec*/ + return 25; + } + if(FDICT != 0) + { + /*error: the specification of PNG says about the zlib stream: + "The additional flags shall not specify a preset dictionary."*/ + return 26; + } + + error = inflate(out, outsize, in + 2, insize - 2, settings); + if(error) return error; + + if(!settings->ignore_adler32) + { + unsigned ADLER32 = lodepng_read32bitInt(&in[insize - 4]); + unsigned checksum = adler32(*out, (unsigned)(*outsize)); + if(checksum != ADLER32) return 58; /*error, adler checksum not correct, data must be corrupted*/ + } + + return 0; /*no error*/ +} + +static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGDecompressSettings* settings) +{ + if(settings->custom_zlib) + { + return settings->custom_zlib(out, outsize, in, insize, settings); + } + else + { + return lodepng_zlib_decompress(out, outsize, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER + +unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGCompressSettings* settings) +{ + /*initially, *out must be NULL and outsize 0, if you just give some random *out + that's pointing to a non allocated buffer, this'll crash*/ + ucvector outv; + size_t i; + unsigned error; + unsigned char* deflatedata = 0; + size_t deflatesize = 0; + + /*zlib data: 1 byte CMF (CM+CINFO), 1 byte FLG, deflate data, 4 byte ADLER32 checksum of the Decompressed data*/ + unsigned CMF = 120; /*0b01111000: CM 8, CINFO 7. With CINFO 7, any window size up to 32768 can be used.*/ + unsigned FLEVEL = 0; + unsigned FDICT = 0; + unsigned CMFFLG = 256 * CMF + FDICT * 32 + FLEVEL * 64; + unsigned FCHECK = 31 - CMFFLG % 31; + CMFFLG += FCHECK; + + /*ucvector-controlled version of the output buffer, for dynamic array*/ + ucvector_init_buffer(&outv, *out, *outsize); + + ucvector_push_back(&outv, (unsigned char)(CMFFLG >> 8)); + ucvector_push_back(&outv, (unsigned char)(CMFFLG & 255)); + + error = deflate(&deflatedata, &deflatesize, in, insize, settings); + + if(!error) + { + unsigned ADLER32 = adler32(in, (unsigned)insize); + for(i = 0; i != deflatesize; ++i) ucvector_push_back(&outv, deflatedata[i]); + lodepng_free(deflatedata); + lodepng_add32bitInt(&outv, ADLER32); + } + + *out = outv.data; + *outsize = outv.size; + + return error; +} + +/* compress using the default or custom zlib function */ +static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGCompressSettings* settings) +{ + if(settings->custom_zlib) + { + return settings->custom_zlib(out, outsize, in, insize, settings); + } + else + { + return lodepng_zlib_compress(out, outsize, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#else /*no LODEPNG_COMPILE_ZLIB*/ + +#ifdef LODEPNG_COMPILE_DECODER +static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGDecompressSettings* settings) +{ + if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ + return settings->custom_zlib(out, outsize, in, insize, settings); +} +#endif /*LODEPNG_COMPILE_DECODER*/ +#ifdef LODEPNG_COMPILE_ENCODER +static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGCompressSettings* settings) +{ + if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ + return settings->custom_zlib(out, outsize, in, insize, settings); +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#endif /*LODEPNG_COMPILE_ZLIB*/ + +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_ENCODER + +/*this is a good tradeoff between speed and compression ratio*/ +#define DEFAULT_WINDOWSIZE 2048 + +void lodepng_compress_settings_init(LodePNGCompressSettings* settings) +{ + /*compress with dynamic huffman tree (not in the mathematical sense, just not the predefined one)*/ + settings->btype = 2; + settings->use_lz77 = 1; + settings->windowsize = DEFAULT_WINDOWSIZE; + settings->minmatch = 3; + settings->nicematch = 128; + settings->lazymatching = 1; + + settings->custom_zlib = 0; + settings->custom_deflate = 0; + settings->custom_context = 0; +} + +const LodePNGCompressSettings lodepng_default_compress_settings = {2, 1, DEFAULT_WINDOWSIZE, 3, 128, 1, 0, 0, 0}; + + +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_DECODER + +void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) +{ + settings->ignore_adler32 = 0; + + settings->custom_zlib = 0; + settings->custom_inflate = 0; + settings->custom_context = 0; +} + +const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0}; + +#endif /*LODEPNG_COMPILE_DECODER*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // End of Zlib related code. Begin of PNG related code. // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_PNG + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / CRC32 / */ +/* ////////////////////////////////////////////////////////////////////////// */ + + +#ifndef LODEPNG_NO_COMPILE_CRC +/* CRC polynomial: 0xedb88320 */ +static unsigned lodepng_crc32_table[256] = { + 0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u, 3915621685u, 2657392035u, + 249268274u, 2044508324u, 3772115230u, 2547177864u, 162941995u, 2125561021u, 3887607047u, 2428444049u, + 498536548u, 1789927666u, 4089016648u, 2227061214u, 450548861u, 1843258603u, 4107580753u, 2211677639u, + 325883990u, 1684777152u, 4251122042u, 2321926636u, 335633487u, 1661365465u, 4195302755u, 2366115317u, + 997073096u, 1281953886u, 3579855332u, 2724688242u, 1006888145u, 1258607687u, 3524101629u, 2768942443u, + 901097722u, 1119000684u, 3686517206u, 2898065728u, 853044451u, 1172266101u, 3705015759u, 2882616665u, + 651767980u, 1373503546u, 3369554304u, 3218104598u, 565507253u, 1454621731u, 3485111705u, 3099436303u, + 671266974u, 1594198024u, 3322730930u, 2970347812u, 795835527u, 1483230225u, 3244367275u, 3060149565u, + 1994146192u, 31158534u, 2563907772u, 4023717930u, 1907459465u, 112637215u, 2680153253u, 3904427059u, + 2013776290u, 251722036u, 2517215374u, 3775830040u, 2137656763u, 141376813u, 2439277719u, 3865271297u, + 1802195444u, 476864866u, 2238001368u, 4066508878u, 1812370925u, 453092731u, 2181625025u, 4111451223u, + 1706088902u, 314042704u, 2344532202u, 4240017532u, 1658658271u, 366619977u, 2362670323u, 4224994405u, + 1303535960u, 984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u, 2765210733u, 3554079995u, + 1131014506u, 879679996u, 2909243462u, 3663771856u, 1141124467u, 855842277u, 2852801631u, 3708648649u, + 1342533948u, 654459306u, 3188396048u, 3373015174u, 1466479909u, 544179635u, 3110523913u, 3462522015u, + 1591671054u, 702138776u, 2966460450u, 3352799412u, 1504918807u, 783551873u, 3082640443u, 3233442989u, + 3988292384u, 2596254646u, 62317068u, 1957810842u, 3939845945u, 2647816111u, 81470997u, 1943803523u, + 3814918930u, 2489596804u, 225274430u, 2053790376u, 3826175755u, 2466906013u, 167816743u, 2097651377u, + 4027552580u, 2265490386u, 503444072u, 1762050814u, 4150417245u, 2154129355u, 426522225u, 1852507879u, + 4275313526u, 2312317920u, 282753626u, 1742555852u, 4189708143u, 2394877945u, 397917763u, 1622183637u, + 3604390888u, 2714866558u, 953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u, + 3624741850u, 2936675148u, 906185462u, 1090812512u, 3747672003u, 2825379669u, 829329135u, 1181335161u, + 3412177804u, 3160834842u, 628085408u, 1382605366u, 3423369109u, 3138078467u, 570562233u, 1426400815u, + 3317316542u, 2998733608u, 733239954u, 1555261956u, 3268935591u, 3050360625u, 752459403u, 1541320221u, + 2607071920u, 3965973030u, 1969922972u, 40735498u, 2617837225u, 3943577151u, 1913087877u, 83908371u, + 2512341634u, 3803740692u, 2075208622u, 213261112u, 2463272603u, 3855990285u, 2094854071u, 198958881u, + 2262029012u, 4057260610u, 1759359992u, 534414190u, 2176718541u, 4139329115u, 1873836001u, 414664567u, + 2282248934u, 4279200368u, 1711684554u, 285281116u, 2405801727u, 4167216745u, 1634467795u, 376229701u, + 2685067896u, 3608007406u, 1308918612u, 956543938u, 2808555105u, 3495958263u, 1231636301u, 1047427035u, + 2932959818u, 3654703836u, 1088359270u, 936918000u, 2847714899u, 3736837829u, 1202900863u, 817233897u, + 3183342108u, 3401237130u, 1404277552u, 615818150u, 3134207493u, 3453421203u, 1423857449u, 601450431u, + 3009837614u, 3294710456u, 1567103746u, 711928724u, 3020668471u, 3272380065u, 1510334235u, 755167117u +}; + +/*Return the CRC of the bytes buf[0..len-1].*/ +unsigned lodepng_crc32(const unsigned char* data, size_t length) +{ + unsigned r = 0xffffffffu; + size_t i; + for(i = 0; i < length; ++i) + { + r = lodepng_crc32_table[(r ^ data[i]) & 0xff] ^ (r >> 8); + } + return r ^ 0xffffffffu; +} +#else /* !LODEPNG_NO_COMPILE_CRC */ +unsigned lodepng_crc32(const unsigned char* data, size_t length); +#endif /* !LODEPNG_NO_COMPILE_CRC */ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Reading and writing single bits and bytes from/to stream for LodePNG / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream) +{ + unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1); + ++(*bitpointer); + return result; +} + +static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) +{ + unsigned result = 0; + size_t i; + for(i = 0 ; i < nbits; ++i) + { + result <<= 1; + result |= (unsigned)readBitFromReversedStream(bitpointer, bitstream); + } + return result; +} + +#ifdef LODEPNG_COMPILE_DECODER +static void setBitOfReversedStream0(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) +{ + /*the current bit in bitstream must be 0 for this to work*/ + if(bit) + { + /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/ + bitstream[(*bitpointer) >> 3] |= (bit << (7 - ((*bitpointer) & 0x7))); + } + ++(*bitpointer); +} +#endif /*LODEPNG_COMPILE_DECODER*/ + +static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) +{ + /*the current bit in bitstream may be 0 or 1 for this to work*/ + if(bit == 0) bitstream[(*bitpointer) >> 3] &= (unsigned char)(~(1 << (7 - ((*bitpointer) & 0x7)))); + else bitstream[(*bitpointer) >> 3] |= (1 << (7 - ((*bitpointer) & 0x7))); + ++(*bitpointer); +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / PNG chunks / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +unsigned lodepng_chunk_length(const unsigned char* chunk) +{ + return lodepng_read32bitInt(&chunk[0]); +} + +void lodepng_chunk_type(char type[5], const unsigned char* chunk) +{ + unsigned i; + for(i = 0; i != 4; ++i) type[i] = (char)chunk[4 + i]; + type[4] = 0; /*null termination char*/ +} + +unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type) +{ + if(strlen(type) != 4) return 0; + return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]); +} + +unsigned char lodepng_chunk_ancillary(const unsigned char* chunk) +{ + return((chunk[4] & 32) != 0); +} + +unsigned char lodepng_chunk_private(const unsigned char* chunk) +{ + return((chunk[6] & 32) != 0); +} + +unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk) +{ + return((chunk[7] & 32) != 0); +} + +unsigned char* lodepng_chunk_data(unsigned char* chunk) +{ + return &chunk[8]; +} + +const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk) +{ + return &chunk[8]; +} + +unsigned lodepng_chunk_check_crc(const unsigned char* chunk) +{ + unsigned length = lodepng_chunk_length(chunk); + unsigned CRC = lodepng_read32bitInt(&chunk[length + 8]); + /*the CRC is taken of the data and the 4 chunk type letters, not the length*/ + unsigned checksum = lodepng_crc32(&chunk[4], length + 4); + if(CRC != checksum) return 1; + else return 0; +} + +void lodepng_chunk_generate_crc(unsigned char* chunk) +{ + unsigned length = lodepng_chunk_length(chunk); + unsigned CRC = lodepng_crc32(&chunk[4], length + 4); + lodepng_set32bitInt(chunk + 8 + length, CRC); +} + +unsigned char* lodepng_chunk_next(unsigned char* chunk) +{ + unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; + return &chunk[total_chunk_length]; +} + +const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk) +{ + unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; + return &chunk[total_chunk_length]; +} + +unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk) +{ + unsigned i; + unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; + unsigned char *chunk_start, *new_buffer; + size_t new_length = (*outlength) + total_chunk_length; + if(new_length < total_chunk_length || new_length < (*outlength)) return 77; /*integer overflow happened*/ + + new_buffer = (unsigned char*)lodepng_realloc(*out, new_length); + if(!new_buffer) return 83; /*alloc fail*/ + (*out) = new_buffer; + (*outlength) = new_length; + chunk_start = &(*out)[new_length - total_chunk_length]; + + for(i = 0; i != total_chunk_length; ++i) chunk_start[i] = chunk[i]; + + return 0; +} + +unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, + const char* type, const unsigned char* data) +{ + unsigned i; + unsigned char *chunk, *new_buffer; + size_t new_length = (*outlength) + length + 12; + if(new_length < length + 12 || new_length < (*outlength)) return 77; /*integer overflow happened*/ + new_buffer = (unsigned char*)lodepng_realloc(*out, new_length); + if(!new_buffer) return 83; /*alloc fail*/ + (*out) = new_buffer; + (*outlength) = new_length; + chunk = &(*out)[(*outlength) - length - 12]; + + /*1: length*/ + lodepng_set32bitInt(chunk, (unsigned)length); + + /*2: chunk name (4 letters)*/ + chunk[4] = (unsigned char)type[0]; + chunk[5] = (unsigned char)type[1]; + chunk[6] = (unsigned char)type[2]; + chunk[7] = (unsigned char)type[3]; + + /*3: the data*/ + for(i = 0; i != length; ++i) chunk[8 + i] = data[i]; + + /*4: CRC (of the chunkname characters and the data)*/ + lodepng_chunk_generate_crc(chunk); + + return 0; +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Color types and such / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*return type is a LodePNG error code*/ +static unsigned checkColorValidity(LodePNGColorType colortype, unsigned bd) /*bd = bitdepth*/ +{ + switch(colortype) + { + case 0: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break; /*grey*/ + case 2: if(!( bd == 8 || bd == 16)) return 37; break; /*RGB*/ + case 3: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break; /*palette*/ + case 4: if(!( bd == 8 || bd == 16)) return 37; break; /*grey + alpha*/ + case 6: if(!( bd == 8 || bd == 16)) return 37; break; /*RGBA*/ + default: return 31; + } + return 0; /*allowed color type / bits combination*/ +} + +static unsigned getNumColorChannels(LodePNGColorType colortype) +{ + switch(colortype) + { + case 0: return 1; /*grey*/ + case 2: return 3; /*RGB*/ + case 3: return 1; /*palette*/ + case 4: return 2; /*grey + alpha*/ + case 6: return 4; /*RGBA*/ + } + return 0; /*unexisting color type*/ +} + +static unsigned lodepng_get_bpp_lct(LodePNGColorType colortype, unsigned bitdepth) +{ + /*bits per pixel is amount of channels * bits per channel*/ + return getNumColorChannels(colortype) * bitdepth; +} + +/* ////////////////////////////////////////////////////////////////////////// */ + +void lodepng_color_mode_init(LodePNGColorMode* info) +{ + info->key_defined = 0; + info->key_r = info->key_g = info->key_b = 0; + info->colortype = LCT_RGBA; + info->bitdepth = 8; + info->palette = 0; + info->palettesize = 0; +} + +void lodepng_color_mode_cleanup(LodePNGColorMode* info) +{ + lodepng_palette_clear(info); +} + +unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source) +{ + size_t i; + lodepng_color_mode_cleanup(dest); + *dest = *source; + if(source->palette) + { + dest->palette = (unsigned char*)lodepng_malloc(1024); + if(!dest->palette && source->palettesize) return 83; /*alloc fail*/ + for(i = 0; i != source->palettesize * 4; ++i) dest->palette[i] = source->palette[i]; + } + return 0; +} + +static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColorMode* b) +{ + size_t i; + if(a->colortype != b->colortype) return 0; + if(a->bitdepth != b->bitdepth) return 0; + if(a->key_defined != b->key_defined) return 0; + if(a->key_defined) + { + if(a->key_r != b->key_r) return 0; + if(a->key_g != b->key_g) return 0; + if(a->key_b != b->key_b) return 0; + } + /*if one of the palette sizes is 0, then we consider it to be the same as the + other: it means that e.g. the palette was not given by the user and should be + considered the same as the palette inside the PNG.*/ + if(1/*a->palettesize != 0 && b->palettesize != 0*/) { + if(a->palettesize != b->palettesize) return 0; + for(i = 0; i != a->palettesize * 4; ++i) + { + if(a->palette[i] != b->palette[i]) return 0; + } + } + return 1; +} + +void lodepng_palette_clear(LodePNGColorMode* info) +{ + if(info->palette) lodepng_free(info->palette); + info->palette = 0; + info->palettesize = 0; +} + +unsigned lodepng_palette_add(LodePNGColorMode* info, + unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + unsigned char* data; + /*the same resize technique as C++ std::vectors is used, and here it's made so that for a palette with + the max of 256 colors, it'll have the exact alloc size*/ + if(!info->palette) /*allocate palette if empty*/ + { + /*room for 256 colors with 4 bytes each*/ + data = (unsigned char*)lodepng_realloc(info->palette, 1024); + if(!data) return 83; /*alloc fail*/ + else info->palette = data; + } + info->palette[4 * info->palettesize + 0] = r; + info->palette[4 * info->palettesize + 1] = g; + info->palette[4 * info->palettesize + 2] = b; + info->palette[4 * info->palettesize + 3] = a; + ++info->palettesize; + return 0; +} + +unsigned lodepng_get_bpp(const LodePNGColorMode* info) +{ + /*calculate bits per pixel out of colortype and bitdepth*/ + return lodepng_get_bpp_lct(info->colortype, info->bitdepth); +} + +unsigned lodepng_get_channels(const LodePNGColorMode* info) +{ + return getNumColorChannels(info->colortype); +} + +unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info) +{ + return info->colortype == LCT_GREY || info->colortype == LCT_GREY_ALPHA; +} + +unsigned lodepng_is_alpha_type(const LodePNGColorMode* info) +{ + return (info->colortype & 4) != 0; /*4 or 6*/ +} + +unsigned lodepng_is_palette_type(const LodePNGColorMode* info) +{ + return info->colortype == LCT_PALETTE; +} + +unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info) +{ + size_t i; + for(i = 0; i != info->palettesize; ++i) + { + if(info->palette[i * 4 + 3] < 255) return 1; + } + return 0; +} + +unsigned lodepng_can_have_alpha(const LodePNGColorMode* info) +{ + return info->key_defined + || lodepng_is_alpha_type(info) + || lodepng_has_palette_alpha(info); +} + +size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color) +{ + /*will not overflow for any color type if roughly w * h < 268435455*/ + size_t bpp = lodepng_get_bpp(color); + size_t n = w * h; + return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8; +} + +size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) +{ + /*will not overflow for any color type if roughly w * h < 268435455*/ + size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth); + size_t n = w * h; + return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8; +} + + +#ifdef LODEPNG_COMPILE_PNG +#ifdef LODEPNG_COMPILE_DECODER +/*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer*/ +static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, const LodePNGColorMode* color) +{ + /*will not overflow for any color type if roughly w * h < 268435455*/ + size_t bpp = lodepng_get_bpp(color); + size_t line = ((w / 8) * bpp) + ((w & 7) * bpp + 7) / 8; + return h * line; +} +#endif /*LODEPNG_COMPILE_DECODER*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + +static void LodePNGUnknownChunks_init(LodePNGInfo* info) +{ + unsigned i; + for(i = 0; i != 3; ++i) info->unknown_chunks_data[i] = 0; + for(i = 0; i != 3; ++i) info->unknown_chunks_size[i] = 0; +} + +static void LodePNGUnknownChunks_cleanup(LodePNGInfo* info) +{ + unsigned i; + for(i = 0; i != 3; ++i) lodepng_free(info->unknown_chunks_data[i]); +} + +static unsigned LodePNGUnknownChunks_copy(LodePNGInfo* dest, const LodePNGInfo* src) +{ + unsigned i; + + LodePNGUnknownChunks_cleanup(dest); + + for(i = 0; i != 3; ++i) + { + size_t j; + dest->unknown_chunks_size[i] = src->unknown_chunks_size[i]; + dest->unknown_chunks_data[i] = (unsigned char*)lodepng_malloc(src->unknown_chunks_size[i]); + if(!dest->unknown_chunks_data[i] && dest->unknown_chunks_size[i]) return 83; /*alloc fail*/ + for(j = 0; j < src->unknown_chunks_size[i]; ++j) + { + dest->unknown_chunks_data[i][j] = src->unknown_chunks_data[i][j]; + } + } + + return 0; +} + +/******************************************************************************/ + +static void LodePNGText_init(LodePNGInfo* info) +{ + info->text_num = 0; + info->text_keys = NULL; + info->text_strings = NULL; +} + +static void LodePNGText_cleanup(LodePNGInfo* info) +{ + size_t i; + for(i = 0; i != info->text_num; ++i) + { + string_cleanup(&info->text_keys[i]); + string_cleanup(&info->text_strings[i]); + } + lodepng_free(info->text_keys); + lodepng_free(info->text_strings); +} + +static unsigned LodePNGText_copy(LodePNGInfo* dest, const LodePNGInfo* source) +{ + size_t i = 0; + dest->text_keys = 0; + dest->text_strings = 0; + dest->text_num = 0; + for(i = 0; i != source->text_num; ++i) + { + CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i])); + } + return 0; +} + +void lodepng_clear_text(LodePNGInfo* info) +{ + LodePNGText_cleanup(info); +} + +unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str) +{ + char** new_keys = (char**)(lodepng_realloc(info->text_keys, sizeof(char*) * (info->text_num + 1))); + char** new_strings = (char**)(lodepng_realloc(info->text_strings, sizeof(char*) * (info->text_num + 1))); + if(!new_keys || !new_strings) + { + lodepng_free(new_keys); + lodepng_free(new_strings); + return 83; /*alloc fail*/ + } + + ++info->text_num; + info->text_keys = new_keys; + info->text_strings = new_strings; + + string_init(&info->text_keys[info->text_num - 1]); + string_set(&info->text_keys[info->text_num - 1], key); + + string_init(&info->text_strings[info->text_num - 1]); + string_set(&info->text_strings[info->text_num - 1], str); + + return 0; +} + +/******************************************************************************/ + +static void LodePNGIText_init(LodePNGInfo* info) +{ + info->itext_num = 0; + info->itext_keys = NULL; + info->itext_langtags = NULL; + info->itext_transkeys = NULL; + info->itext_strings = NULL; +} + +static void LodePNGIText_cleanup(LodePNGInfo* info) +{ + size_t i; + for(i = 0; i != info->itext_num; ++i) + { + string_cleanup(&info->itext_keys[i]); + string_cleanup(&info->itext_langtags[i]); + string_cleanup(&info->itext_transkeys[i]); + string_cleanup(&info->itext_strings[i]); + } + lodepng_free(info->itext_keys); + lodepng_free(info->itext_langtags); + lodepng_free(info->itext_transkeys); + lodepng_free(info->itext_strings); +} + +static unsigned LodePNGIText_copy(LodePNGInfo* dest, const LodePNGInfo* source) +{ + size_t i = 0; + dest->itext_keys = 0; + dest->itext_langtags = 0; + dest->itext_transkeys = 0; + dest->itext_strings = 0; + dest->itext_num = 0; + for(i = 0; i != source->itext_num; ++i) + { + CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i], + source->itext_transkeys[i], source->itext_strings[i])); + } + return 0; +} + +void lodepng_clear_itext(LodePNGInfo* info) +{ + LodePNGIText_cleanup(info); +} + +unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, + const char* transkey, const char* str) +{ + char** new_keys = (char**)(lodepng_realloc(info->itext_keys, sizeof(char*) * (info->itext_num + 1))); + char** new_langtags = (char**)(lodepng_realloc(info->itext_langtags, sizeof(char*) * (info->itext_num + 1))); + char** new_transkeys = (char**)(lodepng_realloc(info->itext_transkeys, sizeof(char*) * (info->itext_num + 1))); + char** new_strings = (char**)(lodepng_realloc(info->itext_strings, sizeof(char*) * (info->itext_num + 1))); + if(!new_keys || !new_langtags || !new_transkeys || !new_strings) + { + lodepng_free(new_keys); + lodepng_free(new_langtags); + lodepng_free(new_transkeys); + lodepng_free(new_strings); + return 83; /*alloc fail*/ + } + + ++info->itext_num; + info->itext_keys = new_keys; + info->itext_langtags = new_langtags; + info->itext_transkeys = new_transkeys; + info->itext_strings = new_strings; + + string_init(&info->itext_keys[info->itext_num - 1]); + string_set(&info->itext_keys[info->itext_num - 1], key); + + string_init(&info->itext_langtags[info->itext_num - 1]); + string_set(&info->itext_langtags[info->itext_num - 1], langtag); + + string_init(&info->itext_transkeys[info->itext_num - 1]); + string_set(&info->itext_transkeys[info->itext_num - 1], transkey); + + string_init(&info->itext_strings[info->itext_num - 1]); + string_set(&info->itext_strings[info->itext_num - 1], str); + + return 0; +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +void lodepng_info_init(LodePNGInfo* info) +{ + lodepng_color_mode_init(&info->color); + info->interlace_method = 0; + info->compression_method = 0; + info->filter_method = 0; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + info->background_defined = 0; + info->background_r = info->background_g = info->background_b = 0; + + LodePNGText_init(info); + LodePNGIText_init(info); + + info->time_defined = 0; + info->phys_defined = 0; + + LodePNGUnknownChunks_init(info); +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} + +void lodepng_info_cleanup(LodePNGInfo* info) +{ + lodepng_color_mode_cleanup(&info->color); +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + LodePNGText_cleanup(info); + LodePNGIText_cleanup(info); + + LodePNGUnknownChunks_cleanup(info); +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} + +unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source) +{ + lodepng_info_cleanup(dest); + *dest = *source; + lodepng_color_mode_init(&dest->color); + CERROR_TRY_RETURN(lodepng_color_mode_copy(&dest->color, &source->color)); + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + CERROR_TRY_RETURN(LodePNGText_copy(dest, source)); + CERROR_TRY_RETURN(LodePNGIText_copy(dest, source)); + + LodePNGUnknownChunks_init(dest); + CERROR_TRY_RETURN(LodePNGUnknownChunks_copy(dest, source)); +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + return 0; +} + +void lodepng_info_swap(LodePNGInfo* a, LodePNGInfo* b) +{ + LodePNGInfo temp = *a; + *a = *b; + *b = temp; +} + +/* ////////////////////////////////////////////////////////////////////////// */ + +/*index: bitgroup index, bits: bitgroup size(1, 2 or 4), in: bitgroup value, out: octet array to add bits to*/ +static void addColorBits(unsigned char* out, size_t index, unsigned bits, unsigned in) +{ + unsigned m = bits == 1 ? 7 : bits == 2 ? 3 : 1; /*8 / bits - 1*/ + /*p = the partial index in the byte, e.g. with 4 palettebits it is 0 for first half or 1 for second half*/ + unsigned p = index & m; + in &= (1u << bits) - 1u; /*filter out any other bits of the input value*/ + in = in << (bits * (m - p)); + if(p == 0) out[index * bits / 8] = in; + else out[index * bits / 8] |= in; +} + +typedef struct ColorTree ColorTree; + +/* +One node of a color tree +This is the data structure used to count the number of unique colors and to get a palette +index for a color. It's like an octree, but because the alpha channel is used too, each +node has 16 instead of 8 children. +*/ +struct ColorTree +{ + ColorTree* children[16]; /*up to 16 pointers to ColorTree of next level*/ + int index; /*the payload. Only has a meaningful value if this is in the last level*/ +}; + +static void color_tree_init(ColorTree* tree) +{ + int i; + for(i = 0; i != 16; ++i) tree->children[i] = 0; + tree->index = -1; +} + +static void color_tree_cleanup(ColorTree* tree) +{ + int i; + for(i = 0; i != 16; ++i) + { + if(tree->children[i]) + { + color_tree_cleanup(tree->children[i]); + lodepng_free(tree->children[i]); + } + } +} + +/*returns -1 if color not present, its index otherwise*/ +static int color_tree_get(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + int bit = 0; + for(bit = 0; bit < 8; ++bit) + { + int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); + if(!tree->children[i]) return -1; + else tree = tree->children[i]; + } + return tree ? tree->index : -1; +} + +#ifdef LODEPNG_COMPILE_ENCODER +static int color_tree_has(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + return color_tree_get(tree, r, g, b, a) >= 0; +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/*color is not allowed to already exist. +Index should be >= 0 (it's signed to be compatible with using -1 for "doesn't exist")*/ +static void color_tree_add(ColorTree* tree, + unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned index) +{ + int bit; + for(bit = 0; bit < 8; ++bit) + { + int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); + if(!tree->children[i]) + { + tree->children[i] = (ColorTree*)lodepng_malloc(sizeof(ColorTree)); + color_tree_init(tree->children[i]); + } + tree = tree->children[i]; + } + tree->index = (int)index; +} + +/*put a pixel, given its RGBA color, into image of any color type*/ +static unsigned rgba8ToPixel(unsigned char* out, size_t i, + const LodePNGColorMode* mode, ColorTree* tree /*for palette*/, + unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + if(mode->colortype == LCT_GREY) + { + unsigned char grey = r; /*((unsigned short)r + g + b) / 3*/; + if(mode->bitdepth == 8) out[i] = grey; + else if(mode->bitdepth == 16) out[i * 2 + 0] = out[i * 2 + 1] = grey; + else + { + /*take the most significant bits of grey*/ + grey = (grey >> (8 - mode->bitdepth)) & ((1 << mode->bitdepth) - 1); + addColorBits(out, i, mode->bitdepth, grey); + } + } + else if(mode->colortype == LCT_RGB) + { + if(mode->bitdepth == 8) + { + out[i * 3 + 0] = r; + out[i * 3 + 1] = g; + out[i * 3 + 2] = b; + } + else + { + out[i * 6 + 0] = out[i * 6 + 1] = r; + out[i * 6 + 2] = out[i * 6 + 3] = g; + out[i * 6 + 4] = out[i * 6 + 5] = b; + } + } + else if(mode->colortype == LCT_PALETTE) + { + int index = color_tree_get(tree, r, g, b, a); + if(index < 0) return 82; /*color not in palette*/ + if(mode->bitdepth == 8) out[i] = index; + else addColorBits(out, i, mode->bitdepth, (unsigned)index); + } + else if(mode->colortype == LCT_GREY_ALPHA) + { + unsigned char grey = r; /*((unsigned short)r + g + b) / 3*/; + if(mode->bitdepth == 8) + { + out[i * 2 + 0] = grey; + out[i * 2 + 1] = a; + } + else if(mode->bitdepth == 16) + { + out[i * 4 + 0] = out[i * 4 + 1] = grey; + out[i * 4 + 2] = out[i * 4 + 3] = a; + } + } + else if(mode->colortype == LCT_RGBA) + { + if(mode->bitdepth == 8) + { + out[i * 4 + 0] = r; + out[i * 4 + 1] = g; + out[i * 4 + 2] = b; + out[i * 4 + 3] = a; + } + else + { + out[i * 8 + 0] = out[i * 8 + 1] = r; + out[i * 8 + 2] = out[i * 8 + 3] = g; + out[i * 8 + 4] = out[i * 8 + 5] = b; + out[i * 8 + 6] = out[i * 8 + 7] = a; + } + } + + return 0; /*no error*/ +} + +/*put a pixel, given its RGBA16 color, into image of any color 16-bitdepth type*/ +static void rgba16ToPixel(unsigned char* out, size_t i, + const LodePNGColorMode* mode, + unsigned short r, unsigned short g, unsigned short b, unsigned short a) +{ + if(mode->colortype == LCT_GREY) + { + unsigned short grey = r; /*((unsigned)r + g + b) / 3*/; + out[i * 2 + 0] = (grey >> 8) & 255; + out[i * 2 + 1] = grey & 255; + } + else if(mode->colortype == LCT_RGB) + { + out[i * 6 + 0] = (r >> 8) & 255; + out[i * 6 + 1] = r & 255; + out[i * 6 + 2] = (g >> 8) & 255; + out[i * 6 + 3] = g & 255; + out[i * 6 + 4] = (b >> 8) & 255; + out[i * 6 + 5] = b & 255; + } + else if(mode->colortype == LCT_GREY_ALPHA) + { + unsigned short grey = r; /*((unsigned)r + g + b) / 3*/; + out[i * 4 + 0] = (grey >> 8) & 255; + out[i * 4 + 1] = grey & 255; + out[i * 4 + 2] = (a >> 8) & 255; + out[i * 4 + 3] = a & 255; + } + else if(mode->colortype == LCT_RGBA) + { + out[i * 8 + 0] = (r >> 8) & 255; + out[i * 8 + 1] = r & 255; + out[i * 8 + 2] = (g >> 8) & 255; + out[i * 8 + 3] = g & 255; + out[i * 8 + 4] = (b >> 8) & 255; + out[i * 8 + 5] = b & 255; + out[i * 8 + 6] = (a >> 8) & 255; + out[i * 8 + 7] = a & 255; + } +} + +/*Get RGBA8 color of pixel with index i (y * width + x) from the raw image with given color type.*/ +static void getPixelColorRGBA8(unsigned char* r, unsigned char* g, + unsigned char* b, unsigned char* a, + const unsigned char* in, size_t i, + const LodePNGColorMode* mode) +{ + if(mode->colortype == LCT_GREY) + { + if(mode->bitdepth == 8) + { + *r = *g = *b = in[i]; + if(mode->key_defined && *r == mode->key_r) *a = 0; + else *a = 255; + } + else if(mode->bitdepth == 16) + { + *r = *g = *b = in[i * 2 + 0]; + if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; + else *a = 255; + } + else + { + unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ + size_t j = i * mode->bitdepth; + unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); + *r = *g = *b = (value * 255) / highest; + if(mode->key_defined && value == mode->key_r) *a = 0; + else *a = 255; + } + } + else if(mode->colortype == LCT_RGB) + { + if(mode->bitdepth == 8) + { + *r = in[i * 3 + 0]; *g = in[i * 3 + 1]; *b = in[i * 3 + 2]; + if(mode->key_defined && *r == mode->key_r && *g == mode->key_g && *b == mode->key_b) *a = 0; + else *a = 255; + } + else + { + *r = in[i * 6 + 0]; + *g = in[i * 6 + 2]; + *b = in[i * 6 + 4]; + if(mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r + && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g + && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; + else *a = 255; + } + } + else if(mode->colortype == LCT_PALETTE) + { + unsigned index; + if(mode->bitdepth == 8) index = in[i]; + else + { + size_t j = i * mode->bitdepth; + index = readBitsFromReversedStream(&j, in, mode->bitdepth); + } + + if(index >= mode->palettesize) + { + /*This is an error according to the PNG spec, but common PNG decoders make it black instead. + Done here too, slightly faster due to no error handling needed.*/ + *r = *g = *b = 0; + *a = 255; + } + else + { + *r = mode->palette[index * 4 + 0]; + *g = mode->palette[index * 4 + 1]; + *b = mode->palette[index * 4 + 2]; + *a = mode->palette[index * 4 + 3]; + } + } + else if(mode->colortype == LCT_GREY_ALPHA) + { + if(mode->bitdepth == 8) + { + *r = *g = *b = in[i * 2 + 0]; + *a = in[i * 2 + 1]; + } + else + { + *r = *g = *b = in[i * 4 + 0]; + *a = in[i * 4 + 2]; + } + } + else if(mode->colortype == LCT_RGBA) + { + if(mode->bitdepth == 8) + { + *r = in[i * 4 + 0]; + *g = in[i * 4 + 1]; + *b = in[i * 4 + 2]; + *a = in[i * 4 + 3]; + } + else + { + *r = in[i * 8 + 0]; + *g = in[i * 8 + 2]; + *b = in[i * 8 + 4]; + *a = in[i * 8 + 6]; + } + } +} + +/*Similar to getPixelColorRGBA8, but with all the for loops inside of the color +mode test cases, optimized to convert the colors much faster, when converting +to RGBA or RGB with 8 bit per cannel. buffer must be RGBA or RGB output with +enough memory, if has_alpha is true the output is RGBA. mode has the color mode +of the input buffer.*/ +static void getPixelColorsRGBA8(unsigned char* buffer, size_t numpixels, + unsigned has_alpha, const unsigned char* in, + const LodePNGColorMode* mode) +{ + unsigned num_channels = has_alpha ? 4 : 3; + size_t i; + if(mode->colortype == LCT_GREY) + { + if(mode->bitdepth == 8) + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = buffer[1] = buffer[2] = in[i]; + if(has_alpha) buffer[3] = mode->key_defined && in[i] == mode->key_r ? 0 : 255; + } + } + else if(mode->bitdepth == 16) + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = buffer[1] = buffer[2] = in[i * 2]; + if(has_alpha) buffer[3] = mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r ? 0 : 255; + } + } + else + { + unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ + size_t j = 0; + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); + buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest; + if(has_alpha) buffer[3] = mode->key_defined && value == mode->key_r ? 0 : 255; + } + } + } + else if(mode->colortype == LCT_RGB) + { + if(mode->bitdepth == 8) + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = in[i * 3 + 0]; + buffer[1] = in[i * 3 + 1]; + buffer[2] = in[i * 3 + 2]; + if(has_alpha) buffer[3] = mode->key_defined && buffer[0] == mode->key_r + && buffer[1]== mode->key_g && buffer[2] == mode->key_b ? 0 : 255; + } + } + else + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = in[i * 6 + 0]; + buffer[1] = in[i * 6 + 2]; + buffer[2] = in[i * 6 + 4]; + if(has_alpha) buffer[3] = mode->key_defined + && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r + && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g + && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b ? 0 : 255; + } + } + } + else if(mode->colortype == LCT_PALETTE) + { + unsigned index; + size_t j = 0; + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + if(mode->bitdepth == 8) index = in[i]; + else index = readBitsFromReversedStream(&j, in, mode->bitdepth); + + if(index >= mode->palettesize) + { + /*This is an error according to the PNG spec, but most PNG decoders make it black instead. + Done here too, slightly faster due to no error handling needed.*/ + buffer[0] = buffer[1] = buffer[2] = 0; + if(has_alpha) buffer[3] = 255; + } + else + { + buffer[0] = mode->palette[index * 4 + 0]; + buffer[1] = mode->palette[index * 4 + 1]; + buffer[2] = mode->palette[index * 4 + 2]; + if(has_alpha) buffer[3] = mode->palette[index * 4 + 3]; + } + } + } + else if(mode->colortype == LCT_GREY_ALPHA) + { + if(mode->bitdepth == 8) + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0]; + if(has_alpha) buffer[3] = in[i * 2 + 1]; + } + } + else + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0]; + if(has_alpha) buffer[3] = in[i * 4 + 2]; + } + } + } + else if(mode->colortype == LCT_RGBA) + { + if(mode->bitdepth == 8) + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = in[i * 4 + 0]; + buffer[1] = in[i * 4 + 1]; + buffer[2] = in[i * 4 + 2]; + if(has_alpha) buffer[3] = in[i * 4 + 3]; + } + } + else + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = in[i * 8 + 0]; + buffer[1] = in[i * 8 + 2]; + buffer[2] = in[i * 8 + 4]; + if(has_alpha) buffer[3] = in[i * 8 + 6]; + } + } + } +} + +/*Get RGBA16 color of pixel with index i (y * width + x) from the raw image with +given color type, but the given color type must be 16-bit itself.*/ +static void getPixelColorRGBA16(unsigned short* r, unsigned short* g, unsigned short* b, unsigned short* a, + const unsigned char* in, size_t i, const LodePNGColorMode* mode) +{ + if(mode->colortype == LCT_GREY) + { + *r = *g = *b = 256 * in[i * 2 + 0] + in[i * 2 + 1]; + if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; + else *a = 65535; + } + else if(mode->colortype == LCT_RGB) + { + *r = 256u * in[i * 6 + 0] + in[i * 6 + 1]; + *g = 256u * in[i * 6 + 2] + in[i * 6 + 3]; + *b = 256u * in[i * 6 + 4] + in[i * 6 + 5]; + if(mode->key_defined + && 256u * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r + && 256u * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g + && 256u * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; + else *a = 65535; + } + else if(mode->colortype == LCT_GREY_ALPHA) + { + *r = *g = *b = 256u * in[i * 4 + 0] + in[i * 4 + 1]; + *a = 256u * in[i * 4 + 2] + in[i * 4 + 3]; + } + else if(mode->colortype == LCT_RGBA) + { + *r = 256u * in[i * 8 + 0] + in[i * 8 + 1]; + *g = 256u * in[i * 8 + 2] + in[i * 8 + 3]; + *b = 256u * in[i * 8 + 4] + in[i * 8 + 5]; + *a = 256u * in[i * 8 + 6] + in[i * 8 + 7]; + } +} + +unsigned lodepng_convert(unsigned char* out, const unsigned char* in, + const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, + unsigned w, unsigned h) +{ + size_t i; + ColorTree tree; + size_t numpixels = w * h; + unsigned error = 0; + + if(lodepng_color_mode_equal(mode_out, mode_in)) + { + size_t numbytes = lodepng_get_raw_size(w, h, mode_in); + for(i = 0; i != numbytes; ++i) out[i] = in[i]; + return 0; + } + + if(mode_out->colortype == LCT_PALETTE) + { + size_t palettesize = mode_out->palettesize; + const unsigned char* palette = mode_out->palette; + size_t palsize = 1u << mode_out->bitdepth; + /*if the user specified output palette but did not give the values, assume + they want the values of the input color type (assuming that one is palette). + Note that we never create a new palette ourselves.*/ + if(palettesize == 0) + { + palettesize = mode_in->palettesize; + palette = mode_in->palette; + } + if(palettesize < palsize) palsize = palettesize; + color_tree_init(&tree); + for(i = 0; i != palsize; ++i) + { + const unsigned char* p = &palette[i * 4]; + color_tree_add(&tree, p[0], p[1], p[2], p[3], (unsigned)i); + } + } + + if(mode_in->bitdepth == 16 && mode_out->bitdepth == 16) + { + for(i = 0; i != numpixels; ++i) + { + unsigned short r = 0, g = 0, b = 0, a = 0; + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); + rgba16ToPixel(out, i, mode_out, r, g, b, a); + } + } + else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGBA) + { + getPixelColorsRGBA8(out, numpixels, 1, in, mode_in); + } + else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGB) + { + getPixelColorsRGBA8(out, numpixels, 0, in, mode_in); + } + else + { + unsigned char r = 0, g = 0, b = 0, a = 0; + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); + error = rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a); + if (error) break; + } + } + + if(mode_out->colortype == LCT_PALETTE) + { + color_tree_cleanup(&tree); + } + + return error; +} + +#ifdef LODEPNG_COMPILE_ENCODER + +void lodepng_color_profile_init(LodePNGColorProfile* profile) +{ + profile->colored = 0; + profile->key = 0; + profile->key_r = profile->key_g = profile->key_b = 0; + profile->alpha = 0; + profile->numcolors = 0; + profile->bits = 1; +} + +/*function used for debug purposes with C++*/ +/*void printColorProfile(LodePNGColorProfile* p) +{ + std::cout << "colored: " << (int)p->colored << ", "; + std::cout << "key: " << (int)p->key << ", "; + std::cout << "key_r: " << (int)p->key_r << ", "; + std::cout << "key_g: " << (int)p->key_g << ", "; + std::cout << "key_b: " << (int)p->key_b << ", "; + std::cout << "alpha: " << (int)p->alpha << ", "; + std::cout << "numcolors: " << (int)p->numcolors << ", "; + std::cout << "bits: " << (int)p->bits << std::endl; +}*/ + +/*Returns how many bits needed to represent given value (max 8 bit)*/ +static unsigned getValueRequiredBits(unsigned char value) +{ + if(value == 0 || value == 255) return 1; + /*The scaling of 2-bit and 4-bit values uses multiples of 85 and 17*/ + if(value % 17 == 0) return value % 85 == 0 ? 2 : 4; + return 8; +} + +/*profile must already have been inited with mode. +It's ok to set some parameters of profile to done already.*/ +unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, + const unsigned char* in, unsigned w, unsigned h, + const LodePNGColorMode* mode) +{ + unsigned error = 0; + size_t i; + ColorTree tree; + size_t numpixels = w * h; + + unsigned colored_done = lodepng_is_greyscale_type(mode) ? 1 : 0; + unsigned alpha_done = lodepng_can_have_alpha(mode) ? 0 : 1; + unsigned numcolors_done = 0; + unsigned bpp = lodepng_get_bpp(mode); + unsigned bits_done = bpp == 1 ? 1 : 0; + unsigned maxnumcolors = 257; + unsigned sixteen = 0; + if(bpp <= 8) maxnumcolors = bpp == 1 ? 2 : (bpp == 2 ? 4 : (bpp == 4 ? 16 : 256)); + + color_tree_init(&tree); + + /*Check if the 16-bit input is truly 16-bit*/ + if(mode->bitdepth == 16) + { + unsigned short r, g, b, a; + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); + if((r & 255) != ((r >> 8) & 255) || (g & 255) != ((g >> 8) & 255) || + (b & 255) != ((b >> 8) & 255) || (a & 255) != ((a >> 8) & 255)) /*first and second byte differ*/ + { + sixteen = 1; + break; + } + } + } + + if(sixteen) + { + unsigned short r = 0, g = 0, b = 0, a = 0; + profile->bits = 16; + bits_done = numcolors_done = 1; /*counting colors no longer useful, palette doesn't support 16-bit*/ + + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); + + if(!colored_done && (r != g || r != b)) + { + profile->colored = 1; + colored_done = 1; + } + + if(!alpha_done) + { + unsigned matchkey = (r == profile->key_r && g == profile->key_g && b == profile->key_b); + if(a != 65535 && (a != 0 || (profile->key && !matchkey))) + { + profile->alpha = 1; + profile->key = 0; + alpha_done = 1; + } + else if(a == 0 && !profile->alpha && !profile->key) + { + profile->key = 1; + profile->key_r = r; + profile->key_g = g; + profile->key_b = b; + } + else if(a == 65535 && profile->key && matchkey) + { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + profile->alpha = 1; + profile->key = 0; + alpha_done = 1; + } + } + if(alpha_done && numcolors_done && colored_done && bits_done) break; + } + + if(profile->key && !profile->alpha) + { + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); + if(a != 0 && r == profile->key_r && g == profile->key_g && b == profile->key_b) + { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + profile->alpha = 1; + profile->key = 0; + alpha_done = 1; + } + } + } + } + else /* < 16-bit */ + { + unsigned char r = 0, g = 0, b = 0, a = 0; + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode); + + if(!bits_done && profile->bits < 8) + { + /*only r is checked, < 8 bits is only relevant for greyscale*/ + unsigned bits = getValueRequiredBits(r); + if(bits > profile->bits) profile->bits = bits; + } + bits_done = (profile->bits >= bpp); + + if(!colored_done && (r != g || r != b)) + { + profile->colored = 1; + colored_done = 1; + if(profile->bits < 8) profile->bits = 8; /*PNG has no colored modes with less than 8-bit per channel*/ + } + + if(!alpha_done) + { + unsigned matchkey = (r == profile->key_r && g == profile->key_g && b == profile->key_b); + if(a != 255 && (a != 0 || (profile->key && !matchkey))) + { + profile->alpha = 1; + profile->key = 0; + alpha_done = 1; + if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + else if(a == 0 && !profile->alpha && !profile->key) + { + profile->key = 1; + profile->key_r = r; + profile->key_g = g; + profile->key_b = b; + } + else if(a == 255 && profile->key && matchkey) + { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + profile->alpha = 1; + profile->key = 0; + alpha_done = 1; + if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + } + + if(!numcolors_done) + { + if(!color_tree_has(&tree, r, g, b, a)) + { + color_tree_add(&tree, r, g, b, a, profile->numcolors); + if(profile->numcolors < 256) + { + unsigned char* p = profile->palette; + unsigned n = profile->numcolors; + p[n * 4 + 0] = r; + p[n * 4 + 1] = g; + p[n * 4 + 2] = b; + p[n * 4 + 3] = a; + } + ++profile->numcolors; + numcolors_done = profile->numcolors >= maxnumcolors; + } + } + + if(alpha_done && numcolors_done && colored_done && bits_done) break; + } + + if(profile->key && !profile->alpha) + { + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode); + if(a != 0 && r == profile->key_r && g == profile->key_g && b == profile->key_b) + { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + profile->alpha = 1; + profile->key = 0; + alpha_done = 1; + if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + } + } + + /*make the profile's key always 16-bit for consistency - repeat each byte twice*/ + profile->key_r += (profile->key_r << 8); + profile->key_g += (profile->key_g << 8); + profile->key_b += (profile->key_b << 8); + } + + color_tree_cleanup(&tree); + return error; +} + +/*Automatically chooses color type that gives smallest amount of bits in the +output image, e.g. grey if there are only greyscale pixels, palette if there +are less than 256 colors, ... +Updates values of mode with a potentially smaller color model. mode_out should +contain the user chosen color model, but will be overwritten with the new chosen one.*/ +unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, + const unsigned char* image, unsigned w, unsigned h, + const LodePNGColorMode* mode_in) +{ + LodePNGColorProfile prof; + unsigned error = 0; + unsigned i, n, palettebits, palette_ok; + + lodepng_color_profile_init(&prof); + error = lodepng_get_color_profile(&prof, image, w, h, mode_in); + if(error) return error; + mode_out->key_defined = 0; + + if(prof.key && w * h <= 16) + { + prof.alpha = 1; /*too few pixels to justify tRNS chunk overhead*/ + prof.key = 0; + if(prof.bits < 8) prof.bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + n = prof.numcolors; + palettebits = n <= 2 ? 1 : (n <= 4 ? 2 : (n <= 16 ? 4 : 8)); + palette_ok = n <= 256 && prof.bits <= 8; + if(w * h < n * 2) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/ + if(!prof.colored && prof.bits <= palettebits) palette_ok = 0; /*grey is less overhead*/ + + if(palette_ok) + { + unsigned char* p = prof.palette; + lodepng_palette_clear(mode_out); /*remove potential earlier palette*/ + for(i = 0; i != prof.numcolors; ++i) + { + error = lodepng_palette_add(mode_out, p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3]); + if(error) break; + } + + mode_out->colortype = LCT_PALETTE; + mode_out->bitdepth = palettebits; + + if(mode_in->colortype == LCT_PALETTE && mode_in->palettesize >= mode_out->palettesize + && mode_in->bitdepth == mode_out->bitdepth) + { + /*If input should have same palette colors, keep original to preserve its order and prevent conversion*/ + lodepng_color_mode_cleanup(mode_out); + lodepng_color_mode_copy(mode_out, mode_in); + } + } + else /*8-bit or 16-bit per channel*/ + { + mode_out->bitdepth = prof.bits; + mode_out->colortype = prof.alpha ? (prof.colored ? LCT_RGBA : LCT_GREY_ALPHA) + : (prof.colored ? LCT_RGB : LCT_GREY); + + if(prof.key) + { + unsigned mask = (1u << mode_out->bitdepth) - 1u; /*profile always uses 16-bit, mask converts it*/ + mode_out->key_r = prof.key_r & mask; + mode_out->key_g = prof.key_g & mask; + mode_out->key_b = prof.key_b & mask; + mode_out->key_defined = 1; + } + } + + return error; +} + +#endif /* #ifdef LODEPNG_COMPILE_ENCODER */ + +/* +Paeth predicter, used by PNG filter type 4 +The parameters are of type short, but should come from unsigned chars, the shorts +are only needed to make the paeth calculation correct. +*/ +static unsigned char paethPredictor(short a, short b, short c) +{ + short pa = abs(b - c); + short pb = abs(a - c); + short pc = abs(a + b - c - c); + + if(pc < pa && pc < pb) return (unsigned char)c; + else if(pb < pa) return (unsigned char)b; + else return (unsigned char)a; +} + +/*shared values used by multiple Adam7 related functions*/ + +static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/ +static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/ +static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/ +static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/ + +/* +Outputs various dimensions and positions in the image related to the Adam7 reduced images. +passw: output containing the width of the 7 passes +passh: output containing the height of the 7 passes +filter_passstart: output containing the index of the start and end of each + reduced image with filter bytes +padded_passstart output containing the index of the start and end of each + reduced image when without filter bytes but with padded scanlines +passstart: output containing the index of the start and end of each reduced + image without padding between scanlines, but still padding between the images +w, h: width and height of non-interlaced image +bpp: bits per pixel +"padded" is only relevant if bpp is less than 8 and a scanline or image does not + end at a full byte +*/ +static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t filter_passstart[8], + size_t padded_passstart[8], size_t passstart[8], unsigned w, unsigned h, unsigned bpp) +{ + /*the passstart values have 8 values: the 8th one indicates the byte after the end of the 7th (= last) pass*/ + unsigned i; + + /*calculate width and height in pixels of each pass*/ + for(i = 0; i != 7; ++i) + { + passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i]; + passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i]; + if(passw[i] == 0) passh[i] = 0; + if(passh[i] == 0) passw[i] = 0; + } + + filter_passstart[0] = padded_passstart[0] = passstart[0] = 0; + for(i = 0; i != 7; ++i) + { + /*if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte)*/ + filter_passstart[i + 1] = filter_passstart[i] + + ((passw[i] && passh[i]) ? passh[i] * (1 + (passw[i] * bpp + 7) / 8) : 0); + /*bits padded if needed to fill full byte at end of each scanline*/ + padded_passstart[i + 1] = padded_passstart[i] + passh[i] * ((passw[i] * bpp + 7) / 8); + /*only padded at end of reduced image*/ + passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7) / 8; + } +} + +#ifdef LODEPNG_COMPILE_DECODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / PNG Decoder / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*read the information from the header and store it in the LodePNGInfo. return value is error*/ +unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, + const unsigned char* in, size_t insize) +{ + LodePNGInfo* info = &state->info_png; + if(insize == 0 || in == 0) + { + CERROR_RETURN_ERROR(state->error, 48); /*error: the given data is empty*/ + } + if(insize < 33) + { + CERROR_RETURN_ERROR(state->error, 27); /*error: the data length is smaller than the length of a PNG header*/ + } + + /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/ + lodepng_info_cleanup(info); + lodepng_info_init(info); + + if(in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71 + || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) + { + CERROR_RETURN_ERROR(state->error, 28); /*error: the first 8 bytes are not the correct PNG signature*/ + } + if(lodepng_chunk_length(in + 8) != 13) + { + CERROR_RETURN_ERROR(state->error, 94); /*error: header size must be 13 bytes*/ + } + if(!lodepng_chunk_type_equals(in + 8, "IHDR")) + { + CERROR_RETURN_ERROR(state->error, 29); /*error: it doesn't start with a IHDR chunk!*/ + } + + /*read the values given in the header*/ + *w = lodepng_read32bitInt(&in[16]); + *h = lodepng_read32bitInt(&in[20]); + info->color.bitdepth = in[24]; + info->color.colortype = (LodePNGColorType)in[25]; + info->compression_method = in[26]; + info->filter_method = in[27]; + info->interlace_method = in[28]; + + if(*w == 0 || *h == 0) + { + CERROR_RETURN_ERROR(state->error, 93); + } + + if(!state->decoder.ignore_crc) + { + unsigned CRC = lodepng_read32bitInt(&in[29]); + unsigned checksum = lodepng_crc32(&in[12], 17); + if(CRC != checksum) + { + CERROR_RETURN_ERROR(state->error, 57); /*invalid CRC*/ + } + } + + /*error: only compression method 0 is allowed in the specification*/ + if(info->compression_method != 0) CERROR_RETURN_ERROR(state->error, 32); + /*error: only filter method 0 is allowed in the specification*/ + if(info->filter_method != 0) CERROR_RETURN_ERROR(state->error, 33); + /*error: only interlace methods 0 and 1 exist in the specification*/ + if(info->interlace_method > 1) CERROR_RETURN_ERROR(state->error, 34); + + state->error = checkColorValidity(info->color.colortype, info->color.bitdepth); + return state->error; +} + +static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, + size_t bytewidth, unsigned char filterType, size_t length) +{ + /* + For PNG filter method 0 + unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, + the filter works byte per byte (bytewidth = 1) + precon is the previous unfiltered scanline, recon the result, scanline the current one + the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead + recon and scanline MAY be the same memory address! precon must be disjoint. + */ + + size_t i; + switch(filterType) + { + case 0: + for(i = 0; i != length; ++i) recon[i] = scanline[i]; + break; + case 1: + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth]; + break; + case 2: + if(precon) + { + for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i]; + } + else + { + for(i = 0; i != length; ++i) recon[i] = scanline[i]; + } + break; + case 3: + if(precon) + { + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1); + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1); + } + else + { + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1); + } + break; + case 4: + if(precon) + { + for(i = 0; i != bytewidth; ++i) + { + recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/ + } + for(i = bytewidth; i < length; ++i) + { + recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); + } + } + else + { + for(i = 0; i != bytewidth; ++i) + { + recon[i] = scanline[i]; + } + for(i = bytewidth; i < length; ++i) + { + /*paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth]*/ + recon[i] = (scanline[i] + recon[i - bytewidth]); + } + } + break; + default: return 36; /*error: unexisting filter type given*/ + } + return 0; +} + +static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) +{ + /* + For PNG filter method 0 + this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 seven times) + out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline + w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel + in and out are allowed to be the same memory address (but aren't the same size since in has the extra filter bytes) + */ + + unsigned y; + unsigned char* prevline = 0; + + /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ + size_t bytewidth = (bpp + 7) / 8; + size_t linebytes = (w * bpp + 7) / 8; + + for(y = 0; y < h; ++y) + { + size_t outindex = linebytes * y; + size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ + unsigned char filterType = in[inindex]; + + CERROR_TRY_RETURN(unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes)); + + prevline = &out[outindex]; + } + + return 0; +} + +/* +in: Adam7 interlaced image, with no padding bits between scanlines, but between + reduced images so that each reduced image starts at a byte. +out: the same pixels, but re-ordered so that they're now a non-interlaced image with size w*h +bpp: bits per pixel +out has the following size in bits: w * h * bpp. +in is possibly bigger due to padding bits between reduced images. +out must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation +(because that's likely a little bit faster) +NOTE: comments about padding bits are only relevant if bpp < 8 +*/ +static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) +{ + unsigned passw[7], passh[7]; + size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned i; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + if(bpp >= 8) + { + for(i = 0; i != 7; ++i) + { + unsigned x, y, b; + size_t bytewidth = bpp / 8; + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) + { + size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth; + size_t pixeloutstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; + for(b = 0; b < bytewidth; ++b) + { + out[pixeloutstart + b] = in[pixelinstart + b]; + } + } + } + } + else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ + { + for(i = 0; i != 7; ++i) + { + unsigned x, y, b; + unsigned ilinebits = bpp * passw[i]; + unsigned olinebits = bpp * w; + size_t obp, ibp; /*bit pointers (for out and in buffer)*/ + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) + { + ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp); + obp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; + for(b = 0; b < bpp; ++b) + { + unsigned char bit = readBitFromReversedStream(&ibp, in); + /*note that this function assumes the out buffer is completely 0, use setBitOfReversedStream otherwise*/ + setBitOfReversedStream0(&obp, out, bit); + } + } + } + } +} + +static void removePaddingBits(unsigned char* out, const unsigned char* in, + size_t olinebits, size_t ilinebits, unsigned h) +{ + /* + After filtering there are still padding bits if scanlines have non multiple of 8 bit amounts. They need + to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers + for the Adam7 code, the color convert code and the output to the user. + in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must + have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits + also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7 + only useful if (ilinebits - olinebits) is a value in the range 1..7 + */ + unsigned y; + size_t diff = ilinebits - olinebits; + size_t ibp = 0, obp = 0; /*input and output bit pointers*/ + for(y = 0; y < h; ++y) + { + size_t x; + for(x = 0; x < olinebits; ++x) + { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream(&obp, out, bit); + } + ibp += diff; + } +} + +/*out must be buffer big enough to contain full image, and in must contain the full decompressed data from +the IDAT chunks (with filter index bytes and possible padding bits) +return value is error*/ +static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, + unsigned w, unsigned h, const LodePNGInfo* info_png) +{ + /* + This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype. + Steps: + *) if no Adam7: 1) unfilter 2) remove padding bits (= posible extra bits per scanline if bpp < 8) + *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace + NOTE: the in buffer will be overwritten with intermediate data! + */ + unsigned bpp = lodepng_get_bpp(&info_png->color); + if(bpp == 0) return 31; /*error: invalid colortype*/ + + if(info_png->interlace_method == 0) + { + if(bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) + { + CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp)); + removePaddingBits(out, in, w * bpp, ((w * bpp + 7) / 8) * 8, h); + } + /*we can immediately filter into the out buffer, no other steps needed*/ + else CERROR_TRY_RETURN(unfilter(out, in, w, h, bpp)); + } + else /*interlace_method is 1 (Adam7)*/ + { + unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned i; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + for(i = 0; i != 7; ++i) + { + CERROR_TRY_RETURN(unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp)); + /*TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline, + move bytes instead of bits or move not at all*/ + if(bpp < 8) + { + /*remove padding bits in scanlines; after this there still may be padding + bits between the different reduced images: each reduced image still starts nicely at a byte*/ + removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp, + ((passw[i] * bpp + 7) / 8) * 8, passh[i]); + } + } + + Adam7_deinterlace(out, in, w, h, bpp); + } + + return 0; +} + +static unsigned readChunk_PLTE(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) +{ + unsigned pos = 0, i; + if(color->palette) lodepng_free(color->palette); + color->palettesize = chunkLength / 3; + color->palette = (unsigned char*)lodepng_malloc(4 * color->palettesize); + if(!color->palette && color->palettesize) + { + color->palettesize = 0; + return 83; /*alloc fail*/ + } + if(color->palettesize > 256) return 38; /*error: palette too big*/ + + for(i = 0; i != color->palettesize; ++i) + { + color->palette[4 * i + 0] = data[pos++]; /*R*/ + color->palette[4 * i + 1] = data[pos++]; /*G*/ + color->palette[4 * i + 2] = data[pos++]; /*B*/ + color->palette[4 * i + 3] = 255; /*alpha*/ + } + + return 0; /* OK */ +} + +static unsigned readChunk_tRNS(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) +{ + unsigned i; + if(color->colortype == LCT_PALETTE) + { + /*error: more alpha values given than there are palette entries*/ + if(chunkLength > color->palettesize) return 38; + + for(i = 0; i != chunkLength; ++i) color->palette[4 * i + 3] = data[i]; + } + else if(color->colortype == LCT_GREY) + { + /*error: this chunk must be 2 bytes for greyscale image*/ + if(chunkLength != 2) return 30; + + color->key_defined = 1; + color->key_r = color->key_g = color->key_b = 256u * data[0] + data[1]; + } + else if(color->colortype == LCT_RGB) + { + /*error: this chunk must be 6 bytes for RGB image*/ + if(chunkLength != 6) return 41; + + color->key_defined = 1; + color->key_r = 256u * data[0] + data[1]; + color->key_g = 256u * data[2] + data[3]; + color->key_b = 256u * data[4] + data[5]; + } + else return 42; /*error: tRNS chunk not allowed for other color models*/ + + return 0; /* OK */ +} + + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +/*background color chunk (bKGD)*/ +static unsigned readChunk_bKGD(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + if(info->color.colortype == LCT_PALETTE) + { + /*error: this chunk must be 1 byte for indexed color image*/ + if(chunkLength != 1) return 43; + + info->background_defined = 1; + info->background_r = info->background_g = info->background_b = data[0]; + } + else if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) + { + /*error: this chunk must be 2 bytes for greyscale image*/ + if(chunkLength != 2) return 44; + + info->background_defined = 1; + info->background_r = info->background_g = info->background_b = 256u * data[0] + data[1]; + } + else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) + { + /*error: this chunk must be 6 bytes for greyscale image*/ + if(chunkLength != 6) return 45; + + info->background_defined = 1; + info->background_r = 256u * data[0] + data[1]; + info->background_g = 256u * data[2] + data[3]; + info->background_b = 256u * data[4] + data[5]; + } + + return 0; /* OK */ +} + +/*text chunk (tEXt)*/ +static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + unsigned error = 0; + char *key = 0, *str = 0; + unsigned i; + + while(!error) /*not really a while loop, only used to break on error*/ + { + unsigned length, string2_begin; + + length = 0; + while(length < chunkLength && data[length] != 0) ++length; + /*even though it's not allowed by the standard, no error is thrown if + there's no null termination char, if the text is empty*/ + if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ + + key = (char*)lodepng_malloc(length + 1); + if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ + + key[length] = 0; + for(i = 0; i != length; ++i) key[i] = (char)data[i]; + + string2_begin = length + 1; /*skip keyword null terminator*/ + + length = (unsigned)(chunkLength < string2_begin ? 0 : chunkLength - string2_begin); + str = (char*)lodepng_malloc(length + 1); + if(!str) CERROR_BREAK(error, 83); /*alloc fail*/ + + str[length] = 0; + for(i = 0; i != length; ++i) str[i] = (char)data[string2_begin + i]; + + error = lodepng_add_text(info, key, str); + + break; + } + + lodepng_free(key); + lodepng_free(str); + + return error; +} + +/*compressed text chunk (zTXt)*/ +static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, + const unsigned char* data, size_t chunkLength) +{ + unsigned error = 0; + unsigned i; + + unsigned length, string2_begin; + char *key = 0; + ucvector decoded; + + ucvector_init(&decoded); + + while(!error) /*not really a while loop, only used to break on error*/ + { + for(length = 0; length < chunkLength && data[length] != 0; ++length) ; + if(length + 2 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ + if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ + + key = (char*)lodepng_malloc(length + 1); + if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ + + key[length] = 0; + for(i = 0; i != length; ++i) key[i] = (char)data[i]; + + if(data[length + 1] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ + + string2_begin = length + 2; + if(string2_begin > chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ + + length = (unsigned)chunkLength - string2_begin; + /*will fail if zlib error, e.g. if length is too small*/ + error = zlib_decompress(&decoded.data, &decoded.size, + (unsigned char*)(&data[string2_begin]), + length, zlibsettings); + if(error) break; + ucvector_push_back(&decoded, 0); + + error = lodepng_add_text(info, key, (char*)decoded.data); + + break; + } + + lodepng_free(key); + ucvector_cleanup(&decoded); + + return error; +} + +/*international text chunk (iTXt)*/ +static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, + const unsigned char* data, size_t chunkLength) +{ + unsigned error = 0; + unsigned i; + + unsigned length, begin, compressed; + char *key = 0, *langtag = 0, *transkey = 0; + ucvector decoded; + ucvector_init(&decoded); + + while(!error) /*not really a while loop, only used to break on error*/ + { + /*Quick check if the chunk length isn't too small. Even without check + it'd still fail with other error checks below if it's too short. This just gives a different error code.*/ + if(chunkLength < 5) CERROR_BREAK(error, 30); /*iTXt chunk too short*/ + + /*read the key*/ + for(length = 0; length < chunkLength && data[length] != 0; ++length) ; + if(length + 3 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination char, corrupt?*/ + if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ + + key = (char*)lodepng_malloc(length + 1); + if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ + + key[length] = 0; + for(i = 0; i != length; ++i) key[i] = (char)data[i]; + + /*read the compression method*/ + compressed = data[length + 1]; + if(data[length + 2] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ + + /*even though it's not allowed by the standard, no error is thrown if + there's no null termination char, if the text is empty for the next 3 texts*/ + + /*read the langtag*/ + begin = length + 3; + length = 0; + for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; + + langtag = (char*)lodepng_malloc(length + 1); + if(!langtag) CERROR_BREAK(error, 83); /*alloc fail*/ + + langtag[length] = 0; + for(i = 0; i != length; ++i) langtag[i] = (char)data[begin + i]; + + /*read the transkey*/ + begin += length + 1; + length = 0; + for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; + + transkey = (char*)lodepng_malloc(length + 1); + if(!transkey) CERROR_BREAK(error, 83); /*alloc fail*/ + + transkey[length] = 0; + for(i = 0; i != length; ++i) transkey[i] = (char)data[begin + i]; + + /*read the actual text*/ + begin += length + 1; + + length = (unsigned)chunkLength < begin ? 0 : (unsigned)chunkLength - begin; + + if(compressed) + { + /*will fail if zlib error, e.g. if length is too small*/ + error = zlib_decompress(&decoded.data, &decoded.size, + (unsigned char*)(&data[begin]), + length, zlibsettings); + if(error) break; + if(decoded.allocsize < decoded.size) decoded.allocsize = decoded.size; + ucvector_push_back(&decoded, 0); + } + else + { + if(!ucvector_resize(&decoded, length + 1)) CERROR_BREAK(error, 83 /*alloc fail*/); + + decoded.data[length] = 0; + for(i = 0; i != length; ++i) decoded.data[i] = data[begin + i]; + } + + error = lodepng_add_itext(info, key, langtag, transkey, (char*)decoded.data); + + break; + } + + lodepng_free(key); + lodepng_free(langtag); + lodepng_free(transkey); + ucvector_cleanup(&decoded); + + return error; +} + +static unsigned readChunk_tIME(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + if(chunkLength != 7) return 73; /*invalid tIME chunk size*/ + + info->time_defined = 1; + info->time.year = 256u * data[0] + data[1]; + info->time.month = data[2]; + info->time.day = data[3]; + info->time.hour = data[4]; + info->time.minute = data[5]; + info->time.second = data[6]; + + return 0; /* OK */ +} + +static unsigned readChunk_pHYs(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + if(chunkLength != 9) return 74; /*invalid pHYs chunk size*/ + + info->phys_defined = 1; + info->phys_x = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3]; + info->phys_y = 16777216u * data[4] + 65536u * data[5] + 256u * data[6] + data[7]; + info->phys_unit = data[8]; + + return 0; /* OK */ +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +/*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/ +static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize) +{ + unsigned char IEND = 0; + const unsigned char* chunk; + size_t i; + ucvector idat; /*the data from idat chunks*/ + ucvector scanlines; + size_t predict; + size_t numpixels; + size_t outsize = 0; + + /*for unknown chunk order*/ + unsigned unknown = 0; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + unsigned critical_pos = 1; /*1 = after IHDR, 2 = after PLTE, 3 = after IDAT*/ +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + + /*provide some proper output values if error will happen*/ + *out = 0; + + state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/ + if(state->error) return; + + numpixels = *w * *h; + + /*multiplication overflow*/ + if(*h != 0 && numpixels / *h != *w) CERROR_RETURN(state->error, 92); + /*multiplication overflow possible further below. Allows up to 2^31-1 pixel + bytes with 16-bit RGBA, the rest is room for filter bytes.*/ + if(numpixels > 268435455) CERROR_RETURN(state->error, 92); + + ucvector_init(&idat); + chunk = &in[33]; /*first byte of the first chunk after the header*/ + + /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. + IDAT data is put at the start of the in buffer*/ + while(!IEND && !state->error) + { + unsigned chunkLength; + const unsigned char* data; /*the data in the chunk*/ + + /*error: size of the in buffer too small to contain next chunk*/ + if((size_t)((chunk - in) + 12) > insize || chunk < in) + { + if(state->decoder.ignore_end) break; /*other errors may still happen though*/ + CERROR_BREAK(state->error, 30); + } + + /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/ + chunkLength = lodepng_chunk_length(chunk); + /*error: chunk length larger than the max PNG chunk size*/ + if(chunkLength > 2147483647) + { + if(state->decoder.ignore_end) break; /*other errors may still happen though*/ + CERROR_BREAK(state->error, 63); + } + + if((size_t)((chunk - in) + chunkLength + 12) > insize || (chunk + chunkLength + 12) < in) + { + CERROR_BREAK(state->error, 64); /*error: size of the in buffer too small to contain next chunk*/ + } + + data = lodepng_chunk_data_const(chunk); + + /*IDAT chunk, containing compressed image data*/ + if(lodepng_chunk_type_equals(chunk, "IDAT")) + { + size_t oldsize = idat.size; + if(!ucvector_resize(&idat, oldsize + chunkLength)) CERROR_BREAK(state->error, 83 /*alloc fail*/); + for(i = 0; i != chunkLength; ++i) idat.data[oldsize + i] = data[i]; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + critical_pos = 3; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } + /*IEND chunk*/ + else if(lodepng_chunk_type_equals(chunk, "IEND")) + { + IEND = 1; + } + /*palette chunk (PLTE)*/ + else if(lodepng_chunk_type_equals(chunk, "PLTE")) + { + state->error = readChunk_PLTE(&state->info_png.color, data, chunkLength); + if(state->error) break; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + critical_pos = 2; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } + /*palette transparency chunk (tRNS)*/ + else if(lodepng_chunk_type_equals(chunk, "tRNS")) + { + state->error = readChunk_tRNS(&state->info_png.color, data, chunkLength); + if(state->error) break; + } +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*background color chunk (bKGD)*/ + else if(lodepng_chunk_type_equals(chunk, "bKGD")) + { + state->error = readChunk_bKGD(&state->info_png, data, chunkLength); + if(state->error) break; + } + /*text chunk (tEXt)*/ + else if(lodepng_chunk_type_equals(chunk, "tEXt")) + { + if(state->decoder.read_text_chunks) + { + state->error = readChunk_tEXt(&state->info_png, data, chunkLength); + if(state->error) break; + } + } + /*compressed text chunk (zTXt)*/ + else if(lodepng_chunk_type_equals(chunk, "zTXt")) + { + if(state->decoder.read_text_chunks) + { + state->error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); + if(state->error) break; + } + } + /*international text chunk (iTXt)*/ + else if(lodepng_chunk_type_equals(chunk, "iTXt")) + { + if(state->decoder.read_text_chunks) + { + state->error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); + if(state->error) break; + } + } + else if(lodepng_chunk_type_equals(chunk, "tIME")) + { + state->error = readChunk_tIME(&state->info_png, data, chunkLength); + if(state->error) break; + } + else if(lodepng_chunk_type_equals(chunk, "pHYs")) + { + state->error = readChunk_pHYs(&state->info_png, data, chunkLength); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + else /*it's not an implemented chunk type, so ignore it: skip over the data*/ + { + /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/ + if(!state->decoder.ignore_critical && !lodepng_chunk_ancillary(chunk)) + { + CERROR_BREAK(state->error, 69); + } + + unknown = 1; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + if(state->decoder.remember_unknown_chunks) + { + state->error = lodepng_chunk_append(&state->info_png.unknown_chunks_data[critical_pos - 1], + &state->info_png.unknown_chunks_size[critical_pos - 1], chunk); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } + + if(!state->decoder.ignore_crc && !unknown) /*check CRC if wanted, only on known chunk types*/ + { + if(lodepng_chunk_check_crc(chunk)) CERROR_BREAK(state->error, 57); /*invalid CRC*/ + } + + if(!IEND) chunk = lodepng_chunk_next_const(chunk); + } + + ucvector_init(&scanlines); + /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation. + If the decompressed size does not match the prediction, the image must be corrupt.*/ + if(state->info_png.interlace_method == 0) + { + /*The extra *h is added because this are the filter bytes every scanline starts with*/ + predict = lodepng_get_raw_size_idat(*w, *h, &state->info_png.color) + *h; + } + else + { + /*Adam-7 interlaced: predicted size is the sum of the 7 sub-images sizes*/ + const LodePNGColorMode* color = &state->info_png.color; + predict = 0; + predict += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3); + if(*w > 4) predict += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3); + predict += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, color) + ((*h + 3) >> 3); + if(*w > 2) predict += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, color) + ((*h + 3) >> 2); + predict += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, color) + ((*h + 1) >> 2); + if(*w > 1) predict += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, color) + ((*h + 1) >> 1); + predict += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, color) + ((*h + 0) >> 1); + } + if(!state->error && !ucvector_reserve(&scanlines, predict)) state->error = 83; /*alloc fail*/ + if(!state->error) + { + state->error = zlib_decompress(&scanlines.data, &scanlines.size, idat.data, + idat.size, &state->decoder.zlibsettings); + if(!state->error && scanlines.size != predict) state->error = 91; /*decompressed size doesn't match prediction*/ + } + ucvector_cleanup(&idat); + + if(!state->error) + { + outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color); + *out = (unsigned char*)lodepng_malloc(outsize); + if(!*out) state->error = 83; /*alloc fail*/ + } + if(!state->error) + { + for(i = 0; i < outsize; i++) (*out)[i] = 0; + state->error = postProcessScanlines(*out, scanlines.data, *w, *h, &state->info_png); + } + ucvector_cleanup(&scanlines); +} + +unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize) +{ + *out = 0; + decodeGeneric(out, w, h, state, in, insize); + if(state->error) return state->error; + if(!state->decoder.color_convert || lodepng_color_mode_equal(&state->info_raw, &state->info_png.color)) + { + /*same color type, no copying or converting of data needed*/ + /*store the info_png color settings on the info_raw so that the info_raw still reflects what colortype + the raw image has to the end user*/ + if(!state->decoder.color_convert) + { + state->error = lodepng_color_mode_copy(&state->info_raw, &state->info_png.color); + if(state->error) return state->error; + } + } + else + { + /*color conversion needed; sort of copy of the data*/ + unsigned char* data = *out; + size_t outsize; + + /*TODO: check if this works according to the statement in the documentation: "The converter can convert + from greyscale input color type, to 8-bit greyscale or greyscale with alpha"*/ + if(!(state->info_raw.colortype == LCT_RGB || state->info_raw.colortype == LCT_RGBA) + && !(state->info_raw.bitdepth == 8)) + { + return 56; /*unsupported color mode conversion*/ + } + + outsize = lodepng_get_raw_size(*w, *h, &state->info_raw); + *out = (unsigned char*)lodepng_malloc(outsize); + if(!(*out)) + { + state->error = 83; /*alloc fail*/ + } + else state->error = lodepng_convert(*out, data, &state->info_raw, + &state->info_png.color, *w, *h); + lodepng_free(data); + } + return state->error; +} + +unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, + size_t insize, LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned error; + LodePNGState state; + lodepng_state_init(&state); + state.info_raw.colortype = colortype; + state.info_raw.bitdepth = bitdepth; + error = lodepng_decode(out, w, h, &state, in, insize); + lodepng_state_cleanup(&state); + return error; +} + +unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) +{ + return lodepng_decode_memory(out, w, h, in, insize, LCT_RGBA, 8); +} + +unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) +{ + return lodepng_decode_memory(out, w, h, in, insize, LCT_RGB, 8); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename, + LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned char* buffer = 0; + size_t buffersize; + unsigned error; + error = lodepng_load_file(&buffer, &buffersize, filename); + if(!error) error = lodepng_decode_memory(out, w, h, buffer, buffersize, colortype, bitdepth); + lodepng_free(buffer); + return error; +} + +unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) +{ + return lodepng_decode_file(out, w, h, filename, LCT_RGBA, 8); +} + +unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) +{ + return lodepng_decode_file(out, w, h, filename, LCT_RGB, 8); +} +#endif /*LODEPNG_COMPILE_DISK*/ + +void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings) +{ + settings->color_convert = 1; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + settings->read_text_chunks = 1; + settings->remember_unknown_chunks = 0; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + settings->ignore_crc = 0; + settings->ignore_critical = 0; + settings->ignore_end = 0; + lodepng_decompress_settings_init(&settings->zlibsettings); +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) + +void lodepng_state_init(LodePNGState* state) +{ +#ifdef LODEPNG_COMPILE_DECODER + lodepng_decoder_settings_init(&state->decoder); +#endif /*LODEPNG_COMPILE_DECODER*/ +#ifdef LODEPNG_COMPILE_ENCODER + lodepng_encoder_settings_init(&state->encoder); +#endif /*LODEPNG_COMPILE_ENCODER*/ + lodepng_color_mode_init(&state->info_raw); + lodepng_info_init(&state->info_png); + state->error = 1; +} + +void lodepng_state_cleanup(LodePNGState* state) +{ + lodepng_color_mode_cleanup(&state->info_raw); + lodepng_info_cleanup(&state->info_png); +} + +void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source) +{ + lodepng_state_cleanup(dest); + *dest = *source; + lodepng_color_mode_init(&dest->info_raw); + lodepng_info_init(&dest->info_png); + dest->error = lodepng_color_mode_copy(&dest->info_raw, &source->info_raw); if(dest->error) return; + dest->error = lodepng_info_copy(&dest->info_png, &source->info_png); if(dest->error) return; +} + +#endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */ + +#ifdef LODEPNG_COMPILE_ENCODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / PNG Encoder / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*chunkName must be string of 4 characters*/ +static unsigned addChunk(ucvector* out, const char* chunkName, const unsigned char* data, size_t length) +{ + CERROR_TRY_RETURN(lodepng_chunk_create(&out->data, &out->size, (unsigned)length, chunkName, data)); + out->allocsize = out->size; /*fix the allocsize again*/ + return 0; +} + +static void writeSignature(ucvector* out) +{ + /*8 bytes PNG signature, aka the magic bytes*/ + ucvector_push_back(out, 137); + ucvector_push_back(out, 80); + ucvector_push_back(out, 78); + ucvector_push_back(out, 71); + ucvector_push_back(out, 13); + ucvector_push_back(out, 10); + ucvector_push_back(out, 26); + ucvector_push_back(out, 10); +} + +static unsigned addChunk_IHDR(ucvector* out, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth, unsigned interlace_method) +{ + unsigned error = 0; + ucvector header; + ucvector_init(&header); + + lodepng_add32bitInt(&header, w); /*width*/ + lodepng_add32bitInt(&header, h); /*height*/ + ucvector_push_back(&header, (unsigned char)bitdepth); /*bit depth*/ + ucvector_push_back(&header, (unsigned char)colortype); /*color type*/ + ucvector_push_back(&header, 0); /*compression method*/ + ucvector_push_back(&header, 0); /*filter method*/ + ucvector_push_back(&header, interlace_method); /*interlace method*/ + + error = addChunk(out, "IHDR", header.data, header.size); + ucvector_cleanup(&header); + + return error; +} + +static unsigned addChunk_PLTE(ucvector* out, const LodePNGColorMode* info) +{ + unsigned error = 0; + size_t i; + ucvector PLTE; + ucvector_init(&PLTE); + for(i = 0; i != info->palettesize * 4; ++i) + { + /*add all channels except alpha channel*/ + if(i % 4 != 3) ucvector_push_back(&PLTE, info->palette[i]); + } + error = addChunk(out, "PLTE", PLTE.data, PLTE.size); + ucvector_cleanup(&PLTE); + + return error; +} + +static unsigned addChunk_tRNS(ucvector* out, const LodePNGColorMode* info) +{ + unsigned error = 0; + size_t i; + ucvector tRNS; + ucvector_init(&tRNS); + if(info->colortype == LCT_PALETTE) + { + size_t amount = info->palettesize; + /*the tail of palette values that all have 255 as alpha, does not have to be encoded*/ + for(i = info->palettesize; i != 0; --i) + { + if(info->palette[4 * (i - 1) + 3] == 255) --amount; + else break; + } + /*add only alpha channel*/ + for(i = 0; i != amount; ++i) ucvector_push_back(&tRNS, info->palette[4 * i + 3]); + } + else if(info->colortype == LCT_GREY) + { + if(info->key_defined) + { + ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255)); + } + } + else if(info->colortype == LCT_RGB) + { + if(info->key_defined) + { + ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_g >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_g & 255)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_b >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_b & 255)); + } + } + + error = addChunk(out, "tRNS", tRNS.data, tRNS.size); + ucvector_cleanup(&tRNS); + + return error; +} + +static unsigned addChunk_IDAT(ucvector* out, const unsigned char* data, size_t datasize, + LodePNGCompressSettings* zlibsettings) +{ + ucvector zlibdata; + unsigned error = 0; + + /*compress with the Zlib compressor*/ + ucvector_init(&zlibdata); + error = zlib_compress(&zlibdata.data, &zlibdata.size, data, datasize, zlibsettings); + if(!error) error = addChunk(out, "IDAT", zlibdata.data, zlibdata.size); + ucvector_cleanup(&zlibdata); + + return error; +} + +static unsigned addChunk_IEND(ucvector* out) +{ + unsigned error = 0; + error = addChunk(out, "IEND", 0, 0); + return error; +} + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + +static unsigned addChunk_tEXt(ucvector* out, const char* keyword, const char* textstring) +{ + unsigned error = 0; + size_t i; + ucvector text; + ucvector_init(&text); + for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)keyword[i]); + if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ + ucvector_push_back(&text, 0); /*0 termination char*/ + for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)textstring[i]); + error = addChunk(out, "tEXt", text.data, text.size); + ucvector_cleanup(&text); + + return error; +} + +static unsigned addChunk_zTXt(ucvector* out, const char* keyword, const char* textstring, + LodePNGCompressSettings* zlibsettings) +{ + unsigned error = 0; + ucvector data, compressed; + size_t i, textsize = strlen(textstring); + + ucvector_init(&data); + ucvector_init(&compressed); + for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]); + if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ + ucvector_push_back(&data, 0); /*0 termination char*/ + ucvector_push_back(&data, 0); /*compression method: 0*/ + + error = zlib_compress(&compressed.data, &compressed.size, + (unsigned char*)textstring, textsize, zlibsettings); + if(!error) + { + for(i = 0; i != compressed.size; ++i) ucvector_push_back(&data, compressed.data[i]); + error = addChunk(out, "zTXt", data.data, data.size); + } + + ucvector_cleanup(&compressed); + ucvector_cleanup(&data); + return error; +} + +static unsigned addChunk_iTXt(ucvector* out, unsigned compressed, const char* keyword, const char* langtag, + const char* transkey, const char* textstring, LodePNGCompressSettings* zlibsettings) +{ + unsigned error = 0; + ucvector data; + size_t i, textsize = strlen(textstring); + + ucvector_init(&data); + + for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]); + if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ + ucvector_push_back(&data, 0); /*null termination char*/ + ucvector_push_back(&data, compressed ? 1 : 0); /*compression flag*/ + ucvector_push_back(&data, 0); /*compression method*/ + for(i = 0; langtag[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)langtag[i]); + ucvector_push_back(&data, 0); /*null termination char*/ + for(i = 0; transkey[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)transkey[i]); + ucvector_push_back(&data, 0); /*null termination char*/ + + if(compressed) + { + ucvector compressed_data; + ucvector_init(&compressed_data); + error = zlib_compress(&compressed_data.data, &compressed_data.size, + (unsigned char*)textstring, textsize, zlibsettings); + if(!error) + { + for(i = 0; i != compressed_data.size; ++i) ucvector_push_back(&data, compressed_data.data[i]); + } + ucvector_cleanup(&compressed_data); + } + else /*not compressed*/ + { + for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)textstring[i]); + } + + if(!error) error = addChunk(out, "iTXt", data.data, data.size); + ucvector_cleanup(&data); + return error; +} + +static unsigned addChunk_bKGD(ucvector* out, const LodePNGInfo* info) +{ + unsigned error = 0; + ucvector bKGD; + ucvector_init(&bKGD); + if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) + { + ucvector_push_back(&bKGD, (unsigned char)(info->background_r >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); + } + else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) + { + ucvector_push_back(&bKGD, (unsigned char)(info->background_r >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_g >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_g & 255)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_b >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_b & 255)); + } + else if(info->color.colortype == LCT_PALETTE) + { + ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); /*palette index*/ + } + + error = addChunk(out, "bKGD", bKGD.data, bKGD.size); + ucvector_cleanup(&bKGD); + + return error; +} + +static unsigned addChunk_tIME(ucvector* out, const LodePNGTime* time) +{ + unsigned error = 0; + unsigned char* data = (unsigned char*)lodepng_malloc(7); + if(!data) return 83; /*alloc fail*/ + data[0] = (unsigned char)(time->year >> 8); + data[1] = (unsigned char)(time->year & 255); + data[2] = (unsigned char)time->month; + data[3] = (unsigned char)time->day; + data[4] = (unsigned char)time->hour; + data[5] = (unsigned char)time->minute; + data[6] = (unsigned char)time->second; + error = addChunk(out, "tIME", data, 7); + lodepng_free(data); + return error; +} + +static unsigned addChunk_pHYs(ucvector* out, const LodePNGInfo* info) +{ + unsigned error = 0; + ucvector data; + ucvector_init(&data); + + lodepng_add32bitInt(&data, info->phys_x); + lodepng_add32bitInt(&data, info->phys_y); + ucvector_push_back(&data, info->phys_unit); + + error = addChunk(out, "pHYs", data.data, data.size); + ucvector_cleanup(&data); + + return error; +} + +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +static void filterScanline(unsigned char* out, const unsigned char* scanline, const unsigned char* prevline, + size_t length, size_t bytewidth, unsigned char filterType) +{ + size_t i; + switch(filterType) + { + case 0: /*None*/ + for(i = 0; i != length; ++i) out[i] = scanline[i]; + break; + case 1: /*Sub*/ + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - scanline[i - bytewidth]; + break; + case 2: /*Up*/ + if(prevline) + { + for(i = 0; i != length; ++i) out[i] = scanline[i] - prevline[i]; + } + else + { + for(i = 0; i != length; ++i) out[i] = scanline[i]; + } + break; + case 3: /*Average*/ + if(prevline) + { + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i] - (prevline[i] >> 1); + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - ((scanline[i - bytewidth] + prevline[i]) >> 1); + } + else + { + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - (scanline[i - bytewidth] >> 1); + } + break; + case 4: /*Paeth*/ + if(prevline) + { + /*paethPredictor(0, prevline[i], 0) is always prevline[i]*/ + for(i = 0; i != bytewidth; ++i) out[i] = (scanline[i] - prevline[i]); + for(i = bytewidth; i < length; ++i) + { + out[i] = (scanline[i] - paethPredictor(scanline[i - bytewidth], prevline[i], prevline[i - bytewidth])); + } + } + else + { + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + /*paethPredictor(scanline[i - bytewidth], 0, 0) is always scanline[i - bytewidth]*/ + for(i = bytewidth; i < length; ++i) out[i] = (scanline[i] - scanline[i - bytewidth]); + } + break; + default: return; /*unexisting filter type given*/ + } +} + +/* log2 approximation. A slight bit faster than std::log. */ +static float flog2(float f) +{ + float result = 0; + while(f > 32) { result += 4; f /= 16; } + while(f > 2) { ++result; f /= 2; } + return result + 1.442695f * (f * f * f / 3 - 3 * f * f / 2 + 3 * f - 1.83333f); +} + +static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, + const LodePNGColorMode* info, const LodePNGEncoderSettings* settings) +{ + /* + For PNG filter method 0 + out must be a buffer with as size: h + (w * h * bpp + 7) / 8, because there are + the scanlines with 1 extra byte per scanline + */ + + unsigned bpp = lodepng_get_bpp(info); + /*the width of a scanline in bytes, not including the filter type*/ + size_t linebytes = (w * bpp + 7) / 8; + /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ + size_t bytewidth = (bpp + 7) / 8; + const unsigned char* prevline = 0; + unsigned x, y; + unsigned error = 0; + LodePNGFilterStrategy strategy = settings->filter_strategy; + + /* + There is a heuristic called the minimum sum of absolute differences heuristic, suggested by the PNG standard: + * If the image type is Palette, or the bit depth is smaller than 8, then do not filter the image (i.e. + use fixed filtering, with the filter None). + * (The other case) If the image type is Grayscale or RGB (with or without Alpha), and the bit depth is + not smaller than 8, then use adaptive filtering heuristic as follows: independently for each row, apply + all five filters and select the filter that produces the smallest sum of absolute values per row. + This heuristic is used if filter strategy is LFS_MINSUM and filter_palette_zero is true. + + If filter_palette_zero is true and filter_strategy is not LFS_MINSUM, the above heuristic is followed, + but for "the other case", whatever strategy filter_strategy is set to instead of the minimum sum + heuristic is used. + */ + if(settings->filter_palette_zero && + (info->colortype == LCT_PALETTE || info->bitdepth < 8)) strategy = LFS_ZERO; + + if(bpp == 0) return 31; /*error: invalid color type*/ + + if(strategy == LFS_ZERO) + { + for(y = 0; y != h; ++y) + { + size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ + size_t inindex = linebytes * y; + out[outindex] = 0; /*filter type byte*/ + filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, 0); + prevline = &in[inindex]; + } + } + else if(strategy == LFS_MINSUM) + { + /*adaptive filtering*/ + size_t sum[5]; + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ + size_t smallest = 0; + unsigned char type, bestType = 0; + + for(type = 0; type != 5; ++type) + { + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) return 83; /*alloc fail*/ + } + + if(!error) + { + for(y = 0; y != h; ++y) + { + /*try the 5 filter types*/ + for(type = 0; type != 5; ++type) + { + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); + + /*calculate the sum of the result*/ + sum[type] = 0; + if(type == 0) + { + for(x = 0; x != linebytes; ++x) sum[type] += (unsigned char)(attempt[type][x]); + } + else + { + for(x = 0; x != linebytes; ++x) + { + /*For differences, each byte should be treated as signed, values above 127 are negative + (converted to signed char). Filtertype 0 isn't a difference though, so use unsigned there. + This means filtertype 0 is almost never chosen, but that is justified.*/ + unsigned char s = attempt[type][x]; + sum[type] += s < 128 ? s : (255U - s); + } + } + + /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/ + if(type == 0 || sum[type] < smallest) + { + bestType = type; + smallest = sum[type]; + } + } + + prevline = &in[y * linebytes]; + + /*now fill the out values*/ + out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; + } + } + + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); + } + else if(strategy == LFS_ENTROPY) + { + float sum[5]; + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ + float smallest = 0; + unsigned type, bestType = 0; + unsigned count[256]; + + for(type = 0; type != 5; ++type) + { + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) return 83; /*alloc fail*/ + } + + for(y = 0; y != h; ++y) + { + /*try the 5 filter types*/ + for(type = 0; type != 5; ++type) + { + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); + for(x = 0; x != 256; ++x) count[x] = 0; + for(x = 0; x != linebytes; ++x) ++count[attempt[type][x]]; + ++count[type]; /*the filter type itself is part of the scanline*/ + sum[type] = 0; + for(x = 0; x != 256; ++x) + { + float p = count[x] / (float)(linebytes + 1); + sum[type] += count[x] == 0 ? 0 : flog2(1 / p) * p; + } + /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/ + if(type == 0 || sum[type] < smallest) + { + bestType = type; + smallest = sum[type]; + } + } + + prevline = &in[y * linebytes]; + + /*now fill the out values*/ + out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; + } + + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); + } + else if(strategy == LFS_PREDEFINED) + { + for(y = 0; y != h; ++y) + { + size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ + size_t inindex = linebytes * y; + unsigned char type = settings->predefined_filters[y]; + out[outindex] = type; /*filter type byte*/ + filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, type); + prevline = &in[inindex]; + } + } + else if(strategy == LFS_BRUTE_FORCE) + { + /*brute force filter chooser. + deflate the scanline after every filter attempt to see which one deflates best. + This is very slow and gives only slightly smaller, sometimes even larger, result*/ + size_t size[5]; + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ + size_t smallest = 0; + unsigned type = 0, bestType = 0; + unsigned char* dummy; + LodePNGCompressSettings zlibsettings = settings->zlibsettings; + /*use fixed tree on the attempts so that the tree is not adapted to the filtertype on purpose, + to simulate the true case where the tree is the same for the whole image. Sometimes it gives + better result with dynamic tree anyway. Using the fixed tree sometimes gives worse, but in rare + cases better compression. It does make this a bit less slow, so it's worth doing this.*/ + zlibsettings.btype = 1; + /*a custom encoder likely doesn't read the btype setting and is optimized for complete PNG + images only, so disable it*/ + zlibsettings.custom_zlib = 0; + zlibsettings.custom_deflate = 0; + for(type = 0; type != 5; ++type) + { + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) return 83; /*alloc fail*/ + } + for(y = 0; y != h; ++y) /*try the 5 filter types*/ + { + for(type = 0; type != 5; ++type) + { + unsigned testsize = (unsigned)linebytes; + /*if(testsize > 8) testsize /= 8;*/ /*it already works good enough by testing a part of the row*/ + + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); + size[type] = 0; + dummy = 0; + zlib_compress(&dummy, &size[type], attempt[type], testsize, &zlibsettings); + lodepng_free(dummy); + /*check if this is smallest size (or if type == 0 it's the first case so always store the values)*/ + if(type == 0 || size[type] < smallest) + { + bestType = type; + smallest = size[type]; + } + } + prevline = &in[y * linebytes]; + out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; + } + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); + } + else return 88; /* unknown filter strategy */ + + return error; +} + +static void addPaddingBits(unsigned char* out, const unsigned char* in, + size_t olinebits, size_t ilinebits, unsigned h) +{ + /*The opposite of the removePaddingBits function + olinebits must be >= ilinebits*/ + unsigned y; + size_t diff = olinebits - ilinebits; + size_t obp = 0, ibp = 0; /*bit pointers*/ + for(y = 0; y != h; ++y) + { + size_t x; + for(x = 0; x < ilinebits; ++x) + { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream(&obp, out, bit); + } + /*obp += diff; --> no, fill in some value in the padding bits too, to avoid + "Use of uninitialised value of size ###" warning from valgrind*/ + for(x = 0; x != diff; ++x) setBitOfReversedStream(&obp, out, 0); + } +} + +/* +in: non-interlaced image with size w*h +out: the same pixels, but re-ordered according to PNG's Adam7 interlacing, with + no padding bits between scanlines, but between reduced images so that each + reduced image starts at a byte. +bpp: bits per pixel +there are no padding bits, not between scanlines, not between reduced images +in has the following size in bits: w * h * bpp. +out is possibly bigger due to padding bits between reduced images +NOTE: comments about padding bits are only relevant if bpp < 8 +*/ +static void Adam7_interlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) +{ + unsigned passw[7], passh[7]; + size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned i; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + if(bpp >= 8) + { + for(i = 0; i != 7; ++i) + { + unsigned x, y, b; + size_t bytewidth = bpp / 8; + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) + { + size_t pixelinstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; + size_t pixeloutstart = passstart[i] + (y * passw[i] + x) * bytewidth; + for(b = 0; b < bytewidth; ++b) + { + out[pixeloutstart + b] = in[pixelinstart + b]; + } + } + } + } + else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ + { + for(i = 0; i != 7; ++i) + { + unsigned x, y, b; + unsigned ilinebits = bpp * passw[i]; + unsigned olinebits = bpp * w; + size_t obp, ibp; /*bit pointers (for out and in buffer)*/ + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) + { + ibp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; + obp = (8 * passstart[i]) + (y * ilinebits + x * bpp); + for(b = 0; b < bpp; ++b) + { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream(&obp, out, bit); + } + } + } + } +} + +/*out must be buffer big enough to contain uncompressed IDAT chunk data, and in must contain the full image. +return value is error**/ +static unsigned preProcessScanlines(unsigned char** out, size_t* outsize, const unsigned char* in, + unsigned w, unsigned h, + const LodePNGInfo* info_png, const LodePNGEncoderSettings* settings) +{ + /* + This function converts the pure 2D image with the PNG's colortype, into filtered-padded-interlaced data. Steps: + *) if no Adam7: 1) add padding bits (= posible extra bits per scanline if bpp < 8) 2) filter + *) if adam7: 1) Adam7_interlace 2) 7x add padding bits 3) 7x filter + */ + unsigned bpp = lodepng_get_bpp(&info_png->color); + unsigned error = 0; + + if(info_png->interlace_method == 0) + { + *outsize = h + (h * ((w * bpp + 7) / 8)); /*image size plus an extra byte per scanline + possible padding bits*/ + *out = (unsigned char*)lodepng_malloc(*outsize); + if(!(*out) && (*outsize)) error = 83; /*alloc fail*/ + + if(!error) + { + /*non multiple of 8 bits per scanline, padding bits needed per scanline*/ + if(bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) + { + unsigned char* padded = (unsigned char*)lodepng_malloc(h * ((w * bpp + 7) / 8)); + if(!padded) error = 83; /*alloc fail*/ + if(!error) + { + addPaddingBits(padded, in, ((w * bpp + 7) / 8) * 8, w * bpp, h); + error = filter(*out, padded, w, h, &info_png->color, settings); + } + lodepng_free(padded); + } + else + { + /*we can immediately filter into the out buffer, no other steps needed*/ + error = filter(*out, in, w, h, &info_png->color, settings); + } + } + } + else /*interlace_method is 1 (Adam7)*/ + { + unsigned passw[7], passh[7]; + size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned char* adam7; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + *outsize = filter_passstart[7]; /*image size plus an extra byte per scanline + possible padding bits*/ + *out = (unsigned char*)lodepng_malloc(*outsize); + if(!(*out)) error = 83; /*alloc fail*/ + + adam7 = (unsigned char*)lodepng_malloc(passstart[7]); + if(!adam7 && passstart[7]) error = 83; /*alloc fail*/ + + if(!error) + { + unsigned i; + + Adam7_interlace(adam7, in, w, h, bpp); + for(i = 0; i != 7; ++i) + { + if(bpp < 8) + { + unsigned char* padded = (unsigned char*)lodepng_malloc(padded_passstart[i + 1] - padded_passstart[i]); + if(!padded) ERROR_BREAK(83); /*alloc fail*/ + addPaddingBits(padded, &adam7[passstart[i]], + ((passw[i] * bpp + 7) / 8) * 8, passw[i] * bpp, passh[i]); + error = filter(&(*out)[filter_passstart[i]], padded, + passw[i], passh[i], &info_png->color, settings); + lodepng_free(padded); + } + else + { + error = filter(&(*out)[filter_passstart[i]], &adam7[padded_passstart[i]], + passw[i], passh[i], &info_png->color, settings); + } + + if(error) break; + } + } + + lodepng_free(adam7); + } + + return error; +} + +/* +palette must have 4 * palettesize bytes allocated, and given in format RGBARGBARGBARGBA... +returns 0 if the palette is opaque, +returns 1 if the palette has a single color with alpha 0 ==> color key +returns 2 if the palette is semi-translucent. +*/ +static unsigned getPaletteTranslucency(const unsigned char* palette, size_t palettesize) +{ + size_t i; + unsigned key = 0; + unsigned r = 0, g = 0, b = 0; /*the value of the color with alpha 0, so long as color keying is possible*/ + for(i = 0; i != palettesize; ++i) + { + if(!key && palette[4 * i + 3] == 0) + { + r = palette[4 * i + 0]; g = palette[4 * i + 1]; b = palette[4 * i + 2]; + key = 1; + i = (size_t)(-1); /*restart from beginning, to detect earlier opaque colors with key's value*/ + } + else if(palette[4 * i + 3] != 255) return 2; + /*when key, no opaque RGB may have key's RGB*/ + else if(key && r == palette[i * 4 + 0] && g == palette[i * 4 + 1] && b == palette[i * 4 + 2]) return 2; + } + return key; +} + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +static unsigned addUnknownChunks(ucvector* out, unsigned char* data, size_t datasize) +{ + unsigned char* inchunk = data; + while((size_t)(inchunk - data) < datasize) + { + CERROR_TRY_RETURN(lodepng_chunk_append(&out->data, &out->size, inchunk)); + out->allocsize = out->size; /*fix the allocsize again*/ + inchunk = lodepng_chunk_next(inchunk); + } + return 0; +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +unsigned lodepng_encode(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h, + LodePNGState* state) +{ + LodePNGInfo info; + ucvector outv; + unsigned char* data = 0; /*uncompressed version of the IDAT chunk data*/ + size_t datasize = 0; + + /*provide some proper output values if error will happen*/ + *out = 0; + *outsize = 0; + state->error = 0; + + /*check input values validity*/ + if((state->info_png.color.colortype == LCT_PALETTE || state->encoder.force_palette) + && (state->info_png.color.palettesize == 0 || state->info_png.color.palettesize > 256)) + { + CERROR_RETURN_ERROR(state->error, 68); /*invalid palette size, it is only allowed to be 1-256*/ + } + if(state->encoder.zlibsettings.btype > 2) + { + CERROR_RETURN_ERROR(state->error, 61); /*error: unexisting btype*/ + } + if(state->info_png.interlace_method > 1) + { + CERROR_RETURN_ERROR(state->error, 71); /*error: unexisting interlace mode*/ + } + state->error = checkColorValidity(state->info_png.color.colortype, state->info_png.color.bitdepth); + if(state->error) return state->error; /*error: unexisting color type given*/ + state->error = checkColorValidity(state->info_raw.colortype, state->info_raw.bitdepth); + if(state->error) return state->error; /*error: unexisting color type given*/ + + /* color convert and compute scanline filter types */ + lodepng_info_init(&info); + lodepng_info_copy(&info, &state->info_png); + if(state->encoder.auto_convert) + { + state->error = lodepng_auto_choose_color(&info.color, image, w, h, &state->info_raw); + } + if (!state->error) + { + if(!lodepng_color_mode_equal(&state->info_raw, &info.color)) + { + unsigned char* converted; + size_t size = (w * h * (size_t)lodepng_get_bpp(&info.color) + 7) / 8; + + converted = (unsigned char*)lodepng_malloc(size); + if(!converted && size) state->error = 83; /*alloc fail*/ + if(!state->error) + { + state->error = lodepng_convert(converted, image, &info.color, &state->info_raw, w, h); + } + if(!state->error) preProcessScanlines(&data, &datasize, converted, w, h, &info, &state->encoder); + lodepng_free(converted); + } + else preProcessScanlines(&data, &datasize, image, w, h, &info, &state->encoder); + } + + /* output all PNG chunks */ + ucvector_init(&outv); + while(!state->error) /*while only executed once, to break on error*/ + { +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + size_t i; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + /*write signature and chunks*/ + writeSignature(&outv); + /*IHDR*/ + addChunk_IHDR(&outv, w, h, info.color.colortype, info.color.bitdepth, info.interlace_method); +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*unknown chunks between IHDR and PLTE*/ + if(info.unknown_chunks_data[0]) + { + state->error = addUnknownChunks(&outv, info.unknown_chunks_data[0], info.unknown_chunks_size[0]); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + /*PLTE*/ + if(info.color.colortype == LCT_PALETTE) + { + addChunk_PLTE(&outv, &info.color); + } + if(state->encoder.force_palette && (info.color.colortype == LCT_RGB || info.color.colortype == LCT_RGBA)) + { + addChunk_PLTE(&outv, &info.color); + } + /*tRNS*/ + if(info.color.colortype == LCT_PALETTE && getPaletteTranslucency(info.color.palette, info.color.palettesize) != 0) + { + addChunk_tRNS(&outv, &info.color); + } + if((info.color.colortype == LCT_GREY || info.color.colortype == LCT_RGB) && info.color.key_defined) + { + addChunk_tRNS(&outv, &info.color); + } +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*bKGD (must come between PLTE and the IDAt chunks*/ + if(info.background_defined) addChunk_bKGD(&outv, &info); + /*pHYs (must come before the IDAT chunks)*/ + if(info.phys_defined) addChunk_pHYs(&outv, &info); + + /*unknown chunks between PLTE and IDAT*/ + if(info.unknown_chunks_data[1]) + { + state->error = addUnknownChunks(&outv, info.unknown_chunks_data[1], info.unknown_chunks_size[1]); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + /*IDAT (multiple IDAT chunks must be consecutive)*/ + state->error = addChunk_IDAT(&outv, data, datasize, &state->encoder.zlibsettings); + if(state->error) break; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*tIME*/ + if(info.time_defined) addChunk_tIME(&outv, &info.time); + /*tEXt and/or zTXt*/ + for(i = 0; i != info.text_num; ++i) + { + if(strlen(info.text_keys[i]) > 79) + { + state->error = 66; /*text chunk too large*/ + break; + } + if(strlen(info.text_keys[i]) < 1) + { + state->error = 67; /*text chunk too small*/ + break; + } + if(state->encoder.text_compression) + { + addChunk_zTXt(&outv, info.text_keys[i], info.text_strings[i], &state->encoder.zlibsettings); + } + else + { + addChunk_tEXt(&outv, info.text_keys[i], info.text_strings[i]); + } + } + /*LodePNG version id in text chunk*/ + if(state->encoder.add_id) + { + unsigned alread_added_id_text = 0; + for(i = 0; i != info.text_num; ++i) + { + if(!strcmp(info.text_keys[i], "LodePNG")) + { + alread_added_id_text = 1; + break; + } + } + if(alread_added_id_text == 0) + { + addChunk_tEXt(&outv, "LodePNG", LODEPNG_VERSION_STRING); /*it's shorter as tEXt than as zTXt chunk*/ + } + } + /*iTXt*/ + for(i = 0; i != info.itext_num; ++i) + { + if(strlen(info.itext_keys[i]) > 79) + { + state->error = 66; /*text chunk too large*/ + break; + } + if(strlen(info.itext_keys[i]) < 1) + { + state->error = 67; /*text chunk too small*/ + break; + } + addChunk_iTXt(&outv, state->encoder.text_compression, + info.itext_keys[i], info.itext_langtags[i], info.itext_transkeys[i], info.itext_strings[i], + &state->encoder.zlibsettings); + } + + /*unknown chunks between IDAT and IEND*/ + if(info.unknown_chunks_data[2]) + { + state->error = addUnknownChunks(&outv, info.unknown_chunks_data[2], info.unknown_chunks_size[2]); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + addChunk_IEND(&outv); + + break; /*this isn't really a while loop; no error happened so break out now!*/ + } + + lodepng_info_cleanup(&info); + lodepng_free(data); + /*instead of cleaning the vector up, give it to the output*/ + *out = outv.data; + *outsize = outv.size; + + return state->error; +} + +unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, const unsigned char* image, + unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned error; + LodePNGState state; + lodepng_state_init(&state); + state.info_raw.colortype = colortype; + state.info_raw.bitdepth = bitdepth; + state.info_png.color.colortype = colortype; + state.info_png.color.bitdepth = bitdepth; + lodepng_encode(out, outsize, image, w, h, &state); + error = state.error; + lodepng_state_cleanup(&state); + return error; +} + +unsigned lodepng_encode32(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) +{ + return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGBA, 8); +} + +unsigned lodepng_encode24(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) +{ + return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGB, 8); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned lodepng_encode_file(const char* filename, const unsigned char* image, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned char* buffer; + size_t buffersize; + unsigned error = lodepng_encode_memory(&buffer, &buffersize, image, w, h, colortype, bitdepth); + if(!error) error = lodepng_save_file(buffer, buffersize, filename); + lodepng_free(buffer); + return error; +} + +unsigned lodepng_encode32_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) +{ + return lodepng_encode_file(filename, image, w, h, LCT_RGBA, 8); +} + +unsigned lodepng_encode24_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) +{ + return lodepng_encode_file(filename, image, w, h, LCT_RGB, 8); +} +#endif /*LODEPNG_COMPILE_DISK*/ + +void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings) +{ + lodepng_compress_settings_init(&settings->zlibsettings); + settings->filter_palette_zero = 1; + settings->filter_strategy = LFS_MINSUM; + settings->auto_convert = 1; + settings->force_palette = 0; + settings->predefined_filters = 0; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + settings->add_id = 0; + settings->text_compression = 1; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} + +#endif /*LODEPNG_COMPILE_ENCODER*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ERROR_TEXT +/* +This returns the description of a numerical error code in English. This is also +the documentation of all the error codes. +*/ +const char* lodepng_error_text(unsigned code) +{ + switch(code) + { + case 0: return "no error, everything went ok"; + case 1: return "nothing done yet"; /*the Encoder/Decoder has done nothing yet, error checking makes no sense yet*/ + case 10: return "end of input memory reached without huffman end code"; /*while huffman decoding*/ + case 11: return "error in code tree made it jump outside of huffman tree"; /*while huffman decoding*/ + case 13: return "problem while processing dynamic deflate block"; + case 14: return "problem while processing dynamic deflate block"; + case 15: return "problem while processing dynamic deflate block"; + case 16: return "unexisting code while processing dynamic deflate block"; + case 17: return "end of out buffer memory reached while inflating"; + case 18: return "invalid distance code while inflating"; + case 19: return "end of out buffer memory reached while inflating"; + case 20: return "invalid deflate block BTYPE encountered while decoding"; + case 21: return "NLEN is not ones complement of LEN in a deflate block"; + /*end of out buffer memory reached while inflating: + This can happen if the inflated deflate data is longer than the amount of bytes required to fill up + all the pixels of the image, given the color depth and image dimensions. Something that doesn't + happen in a normal, well encoded, PNG image.*/ + case 22: return "end of out buffer memory reached while inflating"; + case 23: return "end of in buffer memory reached while inflating"; + case 24: return "invalid FCHECK in zlib header"; + case 25: return "invalid compression method in zlib header"; + case 26: return "FDICT encountered in zlib header while it's not used for PNG"; + case 27: return "PNG file is smaller than a PNG header"; + /*Checks the magic file header, the first 8 bytes of the PNG file*/ + case 28: return "incorrect PNG signature, it's no PNG or corrupted"; + case 29: return "first chunk is not the header chunk"; + case 30: return "chunk length too large, chunk broken off at end of file"; + case 31: return "illegal PNG color type or bpp"; + case 32: return "illegal PNG compression method"; + case 33: return "illegal PNG filter method"; + case 34: return "illegal PNG interlace method"; + case 35: return "chunk length of a chunk is too large or the chunk too small"; + case 36: return "illegal PNG filter type encountered"; + case 37: return "illegal bit depth for this color type given"; + case 38: return "the palette is too big"; /*more than 256 colors*/ + case 39: return "more palette alpha values given in tRNS chunk than there are colors in the palette"; + case 40: return "tRNS chunk has wrong size for greyscale image"; + case 41: return "tRNS chunk has wrong size for RGB image"; + case 42: return "tRNS chunk appeared while it was not allowed for this color type"; + case 43: return "bKGD chunk has wrong size for palette image"; + case 44: return "bKGD chunk has wrong size for greyscale image"; + case 45: return "bKGD chunk has wrong size for RGB image"; + case 48: return "empty input buffer given to decoder. Maybe caused by non-existing file?"; + case 49: return "jumped past memory while generating dynamic huffman tree"; + case 50: return "jumped past memory while generating dynamic huffman tree"; + case 51: return "jumped past memory while inflating huffman block"; + case 52: return "jumped past memory while inflating"; + case 53: return "size of zlib data too small"; + case 54: return "repeat symbol in tree while there was no value symbol yet"; + /*jumped past tree while generating huffman tree, this could be when the + tree will have more leaves than symbols after generating it out of the + given lenghts. They call this an oversubscribed dynamic bit lengths tree in zlib.*/ + case 55: return "jumped past tree while generating huffman tree"; + case 56: return "given output image colortype or bitdepth not supported for color conversion"; + case 57: return "invalid CRC encountered (checking CRC can be disabled)"; + case 58: return "invalid ADLER32 encountered (checking ADLER32 can be disabled)"; + case 59: return "requested color conversion not supported"; + case 60: return "invalid window size given in the settings of the encoder (must be 0-32768)"; + case 61: return "invalid BTYPE given in the settings of the encoder (only 0, 1 and 2 are allowed)"; + /*LodePNG leaves the choice of RGB to greyscale conversion formula to the user.*/ + case 62: return "conversion from color to greyscale not supported"; + case 63: return "length of a chunk too long, max allowed for PNG is 2147483647 bytes per chunk"; /*(2^31-1)*/ + /*this would result in the inability of a deflated block to ever contain an end code. It must be at least 1.*/ + case 64: return "the length of the END symbol 256 in the Huffman tree is 0"; + case 66: return "the length of a text chunk keyword given to the encoder is longer than the maximum of 79 bytes"; + case 67: return "the length of a text chunk keyword given to the encoder is smaller than the minimum of 1 byte"; + case 68: return "tried to encode a PLTE chunk with a palette that has less than 1 or more than 256 colors"; + case 69: return "unknown chunk type with 'critical' flag encountered by the decoder"; + case 71: return "unexisting interlace mode given to encoder (must be 0 or 1)"; + case 72: return "while decoding, unexisting compression method encountering in zTXt or iTXt chunk (it must be 0)"; + case 73: return "invalid tIME chunk size"; + case 74: return "invalid pHYs chunk size"; + /*length could be wrong, or data chopped off*/ + case 75: return "no null termination char found while decoding text chunk"; + case 76: return "iTXt chunk too short to contain required bytes"; + case 77: return "integer overflow in buffer size"; + case 78: return "failed to open file for reading"; /*file doesn't exist or couldn't be opened for reading*/ + case 79: return "failed to open file for writing"; + case 80: return "tried creating a tree of 0 symbols"; + case 81: return "lazy matching at pos 0 is impossible"; + case 82: return "color conversion to palette requested while a color isn't in palette"; + case 83: return "memory allocation failed"; + case 84: return "given image too small to contain all pixels to be encoded"; + case 86: return "impossible offset in lz77 encoding (internal bug)"; + case 87: return "must provide custom zlib function pointer if LODEPNG_COMPILE_ZLIB is not defined"; + case 88: return "invalid filter strategy given for LodePNGEncoderSettings.filter_strategy"; + case 89: return "text chunk keyword too short or long: must have size 1-79"; + /*the windowsize in the LodePNGCompressSettings. Requiring POT(==> & instead of %) makes encoding 12% faster.*/ + case 90: return "windowsize must be a power of two"; + case 91: return "invalid decompressed idat size"; + case 92: return "too many pixels, not supported"; + case 93: return "zero width or height is invalid"; + case 94: return "header chunk must have a size of 13 bytes"; + } + return "unknown error code"; +} +#endif /*LODEPNG_COMPILE_ERROR_TEXT*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // C++ Wrapper // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_CPP +namespace lodepng +{ + +#ifdef LODEPNG_COMPILE_DISK +unsigned load_file(std::vector& buffer, const std::string& filename) +{ + long size = lodepng_filesize(filename.c_str()); + if(size < 0) return 78; + buffer.resize((size_t)size); + return size == 0 ? 0 : lodepng_buffer_file(&buffer[0], (size_t)size, filename.c_str()); +} + +/*write given buffer to the file, overwriting the file, it doesn't append to it.*/ +unsigned save_file(const std::vector& buffer, const std::string& filename) +{ + return lodepng_save_file(buffer.empty() ? 0 : &buffer[0], buffer.size(), filename.c_str()); +} +#endif /* LODEPNG_COMPILE_DISK */ + +#ifdef LODEPNG_COMPILE_ZLIB +#ifdef LODEPNG_COMPILE_DECODER +unsigned decompress(std::vector& out, const unsigned char* in, size_t insize, + const LodePNGDecompressSettings& settings) +{ + unsigned char* buffer = 0; + size_t buffersize = 0; + unsigned error = zlib_decompress(&buffer, &buffersize, in, insize, &settings); + if(buffer) + { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned decompress(std::vector& out, const std::vector& in, + const LodePNGDecompressSettings& settings) +{ + return decompress(out, in.empty() ? 0 : &in[0], in.size(), settings); +} +#endif /* LODEPNG_COMPILE_DECODER */ + +#ifdef LODEPNG_COMPILE_ENCODER +unsigned compress(std::vector& out, const unsigned char* in, size_t insize, + const LodePNGCompressSettings& settings) +{ + unsigned char* buffer = 0; + size_t buffersize = 0; + unsigned error = zlib_compress(&buffer, &buffersize, in, insize, &settings); + if(buffer) + { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned compress(std::vector& out, const std::vector& in, + const LodePNGCompressSettings& settings) +{ + return compress(out, in.empty() ? 0 : &in[0], in.size(), settings); +} +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_ZLIB */ + + +#ifdef LODEPNG_COMPILE_PNG + +State::State() +{ + lodepng_state_init(this); +} + +State::State(const State& other) +{ + lodepng_state_init(this); + lodepng_state_copy(this, &other); +} + +State::~State() +{ + lodepng_state_cleanup(this); +} + +State& State::operator=(const State& other) +{ + lodepng_state_copy(this, &other); + return *this; +} + +#ifdef LODEPNG_COMPILE_DECODER + +unsigned decode(std::vector& out, unsigned& w, unsigned& h, const unsigned char* in, + size_t insize, LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned char* buffer; + unsigned error = lodepng_decode_memory(&buffer, &w, &h, in, insize, colortype, bitdepth); + if(buffer && !error) + { + State state; + state.info_raw.colortype = colortype; + state.info_raw.bitdepth = bitdepth; + size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw); + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + const std::vector& in, LodePNGColorType colortype, unsigned bitdepth) +{ + return decode(out, w, h, in.empty() ? 0 : &in[0], (unsigned)in.size(), colortype, bitdepth); +} + +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + State& state, + const unsigned char* in, size_t insize) +{ + unsigned char* buffer = NULL; + unsigned error = lodepng_decode(&buffer, &w, &h, &state, in, insize); + if(buffer && !error) + { + size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw); + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + } + lodepng_free(buffer); + return error; +} + +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + State& state, + const std::vector& in) +{ + return decode(out, w, h, state, in.empty() ? 0 : &in[0], in.size()); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned decode(std::vector& out, unsigned& w, unsigned& h, const std::string& filename, + LodePNGColorType colortype, unsigned bitdepth) +{ + std::vector buffer; + unsigned error = load_file(buffer, filename); + if(error) return error; + return decode(out, w, h, buffer, colortype, bitdepth); +} +#endif /* LODEPNG_COMPILE_DECODER */ +#endif /* LODEPNG_COMPILE_DISK */ + +#ifdef LODEPNG_COMPILE_ENCODER +unsigned encode(std::vector& out, const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned char* buffer; + size_t buffersize; + unsigned error = lodepng_encode_memory(&buffer, &buffersize, in, w, h, colortype, bitdepth); + if(buffer) + { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned encode(std::vector& out, + const std::vector& in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) +{ + if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; + return encode(out, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); +} + +unsigned encode(std::vector& out, + const unsigned char* in, unsigned w, unsigned h, + State& state) +{ + unsigned char* buffer; + size_t buffersize; + unsigned error = lodepng_encode(&buffer, &buffersize, in, w, h, &state); + if(buffer) + { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned encode(std::vector& out, + const std::vector& in, unsigned w, unsigned h, + State& state) +{ + if(lodepng_get_raw_size(w, h, &state.info_raw) > in.size()) return 84; + return encode(out, in.empty() ? 0 : &in[0], w, h, state); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned encode(const std::string& filename, + const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) +{ + std::vector buffer; + unsigned error = encode(buffer, in, w, h, colortype, bitdepth); + if(!error) error = save_file(buffer, filename); + return error; +} + +unsigned encode(const std::string& filename, + const std::vector& in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) +{ + if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; + return encode(filename, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); +} +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_PNG */ +} /* namespace lodepng */ +#endif /*LODEPNG_COMPILE_CPP*/ diff --git a/source/menus/menu_fileoptions.c b/source/menus/menu_fileoptions.c new file mode 100644 index 0000000..0ad7058 --- /dev/null +++ b/source/menus/menu_fileoptions.c @@ -0,0 +1,642 @@ +#include +#include +#include +#include + +#include "C2D_helper.h" +#include "common.h" +#include "config.h" +#include "dirbrowse.h" +#include "fs.h" +#include "keyboard.h" +#include "progress_bar.h" +#include "menu_fileoptions.h" +#include "textures.h" +#include "touch.h" +#include "utils.h" + +/* +* Copy Flags +*/ +#define COPY_FOLDER_RECURSIVE 2 +#define COPY_DELETE_ON_FINISH 1 +#define COPY_KEEP_ON_FINISH 0 +#define NOTHING_TO_COPY -1 + +/* +* Copy Mode +* -1 : Nothing +* 0 : Copy +* 1 : Move +*/ +static int copymode = NOTHING_TO_COPY; +/* +* Copy Move Origin +*/ +static char copysource[1024]; + +static int delete_dialog_selection = 0, row = 0, column = 0; +static bool copy_status = false, cut_status = false; + +static float delete_confirm_width = 0, delete_confirm_height = 0; +static float delete_cancel_width = 0, delete_cancel_height = 0; + +static float properties_ok_width = 0, properties_ok_height = 0; +static float options_cancel_width = 0, options_cancel_height = 0; + +void FileOptions_ResetClipboard(void) +{ + multi_select_index = 0; + memset(multi_select, 0, sizeof(multi_select)); + memset(multi_select_indices, 0, sizeof(multi_select_indices)); + memset(multi_select_dir, 0, sizeof(multi_select_dir)); + memset(multi_select_paths, 0, sizeof(multi_select_paths)); +} + +static Result FileOptions_CreateFolder(void) +{ + char *buf = (char *)malloc(256); + strcpy(buf, OSK_Get(256, "", "Enter name")); + + if (strncmp(buf, "", 1) == 0) + return -1; + + char path[512]; + strcpy(path, cwd); + strcat(path, buf); + free(buf); + + FS_RecursiveMakeDir(archive, path); + + Dirbrowse_PopulateFiles(true); + return 0; +} + +static Result FileOptions_Rename(void) +{ + Result ret = 0; + File *file = Dirbrowse_GetFileIndex(position); + + if (file == NULL) + return -1; + + if (strncmp(file->name, "..", 2) == 0) + return -2; + + char oldPath[512], newPath[512]; + + char *buf = (char *)malloc(256); + + strcpy(oldPath, cwd); + strcpy(newPath, cwd); + strcat(oldPath, file->name); + + strcpy(buf, OSK_Get(256, file->name, "Enter name")); + strcat(newPath, buf); + free(buf); + + if (file->isDir) + { + if (R_FAILED(ret = FS_RenameDir(archive, oldPath, newPath))) + return ret; + } + else + { + if (R_FAILED(ret = FS_RenameFile(archive, oldPath, newPath))) + return ret; + } + + Dirbrowse_PopulateFiles(true); + return 0; +} + +static int FileOptions_DeleteFile(void) +{ + File *file = Dirbrowse_GetFileIndex(position); + + if (file == NULL) + return -1; + + if (strncmp(file->name, "..", 2) == 0) + return -2; + + char path[512]; + strcpy(path, cwd); + strcpy(path + strlen(path), file->name); + + Result ret = 0; + + if (file->isDir) // Delete folder + { + if (R_FAILED(ret = FS_RmdirRecursive(archive, path))) + return ret; + } + else // Delete file + { + if (R_FAILED(ret = FS_Remove(archive, path))) + return ret; + } + + return 0; +} + +// Copy file from src to dst +static int FileOptions_CopyFile(char *src, char *dst, bool displayAnim) +{ + int chunksize = (512 * 1024); // Chunk size + char *buffer = (char *)malloc(chunksize); // Reading buffer + + u64 totalwrite = 0; // Accumulated writing + u64 totalread = 0; // Accumulated reading + + int result = 0; // Result + + int in = open(src, O_RDONLY, 0777); // Open file for reading + u64 size = FS_GetFileSize(archive, src); + + // Opened file for reading + if (in >= 0) + { + if (FS_FileExists(archive, dst)) + FS_Remove(archive, dst); // Delete output file (if existing) + + int out = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0777); // Open output file for writing + + if (out >= 0) // Opened file for writing + { + u64 b_read = 0; // Read byte count + + // Copy loop (512KB at a time) + while((b_read = read(in, buffer, chunksize)) > 0) + { + totalread += b_read; // Accumulate read data + totalwrite += write(out, buffer, b_read); // Write data + + if (displayAnim) + ProgressBar_DisplayProgress(copymode == 1? "Moving" : "Copying", Utils_Basename(src), totalread, size); + } + + close(out); // Close output file + + if (totalread != totalwrite) // Insufficient copy + result = -3; + } + + else // Output open error + result = -2; + + close(in); // Close input file + } + + // Input open error + else + result = -1; + + free(buffer); // Free memory + return result; // Return result +} + +// Recursively copy file from src to dst +static Result FileOptions_CopyDir(char *src, char *dst) +{ + Handle dirHandle; + Result ret = 0; // Open working Directory + + // Opened directory + if (R_SUCCEEDED(ret = FSUSER_OpenDirectory(&dirHandle, archive, fsMakePath(PATH_ASCII, src)))) + { + FS_MakeDir(archive, dst); // Create output directory (is allowed to fail, we can merge folders after all) + + u32 entryCount = 0; + FS_DirectoryEntry *entries = (FS_DirectoryEntry*) calloc(MAX_FILES, sizeof(FS_DirectoryEntry)); + + if (R_SUCCEEDED(ret = FSDIR_Read(dirHandle, &entryCount, MAX_FILES, entries))) + { + u8 name[255] = {'\0'}; + for (u32 i = 0; i < entryCount; i++) + { + Utils_U16_To_U8(&name[0], entries[i].name, 254); + + if (strlen(name) > 0) + { + // Calculate buffer size + int insize = strlen(src) + strlen(name) + 2; + int outsize = strlen(dst) + strlen(name) + 2; + + // Allocate buffer + char *inbuffer = (char *)malloc(insize); + char *outbuffer = (char *)malloc(outsize); + + // Puzzle input path + strcpy(inbuffer, src); + inbuffer[strlen(inbuffer) + 1] = 0; + inbuffer[strlen(inbuffer)] = '/'; + strcpy(inbuffer + strlen(inbuffer), name); + + // Puzzle output path + strcpy(outbuffer, dst); + outbuffer[strlen(outbuffer) + 1] = 0; + outbuffer[strlen(outbuffer)] = '/'; + strcpy(outbuffer + strlen(outbuffer), name); + + if (entries[i].attributes & FS_ATTRIBUTE_DIRECTORY) // Another folder + FileOptions_CopyDir(inbuffer, outbuffer); // Copy folder (via recursion) + + else + { + if (R_FAILED(ret = ret = FileOptions_CopyFile(inbuffer, outbuffer, false))) // Copy file + return ret; + } + + // Free buffer + free(inbuffer); + free(outbuffer); + } + + ProgressBar_DisplayProgress(copymode == 1? "Moving" : "Copying", Utils_Basename(name), i, entryCount); + } + } + else + return ret; + + free(entries); + + if (R_FAILED(ret = FSDIR_Close(dirHandle))) // Close directory + return ret; + } + else + return ret; + + return 0; +} + +static void FileOptions_Copy(int flag) +{ + File *file = Dirbrowse_GetFileIndex(position); + + if (file == NULL) + return; + + // Copy file source + strcpy(copysource, cwd); + strcpy(copysource + strlen(copysource), file->name); + + if ((file->isDir) && (strncmp(file->name, "..", 2) != 0)) // If directory, add recursive folder flag + flag |= COPY_FOLDER_RECURSIVE; + + copymode = flag; // Set copy flags +} + +// Paste file or folder +static Result FileOptions_Paste(void) +{ + if (copymode == NOTHING_TO_COPY) // No copy source + return -1; + + // Source and target folder are identical + char *lastslash = NULL; + int i = 0; + + for(; i < strlen(copysource); i++) + if (copysource[i] == '/') + lastslash = copysource + i; + + char backup = lastslash[1]; + lastslash[1] = 0; + int identical = strcmp(copysource, cwd) == 0; + lastslash[1] = backup; + + if (identical) + return -2; + + char *filename = lastslash + 1; // Source filename + + int requiredlength = strlen(cwd) + strlen(filename) + 1; // Required target path buffer size + char *copytarget = (char *)malloc(requiredlength); // Allocate target path buffer + + // Puzzle target path + strcpy(copytarget, cwd); + strcpy(copytarget + strlen(copytarget), filename); + + Result ret = -3; // Return result + + // Recursive folder copy + if ((copymode & COPY_FOLDER_RECURSIVE) == COPY_FOLDER_RECURSIVE) + { + // Check files in current folder + File *node = files; for(; node != NULL; node = node->next) + { + if ((strcmp(filename, node->name) == 0) && (!node->isDir)) // Found a file matching the name (folder = ok, file = not) + return -4; // Error out + } + + ret = FileOptions_CopyDir(copysource, copytarget); // Copy folder recursively + + if ((R_SUCCEEDED(ret)) && (copymode & COPY_DELETE_ON_FINISH) == COPY_DELETE_ON_FINISH) + { + // Needs to add a forward "/" + if (!(strcmp(&(copysource[(strlen(copysource)-1)]), "/") == 0)) + strcat(copysource, "/"); + + FS_RmdirRecursive(archive, copysource); // Delete dir + } + } + + // Simple file copy + else + { + ret = FileOptions_CopyFile(copysource, copytarget, true); // Copy file + + if ((R_SUCCEEDED(ret)) && (copymode & COPY_DELETE_ON_FINISH) == COPY_DELETE_ON_FINISH) + FS_Remove(archive, copysource); // Delete file + } + + // Paste success + if (R_SUCCEEDED(ret)) + { + memset(copysource, 0, sizeof(copysource)); // Erase cache data + copymode = NOTHING_TO_COPY; + } + + free(copytarget); // Free target path buffer + return ret; // Return result +} + +static void HandleDelete(void) +{ + if ((multi_select_index > 0) && (strlen(multi_select_dir) != 0)) + { + for (int i = 0; i < multi_select_index; i++) + { + if (strlen(multi_select_paths[i]) != 0) + { + if (strncmp(multi_select_paths[i], "..", 2) != 0) + { + if (FS_DirExists(archive, multi_select_paths[i])) + FS_RmdirRecursive(archive, multi_select_paths[i]); + else if (FS_FileExists(archive, multi_select_paths[i])) + FS_Remove(archive, multi_select_paths[i]); + } + } + } + + FileOptions_ResetClipboard(); + } + else if (FileOptions_DeleteFile() != 0) + return; + + Dirbrowse_PopulateFiles(true); + MENU_STATE = MENU_STATE_HOME; +} + +void Menu_ControlDeleteDialog(u32 input) +{ + if (input & KEY_RIGHT) + delete_dialog_selection++; + else if (input & KEY_LEFT) + delete_dialog_selection--; + + Utils_SetMax(&delete_dialog_selection, 0, 1); + Utils_SetMin(&delete_dialog_selection, 1, 0); + + if (input & KEY_B) + { + delete_dialog_selection = 0; + MENU_STATE = MENU_STATE_FILEOPTIONS; + } + + if (input & KEY_A) + { + if (delete_dialog_selection == 1) + HandleDelete(); + else + MENU_STATE = MENU_STATE_FILEOPTIONS; + + delete_dialog_selection = 0; + } +} + +void Menu_DisplayDeleteDialog(void) +{ + float text_width = 0; + Draw_GetTextSize(0.45f, &text_width, NULL, "Do you want to continue?"); + + Draw_GetTextSize(0.45f, &delete_confirm_width, &delete_confirm_height, "YES"); + Draw_GetTextSize(0.45f, &delete_cancel_width, &delete_cancel_height, "NO"); + + Draw_Image(config_dark_theme? dialog_dark : dialog, ((320 - (dialog.subtex->width)) / 2), ((240 - (dialog.subtex->height)) / 2)); + + Draw_Text(((320 - (dialog.subtex->width)) / 2) + 6, ((240 - (dialog.subtex->height)) / 2) + 6, 0.45f, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR, "Confirm deletion"); + + Draw_Text(((320 - (text_width)) / 2), ((240 - (dialog.subtex->height)) / 2) + 40, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Do you wish to continue?"); + + if (delete_dialog_selection == 0) + Draw_Rect((288 - delete_cancel_width) - 5, (159 - delete_cancel_height) - 5, delete_cancel_width + 10, delete_cancel_height + 10, config_dark_theme? SELECTOR_COLOUR_DARK : SELECTOR_COLOUR_LIGHT); + else if (delete_dialog_selection == 1) + Draw_Rect((248 - (delete_confirm_width)) - 5, (159 - delete_confirm_height) - 5, delete_confirm_width + 10, delete_confirm_height + 10, config_dark_theme? SELECTOR_COLOUR_DARK : SELECTOR_COLOUR_LIGHT); + + Draw_Text(248 - (delete_confirm_width), (159 - delete_confirm_height), 0.45f, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR, "YES"); + Draw_Text(288 - delete_cancel_width, (159 - delete_cancel_height), 0.45f, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR, "NO"); +} + +void Menu_ControlProperties(u32 input) +{ + if ((input & KEY_A) || (input & KEY_B)) + MENU_STATE = MENU_STATE_FILEOPTIONS; +} + +void Menu_DisplayProperties(void) +{ + // Find File + File *file = Dirbrowse_GetFileIndex(position); + + char path[1024]; + strcpy(path, cwd); + strcpy(path + strlen(path), file->name); + + Draw_Image(config_dark_theme? properties_dialog_dark : properties_dialog, 54.5, 30); + Draw_Text(61, 37, 0.45f, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR, "Properties"); + + char utils_size[16]; + u64 size = 0; + + if (file->isDir) + { + size = FS_GetFileSize(archive, path); + Utils_GetSizeString(utils_size, size); + } + + Draw_Textf(66, 65, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Name: %s", file->name); + Draw_Textf(66, 80, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Parent: %s", cwd); + Draw_Textf(66, 95, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Size: %s", utils_size); + + Draw_GetTextSize(0.45f, &properties_ok_width, &properties_ok_height, "OK"); + Draw_Rect((253 - properties_ok_width) - 5, (218 - properties_ok_height) - 5, properties_ok_width + 10, properties_ok_height + 10, config_dark_theme? SELECTOR_COLOUR_DARK : SELECTOR_COLOUR_LIGHT); + Draw_Text(253 - properties_ok_width, 218 - properties_ok_height, 0.45f, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR, "OK"); +} + +static void HandleCopy() +{ + if (copy_status == false && cut_status == false) + { + copy_status = true; + FileOptions_Copy(COPY_KEEP_ON_FINISH); + MENU_STATE = MENU_STATE_HOME; + } + else if (copy_status == true) + { + if ((multi_select_index > 0) && (strlen(multi_select_dir) != 0)) + { + char dest[512]; + + for (int i = 0; i < multi_select_index; i++) + { + if (strlen(multi_select_paths[i]) != 0) + { + if (strncmp(multi_select_paths[i], "..", 2) != 0) + { + snprintf(dest, 512, "%s%s", cwd, Utils_Basename(multi_select_paths[i])); + + if (FS_DirExists(archive, multi_select_paths[i])) + FileOptions_CopyDir(multi_select_paths[i], dest); + else if (FS_FileExists(archive, multi_select_paths[i])) + FileOptions_CopyFile(multi_select_paths[i], dest, true); + } + } + } + + FileOptions_ResetClipboard(); + copymode = NOTHING_TO_COPY; + + } + else if (FileOptions_Paste() != 0) + return; + + copy_status = false; + Dirbrowse_PopulateFiles(true); + MENU_STATE = MENU_STATE_HOME; + } +} + +static void HandleCut() +{ + if (cut_status == false && copy_status == false) + { + cut_status = true; + FileOptions_Copy(COPY_DELETE_ON_FINISH); + MENU_STATE = MENU_STATE_HOME; + } + else if (cut_status == true) + { + char dest[512]; + + if ((multi_select_index > 0) && (strlen(multi_select_dir) != 0)) + { + for (int i = 0; i < multi_select_index; i++) + { + if (strlen(multi_select_paths[i]) != 0) + { + snprintf(dest, 512, "%s%s", cwd, Utils_Basename(multi_select_paths[i])); + + if (FS_DirExists(archive, multi_select_paths[i])) + FS_RenameDir(archive, multi_select_paths[i], dest); + else if (FS_FileExists(archive, multi_select_paths[i])) + FS_RenameFile(archive, multi_select_paths[i], dest); + } + } + + FileOptions_ResetClipboard(); + } + else + { + snprintf(dest, 512, "%s%s", cwd, Utils_Basename(copysource)); + + if (FS_DirExists(archive, copysource)) + FS_RenameDir(archive, copysource, dest); + else if (FS_FileExists(archive, copysource)) + FS_RenameFile(archive, copysource, dest); + } + + cut_status = false; + copymode = NOTHING_TO_COPY; + Dirbrowse_PopulateFiles(true); + MENU_STATE = MENU_STATE_HOME; + } +} + +void Menu_ControlFileOptions(u32 input) +{ + if (input & KEY_RIGHT) + row++; + else if (input & KEY_LEFT) + row--; + + if (input & KEY_DDOWN) + column++; + else if (input & KEY_DUP) + column--; + + Utils_SetMax(&row, 0, 1); + Utils_SetMin(&row, 1, 0); + + Utils_SetMax(&column, 0, 2); + Utils_SetMin(&column, 2, 0); + + if (input & KEY_A) + { + if (row == 0 && column == 0) + MENU_STATE = MENU_STATE_PROPERTIES; + else if (row == 1 && column == 0) + FileOptions_CreateFolder(); + else if (row == 0 && column == 1) + FileOptions_Rename(); + else if (row == 1 && column == 1) + HandleCopy(); + else if (row == 0 && column == 2) + HandleCut(); + else if (row == 1 && column == 2) + MENU_STATE = MENU_STATE_DIALOG; + } + + if (input & KEY_B) + { + copy_status = false; + cut_status = false; + row = 0; + column = 0; + MENU_STATE = MENU_STATE_HOME; + } + + if (input & KEY_X) + MENU_STATE = MENU_STATE_HOME; +} + +void Menu_DisplayFileOptions(void) +{ + Draw_Image(config_dark_theme? options_dialog_dark : options_dialog, 54.5, 30); + Draw_Text(61, 37, 0.45f, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR, "Actions"); + + Draw_GetTextSize(0.45f, &options_cancel_width, &options_cancel_height, "CANCEL"); + Draw_Text(258 - options_cancel_width, 223 - options_cancel_height, 0.45f, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR, "CANCEL"); + + if (row == 0 && column == 0) + Draw_Rect(56, 69, 103, 36, config_dark_theme? SELECTOR_COLOUR_DARK : SELECTOR_COLOUR_LIGHT); + else if (row == 1 && column == 0) + Draw_Rect(160, 69, 103, 36, config_dark_theme? SELECTOR_COLOUR_DARK : SELECTOR_COLOUR_LIGHT); + else if (row == 0 && column == 1) + Draw_Rect(56, 105, 103, 36, config_dark_theme? SELECTOR_COLOUR_DARK : SELECTOR_COLOUR_LIGHT); + else if (row == 1 && column == 1) + Draw_Rect(160, 105, 103, 36, config_dark_theme? SELECTOR_COLOUR_DARK : SELECTOR_COLOUR_LIGHT); + else if (row == 0 && column == 2) + Draw_Rect(56, 142, 103, 36, config_dark_theme? SELECTOR_COLOUR_DARK : SELECTOR_COLOUR_LIGHT); + else if (row == 1 && column == 2) + Draw_Rect(160, 142, 103, 36, config_dark_theme? SELECTOR_COLOUR_DARK : SELECTOR_COLOUR_LIGHT); + + Draw_Text(66, 80, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Properties"); + Draw_Text(66, 116, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Rename"); + Draw_Text(66, 152, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, cut_status? "Paste" : "Move"); + + Draw_Text(170, 80, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "New folder"); + Draw_Text(170, 116, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, copy_status? "Paste" : "Copy"); + Draw_Text(170, 152, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Delete"); +} diff --git a/source/menus/menu_ftp.c b/source/menus/menu_ftp.c index 8829b68..e041765 100644 --- a/source/menus/menu_ftp.c +++ b/source/menus/menu_ftp.c @@ -24,7 +24,7 @@ void Menu_DisplayFTP(void) int pBar = 0, xlim = 270; - while(MENU_DEFAULT_STATE == MENU_STATE_FTP) + while(MENU_STATE == MENU_STATE_FTP) { ftp_loop(); @@ -35,11 +35,8 @@ void Menu_DisplayFTP(void) Draw_Rect(0, 20, 320, 220, config_dark_theme? MENU_BAR_DARK : MENU_BAR_LIGHT); // Menu bar ACU_GetWifiStatus(&wifiStatus); - - //Draw_Rect(0, 0, 320, 240, RGBA8(BottomScreen_colour.r, BottomScreen_colour.g, BottomScreen_colour.b, 255)); - //Draw_Rect(0, 0, 320, 20, RGBA8(BottomScreen_bar_colour.r, BottomScreen_bar_colour.g, BottomScreen_bar_colour.b, 255)); - //Menu_Draw_MenuBar(); + Menu_DrawMenuBar(); if (!(wifiStatus)) { @@ -92,6 +89,6 @@ void Menu_DisplayFTP(void) memset(ftp_accepted_connection, 0, 20); // Empty accepted connection address memset(ftp_file_transfer, 0, 50); // Empty transfer status ftp_exit(); - MENU_DEFAULT_STATE = MENU_STATE_HOME; + MENU_STATE = MENU_STATE_HOME; Dirbrowse_PopulateFiles(true); } \ No newline at end of file diff --git a/source/menus/menu_gallery.c b/source/menus/menu_gallery.c index 60a596e..ff94b24 100644 --- a/source/menus/menu_gallery.c +++ b/source/menus/menu_gallery.c @@ -387,5 +387,5 @@ void Gallery_DisplayImage(char *path) Gallery_FreeImage(image); memset(album, 0, sizeof(album[0][0]) * 512 * 512); count = 0; - MENU_DEFAULT_STATE = MENU_STATE_HOME; + MENU_STATE = MENU_STATE_HOME; } diff --git a/source/menus/menu_main.c b/source/menus/menu_main.c index 9d5ac66..bcf6e9b 100644 --- a/source/menus/menu_main.c +++ b/source/menus/menu_main.c @@ -1,28 +1,23 @@ +#include #include <3ds.h> #include "C2D_helper.h" #include "common.h" #include "config.h" #include "dirbrowse.h" +#include "fs.h" +#include "keyboard.h" #include "menu_ftp.h" +#include "menu_fileoptions.h" #include "menu_main.h" +#include "menu_settings.h" #include "status_bar.h" #include "textures.h" +#include "touch.h" #include "utils.h" -#define MENUBAR_X_BOUNDARY 0 -static int menubar_x = -125; static char multi_select_dir_old[256]; -static void Menu_ControlMenuBar(u32 input) -{ - if (input & KEY_A) - MENU_DEFAULT_STATE = MENU_STATE_SETTINGS; - - if ((input & KEY_SELECT) || (input & KEY_B)) - MENU_DEFAULT_STATE = MENU_STATE_HOME; -} - static void Menu_HandleMultiSelect(void) { // multi_select_dir can only hold one dir @@ -59,23 +54,7 @@ static void Menu_ControlHome(u32 input) if (input & KEY_START) longjmp(exitJmp, 1); - if (input & KEY_SELECT) - { - if (MENU_DEFAULT_STATE == MENU_STATE_MENUBAR) - MENU_DEFAULT_STATE = MENU_STATE_HOME; - else - { - menubar_x = -125; - MENU_DEFAULT_STATE = MENU_STATE_MENUBAR; - } - } - - if (input & KEY_SELECT) - { - wait(1); - MENU_DEFAULT_STATE = MENU_STATE_FTP; - Menu_DisplayFTP(); - } + Menu_ControlMenuBar(input); if (fileCount > 0) { @@ -95,10 +74,10 @@ static void Menu_ControlHome(u32 input) // Open options if (input & KEY_X) { - if (MENU_DEFAULT_STATE == MENU_STATE_OPTIONS) - MENU_DEFAULT_STATE = MENU_STATE_HOME; + if (MENU_STATE == MENU_STATE_FILEOPTIONS) + MENU_STATE = MENU_STATE_HOME; else - MENU_DEFAULT_STATE = MENU_STATE_OPTIONS; + MENU_STATE = MENU_STATE_FILEOPTIONS; } if (input & KEY_Y) @@ -118,11 +97,88 @@ static void Menu_ControlHome(u32 input) } } -void Menu_Main(void) +void Menu_DrawMenuBar(void) +{ + Draw_Image(MENU_STATE == MENU_STATE_HOME? icon_home_dark : icon_home, 0, -2.5); + Draw_Image((MENU_STATE == MENU_STATE_FILEOPTIONS) || (MENU_STATE == MENU_STATE_PROPERTIES)? icon_options_dark : icon_options, 25, 0); + Draw_Image((MENU_STATE == MENU_STATE_SETTINGS) || (MENU_STATE == MENU_STATE_SORT)? icon_settings_dark : icon_settings, 50, 0); + Draw_Image(MENU_STATE == MENU_STATE_FTP? icon_ftp_dark : icon_ftp, 75, 0); + + Draw_Image(BROWSE_STATE == BROWSE_STATE_SD? icon_sd_dark : icon_sd, 250, 0); + Draw_Image(BROWSE_STATE == BROWSE_STATE_NAND? icon_secure_dark : icon_secure, 275, 0); + Draw_Image(icon_search, 300, 0); +} + +void Menu_ControlMenuBar(u32 input) { - //TouchInfo touchInfo; - //Touch_Init(&touchInfo); + if ((input & KEY_TOUCH) && (TouchInRect(0, 0, 22, 20))) + { + wait(1); + MENU_STATE = MENU_STATE_HOME; + } + else if ((input & KEY_TOUCH) && (TouchInRect(23, 0, 47, 20))) + { + wait(1); + MENU_STATE = MENU_STATE_FILEOPTIONS; + } + else if ((input & KEY_TOUCH) && (TouchInRect(48, 0, 72, 20))) + { + wait(1); + MENU_STATE = MENU_STATE_SETTINGS; + } + else if (((input & KEY_TOUCH) && (TouchInRect(73, 0, 97, 20))) || (input & KEY_SELECT)) + { + wait(1); + MENU_STATE = MENU_STATE_FTP; + Menu_DisplayFTP(); + } +} + +static void Menu_ControlBrowseOptions(u32 input) +{ + if ((input & KEY_TOUCH) && (TouchInRect(247, 0, 272, 20))) // SD + { + wait(1); + FS_Write(archive, "/3ds/3DShell/lastdir.txt", START_PATH); + strcpy(cwd, START_PATH); + + BROWSE_STATE = BROWSE_STATE_SD; + + FS_CloseArchive(archive); + FS_OpenArchive(&archive, ARCHIVE_SDMC); + + Dirbrowse_PopulateFiles(true); + } + else if ((input & KEY_TOUCH) && (TouchInRect(273, 0, 292, 20))) // CTR NAND + { + wait(1); + strcpy(cwd, START_PATH); + + BROWSE_STATE = BROWSE_STATE_NAND; + + FS_CloseArchive(archive); + FS_OpenArchive(&archive, ARCHIVE_NAND_CTR_FS); + + Dirbrowse_PopulateFiles(true); + } + + if ((input & KEY_TOUCH) && (TouchInRect(293, 0, 320, 20))) + { + char *path = (char *)malloc(256); + strcpy(path, OSK_Get(256, "/", "Enter path")); + + if (FS_DirExists(archive, path)) + { + strcpy(cwd, path); + Dirbrowse_PopulateFiles(true); + } + + free(path); + } +} +void Menu_Main(void) +{ Dirbrowse_PopulateFiles(false); memset(multi_select, 0, sizeof(multi_select)); // Reset all multi selected items @@ -140,39 +196,46 @@ void Menu_Main(void) Dirbrowse_DisplayFiles(); C2D_SceneBegin(RENDER_BOTTOM); - Draw_Rect(0, 0, 320, 20, config_dark_theme? STATUS_BAR_DARK : STATUS_BAR_LIGHT); // Status bar - Draw_Rect(0, 20, 320, 220, config_dark_theme? MENU_BAR_DARK : MENU_BAR_LIGHT); // Menu bar - - Draw_Image(config_dark_theme? icon_home_dark : icon_home, 0, 0); - Draw_Image(config_dark_theme? icon_options_dark : icon_options, 25, 0); - Draw_Image(config_dark_theme? icon_settings_dark : icon_settings, 50, 0); - Draw_Image(config_dark_theme? icon_ftp_dark : icon_ftp, 75, 0); + Draw_Rect(0, 0, 320, 20, config_dark_theme? STATUS_BAR_DARK : MENU_BAR_LIGHT); // Status bar + Draw_Rect(0, 20, 320, 220, config_dark_theme? MENU_BAR_DARK : STATUS_BAR_LIGHT); // Menu bar - Draw_Image(config_dark_theme? icon_sd_dark : icon_sd, 250, 0); - Draw_Image(config_dark_theme? icon_secure_dark : icon_secure, 275, 0); - Draw_Image(icon_search, 300, 0); - - /*else if (MENU_DEFAULT_STATE == MENU_STATE_OPTIONS) - Menu_DisplayFileOptions(); - else if (MENU_DEFAULT_STATE == MENU_STATE_PROPERTIES) - Menu_DisplayProperties(); - else if (MENU_DEFAULT_STATE == MENU_STATE_SETTINGS) - Menu_DisplaySettings(); - else if (MENU_DEFAULT_STATE == MENU_STATE_SORT) - Menu_DisplaySort(); - else if (MENU_DEFAULT_STATE == MENU_STATE_DIALOG) - Menu_DisplayDeleteDialog();*/ - - Draw_EndFrame(); + Menu_DrawMenuBar(); hidScanInput(); - //Touch_Process(&touchInfo); u32 kDown = hidKeysDown(); + Menu_ControlMenuBar(kDown); - if (MENU_DEFAULT_STATE == MENU_STATE_HOME) + if (MENU_STATE == MENU_STATE_HOME) { Menu_ControlHome(kDown); - //Menu_TouchHome(touchInfo); + Menu_ControlBrowseOptions(kDown); + } + else if (MENU_STATE == MENU_STATE_FILEOPTIONS) + { + Menu_DisplayFileOptions(); + Menu_ControlFileOptions(kDown); + } + else if (MENU_STATE == MENU_STATE_PROPERTIES) + { + Menu_DisplayProperties(); + Menu_ControlProperties(kDown); } + else if (MENU_STATE == MENU_STATE_DIALOG) + { + Menu_DisplayDeleteDialog(); + Menu_ControlDeleteDialog(kDown); + } + else if (MENU_STATE == MENU_STATE_SETTINGS) + { + Menu_DisplaySettings(); + Menu_ControlSettings(kDown); + } + else if (MENU_STATE == MENU_STATE_SORT) + { + Menu_DisplaySortSettings(); + Menu_ControlSortSettings(kDown); + } + + Draw_EndFrame(); } } \ No newline at end of file diff --git a/source/menus/menu_music.c b/source/menus/menu_music.c index 393e558..740c601 100644 --- a/source/menus/menu_music.c +++ b/source/menus/menu_music.c @@ -233,8 +233,8 @@ void Music_Player(char *path) u32 kDown = hidKeysDown(); u32 kHeld = hidKeysHeld(); - if ((kDown & KEY_A) || ((TouchInRect(114, 76, 204, 164)) && (kDown & KEY_TOUCH))) - Audio_TogglePlayback(SFX); + //if ((kDown & KEY_A) || ((TouchInRect(114, 76, 204, 164)) && (kDown & KEY_TOUCH))) + // Audio_TogglePlayback(SFX); //pp2d_begin_draw(GFX_BOTTOM, GFX_LEFT); diff --git a/source/menus/menu_settings.c b/source/menus/menu_settings.c new file mode 100644 index 0000000..8f45bd3 --- /dev/null +++ b/source/menus/menu_settings.c @@ -0,0 +1,135 @@ +#include "C2D_helper.h" +#include "common.h" +#include "config.h" +#include "dirbrowse.h" +#include "menu_main.h" +#include "menu_settings.h" +#include "status_bar.h" +#include "textures.h" +#include "utils.h" + +static int selection = 0, max_items = 3; + +void Menu_DisplaySortSettings(void) +{ + Draw_Rect(0, 0, 320, 20, config_dark_theme? STATUS_BAR_DARK : MENU_BAR_LIGHT); + Draw_Rect(0, 20, 320, 220, config_dark_theme? BLACK_BG : WHITE); + + Menu_DrawMenuBar(); + + Draw_Rect(0, 20, 400, 35, config_dark_theme? MENU_BAR_DARK : STATUS_BAR_LIGHT); // Menu bar + Draw_Text(10, 30, 0.48f, WHITE, "Sorting options"); + + Draw_Rect(0, 55 + (selection * 40), 320, 40, config_dark_theme? SELECTOR_COLOUR_DARK : SELECTOR_COLOUR_LIGHT); + + Draw_Text(10, 60, 0.48f, config_dark_theme? WHITE : BLACK, "Alphabetical \uE01B"); + Draw_Text(10, 75, 0.45f, config_dark_theme? WHITE : BLACK, "Sort alphabetically in ascending order."); + Draw_Text(10, 100, 0.48f, config_dark_theme? WHITE : BLACK, "Alphabetical \uE01C"); + Draw_Text(10, 115, 0.45f, config_dark_theme? WHITE : BLACK, "Sort alphabetically in descending order."); + Draw_Text(10, 140, 0.48f, config_dark_theme? WHITE : BLACK, "Size \uE01B"); + Draw_Text(10, 155, 0.45f, config_dark_theme? WHITE : BLACK, "Sort by size (largest first)."); + Draw_Text(10, 180, 0.48f, config_dark_theme? WHITE : BLACK, "Size \uE01C"); + Draw_Text(10, 195, 0.45f, config_dark_theme? WHITE : BLACK, "Sort by size (smallest first)."); + + Draw_Image(config_sort_by == 0? (config_dark_theme? icon_radio_dark_on : icon_radio_on) : (config_dark_theme? icon_radio_dark_off : icon_radio_off), 270, 60); + Draw_Image(config_sort_by == 1? (config_dark_theme? icon_radio_dark_on : icon_radio_on) : (config_dark_theme? icon_radio_dark_off : icon_radio_off), 270, 100); + Draw_Image(config_sort_by == 2? (config_dark_theme? icon_radio_dark_on : icon_radio_on) : (config_dark_theme? icon_radio_dark_off : icon_radio_off), 270, 140); + Draw_Image(config_sort_by == 3? (config_dark_theme? icon_radio_dark_on : icon_radio_on) : (config_dark_theme? icon_radio_dark_off : icon_radio_off), 270, 180); +} + +void Menu_ControlSortSettings(u32 input) +{ + if (input & KEY_B) + MENU_STATE = MENU_STATE_SETTINGS; + + if (input & KEY_A) + { + switch (selection) + { + case 0: + config_sort_by = 0; + break; + case 1: + config_sort_by = 1; + break; + case 2: + config_sort_by = 2; + break; + case 3: + config_sort_by = 3; + break; + } + + Dirbrowse_PopulateFiles(true); + Config_Save(config_dark_theme, config_hidden_files, config_sort_by); + } + + if (input & KEY_DDOWN) + selection++; + else if (input & KEY_DUP) + selection--; + + Utils_SetMax(&selection, 0, max_items); + Utils_SetMin(&selection, max_items, 0); +} + +void Menu_DisplaySettings(void) +{ + Draw_Rect(0, 0, 320, 20, config_dark_theme? STATUS_BAR_DARK : MENU_BAR_LIGHT); + Draw_Rect(0, 20, 320, 220, config_dark_theme? BLACK_BG : WHITE); + + Menu_DrawMenuBar(); + + Draw_Rect(0, 20, 400, 35, config_dark_theme? MENU_BAR_DARK : STATUS_BAR_LIGHT); // Menu bar + Draw_Text(10, 30, 0.48f, WHITE, "Settings"); + + Draw_Rect(0, 55 + (selection * 40), 320, 40, config_dark_theme? SELECTOR_COLOUR_DARK : SELECTOR_COLOUR_LIGHT); + + Draw_Text(10, 60, 0.48f, config_dark_theme? WHITE : BLACK, "Sort by"); + Draw_Text(10, 75, 0.45f, config_dark_theme? WHITE : BLACK, "Select between various sorting options."); + Draw_Text(10, 100, 0.48f, config_dark_theme? WHITE : BLACK, "Dark theme"); + Draw_Text(10, 115, 0.45f, config_dark_theme? WHITE : BLACK, "Enables dark theme mode."); + Draw_Text(10, 140, 0.48f, config_dark_theme? WHITE : BLACK, "Hidden files"); + Draw_Text(10, 155, 0.45f, config_dark_theme? WHITE : BLACK, "Displays hidden files."); + Draw_Text(10, 180, 0.48f, config_dark_theme? WHITE : BLACK, "About"); + Draw_Text(10, 195, 0.45f, config_dark_theme? WHITE : BLACK, "Details about application."); + + if (config_dark_theme) + Draw_Image(config_dark_theme? icon_toggle_dark_on : icon_toggle_on, 270, 97.5); + else + Draw_Image(icon_toggle_off, 270, 97.5); + + Draw_Image(config_hidden_files? (config_dark_theme? icon_toggle_dark_on : icon_toggle_on) : icon_toggle_off, 270, 137.5); +} + +void Menu_ControlSettings(u32 input) +{ + if (input & KEY_B) + MENU_STATE = MENU_STATE_HOME; + + if (input & KEY_A) + { + switch (selection) + { + case 0: + MENU_STATE = MENU_STATE_SORT; + break; + case 1: + config_dark_theme = !config_dark_theme; + Config_Save(config_dark_theme, config_hidden_files, config_sort_by); + break; + case 2: + config_hidden_files = !config_hidden_files; + Config_Save(config_dark_theme, config_hidden_files, config_sort_by); + break; + } + } + + if (input & KEY_DDOWN) + selection++; + else if (input & KEY_DUP) + selection--; + + Utils_SetMax(&selection, 0, max_items); + Utils_SetMin(&selection, max_items, 0); +} diff --git a/source/progress_bar.c b/source/progress_bar.c index e260e03..badb05d 100644 --- a/source/progress_bar.c +++ b/source/progress_bar.c @@ -8,15 +8,24 @@ void ProgressBar_DisplayProgress(char *msg, char *src, u32 offset, u32 size) { + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + C2D_TargetClear(RENDER_BOTTOM, config_dark_theme? BLACK_BG : WHITE); + C2D_SceneBegin(RENDER_BOTTOM); + + Draw_Rect(0, 0, 320, 20, config_dark_theme? STATUS_BAR_DARK : MENU_BAR_LIGHT); // Status bar + Draw_Rect(0, 20, 320, 220, config_dark_theme? MENU_BAR_DARK : STATUS_BAR_LIGHT); // Menu bar + float text_width = 0; - Draw_GetTextSize(0.48f, &text_width, NULL, src); + Draw_GetTextSize(0.45f, &text_width, NULL, src); Draw_Image(config_dark_theme? dialog_dark : dialog, ((320 - (dialog.subtex->width)) / 2), ((240 - (dialog.subtex->height)) / 2)); - Draw_Text(((320 - (dialog.subtex->width)) / 2) + 80, ((240 - (dialog.subtex->height)) / 2) + 45, 0.48f, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR, msg); + Draw_Text(((320 - (dialog.subtex->width)) / 2) + 6, ((240 - (dialog.subtex->height)) / 2) + 6, 0.45f, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR, msg); + + Draw_Text(((320 - (text_width)) / 2), ((240 - (dialog.subtex->height)) / 2) + 30, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, src); - Draw_Text(((320 - (text_width)) / 2), ((240 - (dialog.subtex->height)) / 2) + 111, 0.48f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, src); + Draw_Rect(((320 - (dialog.subtex->width)) / 2) + 20, ((240 - (dialog.subtex->height)) / 2) + 60, 240, 4, config_dark_theme? SELECTOR_COLOUR_DARK : SELECTOR_COLOUR_LIGHT); + Draw_Rect(((320 - (dialog.subtex->width)) / 2) + 20, ((240 - (dialog.subtex->height)) / 2) + 60, (double)offset / (double)size * 240.0, 4, config_dark_theme? TITLE_COLOUR : PROGRESS_COLOUR); - Draw_Rect(((320 - (dialog.subtex->width)) / 2) + 80, ((240 - (dialog.subtex->height)) / 2) + 178, 240, 12, config_dark_theme? SELECTOR_COLOUR_DARK : SELECTOR_COLOUR_LIGHT); - Draw_Rect(((320 - (dialog.subtex->width)) / 2) + 80, ((240 - (dialog.subtex->height)) / 2) + 178, (double)offset / (double)size * 240.0, 12, config_dark_theme? TITLE_COLOUR : PROGRESS_COLOUR); + Draw_EndFrame(); } \ No newline at end of file diff --git a/source/screenshot.c b/source/screenshot.c index 55a4b62..727850d 100644 --- a/source/screenshot.c +++ b/source/screenshot.c @@ -118,7 +118,7 @@ static void generateScreenshotFileName(int number, char *fileName, const char *e int month = timeStruct->tm_mon + 1; int year = timeStruct->tm_year + 1900; - if (!(BROWSE_STATE == STATE_NAND)) + if (!(BROWSE_STATE == BROWSE_STATE_NAND)) { if (!(FS_DirExists(archive, "/screenshots/"))) FS_MakeDir(archive, "/screenshots"); diff --git a/source/touch.c b/source/touch.c index d24801f..ae588bd 100644 --- a/source/touch.c +++ b/source/touch.c @@ -4,16 +4,16 @@ u16 Touch_GetX(void) { - touchPosition pos; - hidTouchRead(&pos); - - return pos.px; + touchPosition pos; + hidTouchRead(&pos); + + return pos.px; } u16 Touch_GetY(void) { - touchPosition pos; - hidTouchRead(&pos); - - return pos.py; + touchPosition pos; + hidTouchRead(&pos); + + return pos.py; } \ No newline at end of file From bcc32b297c3f3e8958b06e9184c9c26333264b8f Mon Sep 17 00:00:00 2001 From: Joel16 Date: Sun, 8 Jul 2018 15:21:39 -0500 Subject: [PATCH 03/33] Populate files after using hidden toggle --- .travis.yml | 2 +- .../btn_material_light_toggle_off_normalx.png | Bin 15698 -> 0 bytes ...tn_material_light_toggle_on_normal_darkx.png | Bin 15698 -> 0 bytes source/menus/menu_settings.c | 1 + 4 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 res/drawable/btn_material_light_toggle_off_normalx.png delete mode 100644 res/drawable/btn_material_light_toggle_on_normal_darkx.png diff --git a/.travis.yml b/.travis.yml index d18f846..bf5b733 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,4 +21,4 @@ deploy: skip_cleanup: true github_token: $GITHUB_TOKEN # Set in travis-ci.org dashboard on: - branch: master \ No newline at end of file + branch: C2D \ No newline at end of file diff --git a/res/drawable/btn_material_light_toggle_off_normalx.png b/res/drawable/btn_material_light_toggle_off_normalx.png deleted file mode 100644 index 18b8202fe24345adb0ebd1fe927af02be213a0e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15698 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 C8+^9_ diff --git a/source/menus/menu_settings.c b/source/menus/menu_settings.c index 8f45bd3..b7ae31c 100644 --- a/source/menus/menu_settings.c +++ b/source/menus/menu_settings.c @@ -121,6 +121,7 @@ void Menu_ControlSettings(u32 input) case 2: config_hidden_files = !config_hidden_files; Config_Save(config_dark_theme, config_hidden_files, config_sort_by); + Dirbrowse_PopulateFiles(true); break; } } From e182eb36ff8151bf85dc591a4f40891424854368 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Sun, 8 Jul 2018 15:50:23 -0500 Subject: [PATCH 04/33] Try to build properly --- .travis/build.sh | 6 +++--- source/menus/menu_main.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis/build.sh b/.travis/build.sh index 1762df7..73380ed 100644 --- a/.travis/build.sh +++ b/.travis/build.sh @@ -2,6 +2,6 @@ source /etc/profile.d/devkit-env.sh -cd /3DShell - -make \ No newline at end of file +make clean && make +rm .gitignore .gitmodules .travis.yml LICENSE Makefile README.md +rm -rf .travis build include res romfs source \ No newline at end of file diff --git a/source/menus/menu_main.c b/source/menus/menu_main.c index bcf6e9b..a5ca81d 100644 --- a/source/menus/menu_main.c +++ b/source/menus/menu_main.c @@ -22,8 +22,8 @@ static void Menu_HandleMultiSelect(void) { // multi_select_dir can only hold one dir strcpy(multi_select_dir_old, cwd); - //if (strcmp(multi_select_dir_old, multi_select_dir) != 0) - // FileOptions_ResetClipboard(); + if (strcmp(multi_select_dir_old, multi_select_dir) != 0) + FileOptions_ResetClipboard(); char path[256]; File *file = Dirbrowse_GetFileIndex(position); From 1d232b889c794d6961eec0332d6f71c65bbf6716 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Sun, 8 Jul 2018 15:54:04 -0500 Subject: [PATCH 05/33] Try to build properly 2 --- .travis/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis/build.sh b/.travis/build.sh index 73380ed..ca5ba74 100644 --- a/.travis/build.sh +++ b/.travis/build.sh @@ -1,7 +1,7 @@ #!/bin/bash -ex source /etc/profile.d/devkit-env.sh - +cd /3DShell make clean && make rm .gitignore .gitmodules .travis.yml LICENSE Makefile README.md rm -rf .travis build include res romfs source \ No newline at end of file From 73060a4d0df31f07eb0976ec6fc56102898cbe50 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Sun, 8 Jul 2018 15:57:11 -0500 Subject: [PATCH 06/33] Try to build properly 3 --- include/menus/menu_fileoptions.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/menus/menu_fileoptions.h b/include/menus/menu_fileoptions.h index 8cda47e..74229fa 100644 --- a/include/menus/menu_fileoptions.h +++ b/include/menus/menu_fileoptions.h @@ -3,6 +3,7 @@ #include <3ds.h> +void FileOptions_ResetClipboard(void); void Menu_DisplayDeleteDialog(void); void Menu_ControlDeleteDialog(u32 input); void Menu_DisplayProperties(void); From 8e6d235ac000df866db07d542fcaa626cc9d8bc4 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Sun, 8 Jul 2018 15:59:43 -0500 Subject: [PATCH 07/33] Remove .elf and .smdh --- .travis/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis/build.sh b/.travis/build.sh index ca5ba74..bd6e436 100644 --- a/.travis/build.sh +++ b/.travis/build.sh @@ -3,5 +3,5 @@ source /etc/profile.d/devkit-env.sh cd /3DShell make clean && make -rm .gitignore .gitmodules .travis.yml LICENSE Makefile README.md +rm .gitignore .gitmodules .travis.yml 3DShell.elf 3DShell.smdh LICENSE Makefile README.md rm -rf .travis build include res romfs source \ No newline at end of file From cd1d1e385f370d376b38a7cee1c345c6bd3a6147 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Sun, 8 Jul 2018 16:57:59 -0500 Subject: [PATCH 08/33] Add networking stuff for updater --- Makefile | 15 +- include/cia.h | 8 + include/common.h | 1 + include/net.h | 8 + include/textures.h | 1 + res/drawable/ic_material_light_tab.png | Bin 0 -> 385 bytes res/drawable/ic_material_light_tab_dark.png | Bin 0 -> 385 bytes res/drawable/sprites.t3s | 2 + source/cia.c | 116 +++++++++++++++ source/menus/menu_main.c | 2 +- source/net.c | 157 ++++++++++++++++++++ source/textures.c | 2 + 12 files changed, 309 insertions(+), 3 deletions(-) create mode 100644 include/cia.h create mode 100644 include/net.h create mode 100644 res/drawable/ic_material_light_tab.png create mode 100644 res/drawable/ic_material_light_tab_dark.png create mode 100644 source/cia.c create mode 100644 source/net.c diff --git a/Makefile b/Makefile index 48dee92..d5a420b 100644 --- a/Makefile +++ b/Makefile @@ -40,6 +40,13 @@ GRAPHICS := res/drawable ROMFS := romfs GFXBUILD := $(ROMFS)/res/drawable +APP_TITLE := 3DShell +VERSION_MAJOR := 3 +VERSION_MINOR := 0 +VERSION_MICRO := 0 + +GITVERSION := $(shell git log -1 --pretty='%h') + #--------------------------------------------------------------------------------- # options for code generation #--------------------------------------------------------------------------------- @@ -47,7 +54,10 @@ ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft CFLAGS := -g -Werror -O2 -mword-relocations \ -fomit-frame-pointer -ffunction-sections \ - $(ARCH) + -DVERSION_MAJOR=$(VERSION_MAJOR) -DVERSION_MINOR=$(VERSION_MINOR) -DVERSION_MICRO=$(VERSION_MICRO) \ + -DAPP_TITLE="\"$(APP_TITLE)\"" \ + -DGITVERSION="\"${GITVERSION}\"" \ + $(ARCH) CFLAGS += $(INCLUDE) -DARM11 -D_3DS @@ -56,7 +66,8 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 ASFLAGS := -g $(ARCH) LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) -LIBS := -lturbojpeg -lpng -lcitro2d -lcitro3d -lctru -lm -lz +LIBS := -lcurl -lmbedtls -lmbedx509 -lmbedcrypto -lmpg123 -lvorbisidec -logg \ + -lturbojpeg -lpng -lcitro2d -lcitro3d -lctru -lm -lz #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing diff --git a/include/cia.h b/include/cia.h new file mode 100644 index 0000000..735161d --- /dev/null +++ b/include/cia.h @@ -0,0 +1,8 @@ +#ifndef _3D_SHELL_CIA_H +#define _3D_SHELL_CIA_H + +#include <3ds.h> + +Result CIA_InstallTitle(const char *path, FS_MediaType media, bool update); + +#endif \ No newline at end of file diff --git a/include/common.h b/include/common.h index 2b18eee..6cdcef0 100644 --- a/include/common.h +++ b/include/common.h @@ -19,6 +19,7 @@ #define MENU_STATE_THEMES 6 #define MENU_STATE_DIALOG 7 #define MENU_STATE_PROPERTIES 8 +#define MENU_STATE_UPDATER 9 #define BROWSE_STATE_SD 0 #define BROWSE_STATE_NAND 1 diff --git a/include/net.h b/include/net.h new file mode 100644 index 0000000..bb277fb --- /dev/null +++ b/include/net.h @@ -0,0 +1,8 @@ +#ifndef _3D_SHELL_NET_H +#define _3D_SHELL_NET_H + +#include <3ds.h> + +Result Net_DownloadFile(const char *url, const char *path); + +#endif \ No newline at end of file diff --git a/include/textures.h b/include/textures.h index 6926792..55c0872 100644 --- a/include/textures.h +++ b/include/textures.h @@ -9,6 +9,7 @@ C2D_Image icon_app, icon_archive, icon_audio, icon_dir, icon_file, icon_image, i dialog, options_dialog, properties_dialog, dialog_dark, options_dialog_dark, properties_dialog_dark, \ icon_home, icon_home_dark, icon_options, icon_options_dark, icon_settings, icon_settings_dark, \ icon_ftp, icon_ftp_dark, icon_sd, icon_sd_dark, icon_secure, icon_secure_dark, icon_search, \ + icon_updates, icon_updates_dark, \ default_artwork, default_artwork_blur, btn_play, btn_pause, btn_rewind, btn_forward, \ btn_repeat, btn_shuffle, btn_repeat_overlay, btn_shuffle_overlay, \ icon_nav_drawer, icon_actions, icon_back; diff --git a/res/drawable/ic_material_light_tab.png b/res/drawable/ic_material_light_tab.png new file mode 100644 index 0000000000000000000000000000000000000000..9eed359e75510225a86bb8ae46f39308759ab219 GIT binary patch literal 385 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQaT>(BJu0Yy&aDa)O4zqx+(JKk^ z3ua(TDi2}gsGFI~EmWV+!>ZHPK5cTN982;nU*6_C)9DjsnfZLMKe@p55NuJiWhP-ZQ`_AWrZIM&RC>+EWLe3_$LeZwd+HR+me7FQ@YK}M;OhZNsiCeySFT*T vbm31MJZ=f!FBN9F& literal 0 HcmV?d00001 diff --git a/res/drawable/ic_material_light_tab_dark.png b/res/drawable/ic_material_light_tab_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..969f532ab8b16a8544012f55d2c9e39b37d3c0b8 GIT binary patch literal 385 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQaT>(BJu0Z<#-~ivuGkSrp(JKk^ z3ua(TDi2}gsGFI~EmWV+!>ZHPK5cTN982;nU*6_C)9DjsnfZLMKe@p55NuJiWhP-ZQ`_AWrZIM&RC>+EWLe3_$LeZwd+HR+me7FQ@YK}M;OhZNsiCeySFT*T vbm31MJZ=f!FB + +#include "cia.h" +#include "progress_bar.h" +#include "utils.h" + +static Result CIA_RemoveTitle(u64 titleID, FS_MediaType media) +{ + u32 count = 0; + + Result ret = AM_GetTitleCount(media, &count); + if (R_FAILED(ret)) + return ret; + + u32 read = 0; + u64 *titleIDs = malloc(count *sizeof(u64)); + + ret = AM_GetTitleList(&read, media, count, titleIDs); + if (R_FAILED(ret)) + { + free(titleIDs); + return ret; + } + + for (unsigned int i = 0; i < read; i++) + { + if (titleIDs[i] == titleID) + { + ret = AM_DeleteAppTitle(media, titleID); + if (R_FAILED(ret)) + { + free(titleIDs); + return ret; + } + break; + } + } + + free(titleIDs); + + if (R_FAILED(ret)) + return ret; + + return 0; +} + +static void CIA_LaunchTitle(u64 titleId, FS_MediaType mediaType) +{ + u8 param[0x300]; + u8 hmac[0x20]; + + APT_PrepareToDoApplicationJump(0, titleId, mediaType); + APT_DoApplicationJump(param, sizeof(param), hmac); +} + +Result CIA_InstallTitle(const char *path, FS_MediaType media, bool update) +{ + u32 bytesRead = 0, bytesWritten = 0; + u64 fileSize = 0, offset = 0; + Handle ciaHandle, fileHandle; + AM_TitleEntry title; + + Result ret = 0; + + char *filename = Utils_Basename(path); + + if (R_FAILED(ret = FSUSER_OpenFileDirectly(&fileHandle, ARCHIVE_SDMC, fsMakePath(PATH_ASCII, ""), fsMakePath(PATH_ASCII, path), FS_OPEN_READ, 0))) + return ret; + + if (R_FAILED(ret = AM_GetCiaFileInfo(media, &title, fileHandle))) + return ret; + + if (!update) // As long as we aren't updating 3DShell, remove the title before installing. + { + if (R_FAILED(ret = CIA_RemoveTitle(title.titleID, media))) + return ret; + } + + if (R_FAILED(ret = FSFILE_GetSize(fileHandle, &fileSize))) + return ret; + + if (R_FAILED(ret = AM_StartCiaInstall(media, &ciaHandle))) + return ret; + + u8 *cia_buffer = malloc((512 *1024)); + + while (offset < fileSize) + { + u64 bytesRemaining = fileSize - offset; + ProgressBar_DisplayProgress("Installing", filename, bytesRead, fileSize); + + ret = FSFILE_Read(fileHandle, &bytesRead, offset, cia_buffer, (512 *1024)); // (512 *1024) - chunk size + ret = FSFILE_Write(ciaHandle, &bytesWritten, offset, cia_buffer, bytesRead, 0); + + if (bytesRead != bytesWritten) + { + AM_CancelCIAInstall(ciaHandle); + return ret; + } + + offset += bytesWritten; + } + + free(cia_buffer); + + if (R_FAILED(ret = AM_FinishCiaInstall(ciaHandle))) + return ret; + + if (R_FAILED(ret = svcCloseHandle(fileHandle))) + return ret; + + if (update) + CIA_LaunchTitle(title.titleID, MEDIATYPE_SD); + + return 2; +} diff --git a/source/menus/menu_main.c b/source/menus/menu_main.c index a5ca81d..c80ba7a 100644 --- a/source/menus/menu_main.c +++ b/source/menus/menu_main.c @@ -103,7 +103,7 @@ void Menu_DrawMenuBar(void) Draw_Image((MENU_STATE == MENU_STATE_FILEOPTIONS) || (MENU_STATE == MENU_STATE_PROPERTIES)? icon_options_dark : icon_options, 25, 0); Draw_Image((MENU_STATE == MENU_STATE_SETTINGS) || (MENU_STATE == MENU_STATE_SORT)? icon_settings_dark : icon_settings, 50, 0); Draw_Image(MENU_STATE == MENU_STATE_FTP? icon_ftp_dark : icon_ftp, 75, 0); - + Draw_Image(MENU_STATE == MENU_STATE_UPDATER? icon_updates_dark : icon_updates, 100, 0.5); Draw_Image(BROWSE_STATE == BROWSE_STATE_SD? icon_sd_dark : icon_sd, 250, 0); Draw_Image(BROWSE_STATE == BROWSE_STATE_NAND? icon_secure_dark : icon_secure, 275, 0); Draw_Image(icon_search, 300, 0); diff --git a/source/net.c b/source/net.c new file mode 100644 index 0000000..fe7a8ba --- /dev/null +++ b/source/net.c @@ -0,0 +1,157 @@ +#include +#include +#include + +#include "fs.h" +#include "progress_bar.h" +#include "net.h" +#include "utils.h" + +#define USER_AGENT APP_TITLE + +static char *result_buf = NULL; +static size_t result_sz = 0; +static size_t result_written = 0; + +// following function is from +// https://github.com/angelsl/libctrfgh/blob/master/curl_test/src/main.c +static size_t Net_HandleData(char *ptr, size_t size, size_t nmemb, void *userdata) +{ + (void) userdata; + const size_t bsz = size*nmemb; + + if (result_sz == 0 || !result_buf) + { + result_sz = 0x1000; + result_buf = (char*)malloc(result_sz); + } + + bool need_realloc = false; + while (result_written + bsz > result_sz) + { + result_sz <<= 1; + need_realloc = true; + } + + if (need_realloc) + { + char *new_buf = (char*)realloc(result_buf, result_sz); + if (!new_buf) + { + return 0; + } + result_buf = new_buf; + } + + if (!result_buf) + { + return 0; + } + + memcpy(result_buf + result_written, ptr, bsz); + result_written += bsz; + return bsz; +} + +// From MultiUpdater +static Result Net_SetupContext(CURL *hnd, const char *url) +{ + curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, 102400L); + curl_easy_setopt(hnd, CURLOPT_URL, url); + curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(hnd, CURLOPT_USERAGENT, USER_AGENT); + curl_easy_setopt(hnd, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L); + curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_2TLS); + curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, Net_HandleData); + curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(hnd, CURLOPT_STDERR, stdout); + + return 0; +} + +// From MultiUpdater +Result Net_DownloadFile(const char *url, const char *path) +{ + Result ret = 0; + printf("Downloading from:\n%s\nto:\n%s\n", url, path); + + void *socubuf = memalign(0x1000, 0x100000); + if (!socubuf) + return -1; + + ret = socInit((u32*)socubuf, 0x100000); + if (R_FAILED(ret)) + { + free(socubuf); + return ret; + } + + CURL *hnd = curl_easy_init(); + ret = Net_SetupContext(hnd, url); + + if (ret != 0) + { + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_sz = 0; + result_written = 0; + return ret; + } + + Handle fileHandle; + u64 offset = 0; + u32 bytesWritten = 0; + + //ret = openFile(&fileHandle, path.c_str(), true); + ret = FS_Open(&fileHandle, archive, path, (FS_OPEN_WRITE | FS_OPEN_CREATE)); + + if (R_FAILED(ret)) + { + printf("Error: couldn't open file to write.\n"); + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_sz = 0; + result_written = 0; + return -1; + } + + u64 startTime = osGetTime(); + + CURLcode cres = curl_easy_perform(hnd); + curl_easy_cleanup(hnd); + + if (cres != CURLE_OK) + { + printf("Error in:\ncurl\n"); + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_sz = 0; + result_written = 0; + return -1; + } + + FSFILE_Write(fileHandle, &bytesWritten, offset, result_buf, result_written, 0); + + ProgressBar_DisplayProgress("Downloading", Utils_Basename(path), 0, 0); + + u64 endTime = osGetTime(); + u64 totalTime = endTime - startTime; + printf("Download took %llu milliseconds.\n", totalTime); + + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_sz = 0; + result_written = 0; + FSFILE_Close(fileHandle); + return 0; +} diff --git a/source/textures.c b/source/textures.c index 361bfe5..69ce56d 100644 --- a/source/textures.c +++ b/source/textures.c @@ -49,6 +49,8 @@ void Textures_Load(void) icon_secure = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_secure_idx); icon_secure_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_secure_dark_idx); icon_search = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_search_idx); + icon_updates = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_tab_idx); + icon_updates_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_tab_dark_idx); btn_play = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_playback_play_idx); btn_pause = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_playback_pause_idx); btn_rewind = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_playback_rewind_idx); From 48dd54ad5fae922895345949879149daa51ef6a9 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Sun, 8 Jul 2018 19:53:17 -0500 Subject: [PATCH 09/33] Fix updater --- Makefile | 2 + include/common.h | 3 +- include/menu_update.h | 11 +++ source/menus/menu_main.c | 32 +++++-- source/menus/menu_update.c | 167 +++++++++++++++++++++++++++++++++++++ 5 files changed, 208 insertions(+), 7 deletions(-) create mode 100644 include/menu_update.h create mode 100644 source/menus/menu_update.c diff --git a/Makefile b/Makefile index d5a420b..6933e17 100644 --- a/Makefile +++ b/Makefile @@ -172,6 +172,8 @@ endif #--------------------------------------------------------------------------------- all: $(BUILD) $(GFXBUILD) $(DEPSDIR) $(ROMFS_T3XFILES) $(T3XHFILES) @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + @echo "${VERSION_MAJOR}${VERSION_MINOR}${VERSION_MICRO}" > UPDATE_MILESTONE.txt # For maintainer builds + @echo "${GITVERSION}" > UPDATE_NIGHTLY.txt # For maintainer builds $(BUILD): @mkdir -p $@ diff --git a/include/common.h b/include/common.h index 6cdcef0..548ef62 100644 --- a/include/common.h +++ b/include/common.h @@ -19,7 +19,8 @@ #define MENU_STATE_THEMES 6 #define MENU_STATE_DIALOG 7 #define MENU_STATE_PROPERTIES 8 -#define MENU_STATE_UPDATER 9 +#define MENU_STATE_UPDATE 9 +#define MENU_STATE_UPDATE_2 10 #define BROWSE_STATE_SD 0 #define BROWSE_STATE_NAND 1 diff --git a/include/menu_update.h b/include/menu_update.h new file mode 100644 index 0000000..368faba --- /dev/null +++ b/include/menu_update.h @@ -0,0 +1,11 @@ +#ifndef _3D_SHELL_MENU_UPDATE_H +#define _3D_SHELL_MENU_UPDATE_H + +#include <3ds.h> + +void Menu_DisplayUpdate(void); +void Menu_DisplayUpdate2(void); +void Menu_ControlUpdate(u32 input); +void Menu_ControlUpdate2(u32 input); + +#endif \ No newline at end of file diff --git a/source/menus/menu_main.c b/source/menus/menu_main.c index c80ba7a..e261bfa 100644 --- a/source/menus/menu_main.c +++ b/source/menus/menu_main.c @@ -10,8 +10,10 @@ #include "menu_ftp.h" #include "menu_fileoptions.h" #include "menu_main.h" +#include "menu_update.h" #include "menu_settings.h" #include "status_bar.h" +#include "screenshot.h" #include "textures.h" #include "touch.h" #include "utils.h" @@ -51,11 +53,6 @@ static void Menu_HandleMultiSelect(void) static void Menu_ControlHome(u32 input) { - if (input & KEY_START) - longjmp(exitJmp, 1); - - Menu_ControlMenuBar(input); - if (fileCount > 0) { if (input & KEY_DUP) @@ -103,7 +100,7 @@ void Menu_DrawMenuBar(void) Draw_Image((MENU_STATE == MENU_STATE_FILEOPTIONS) || (MENU_STATE == MENU_STATE_PROPERTIES)? icon_options_dark : icon_options, 25, 0); Draw_Image((MENU_STATE == MENU_STATE_SETTINGS) || (MENU_STATE == MENU_STATE_SORT)? icon_settings_dark : icon_settings, 50, 0); Draw_Image(MENU_STATE == MENU_STATE_FTP? icon_ftp_dark : icon_ftp, 75, 0); - Draw_Image(MENU_STATE == MENU_STATE_UPDATER? icon_updates_dark : icon_updates, 100, 0.5); + Draw_Image((MENU_STATE == MENU_STATE_UPDATE) || (MENU_STATE == MENU_STATE_UPDATE_2)? icon_updates_dark : icon_updates, 100, 0.5); Draw_Image(BROWSE_STATE == BROWSE_STATE_SD? icon_sd_dark : icon_sd, 250, 0); Draw_Image(BROWSE_STATE == BROWSE_STATE_NAND? icon_secure_dark : icon_secure, 275, 0); Draw_Image(icon_search, 300, 0); @@ -132,6 +129,11 @@ void Menu_ControlMenuBar(u32 input) MENU_STATE = MENU_STATE_FTP; Menu_DisplayFTP(); } + else if ((input & KEY_TOUCH) && (TouchInRect(98, 0, 122, 20))) + { + wait(1); + MENU_STATE = MENU_STATE_UPDATE; + } } static void Menu_ControlBrowseOptions(u32 input) @@ -203,7 +205,12 @@ void Menu_Main(void) hidScanInput(); u32 kDown = hidKeysDown(); + u32 kHeld = hidKeysHeld(); + Menu_ControlMenuBar(kDown); + + if (((kHeld & KEY_L) && (kDown & KEY_R)) || ((kHeld & KEY_R) && (kDown & KEY_L))) + Screenshot_Capture(); if (MENU_STATE == MENU_STATE_HOME) { @@ -235,7 +242,20 @@ void Menu_Main(void) Menu_DisplaySortSettings(); Menu_ControlSortSettings(kDown); } + else if (MENU_STATE == MENU_STATE_UPDATE) + { + Menu_DisplayUpdate(); + Menu_ControlUpdate(kDown); + } + else if (MENU_STATE == MENU_STATE_UPDATE_2) + { + Menu_DisplayUpdate2(); + Menu_ControlUpdate2(kDown); + } Draw_EndFrame(); + + if (kDown & KEY_START) + longjmp(exitJmp, 1); } } \ No newline at end of file diff --git a/source/menus/menu_update.c b/source/menus/menu_update.c new file mode 100644 index 0000000..1029949 --- /dev/null +++ b/source/menus/menu_update.c @@ -0,0 +1,167 @@ +#include + +#include "C2D_helper.h" +#include "cia.h" +#include "common.h" +#include "config.h" +#include "fs.h" +#include "menu_main.h" +#include "menu_update.h" +#include "net.h" +#include "textures.h" +#include "touch.h" +#include "utils.h" + +static int update_dialog_selection = 0; +static bool err = false; +char ver[10]; + +static bool Menu_ValidateUpdate(bool nighlty) +{ + if (nighlty) + { + if (FS_FileExists(archive, "/3ds/3DShell/UPDATE_NIGHTLY.txt")) + { + u64 size64 = 0; + u32 size = 0; + + size64 = FS_GetFileSize(archive, "/3ds/3DShell/UPDATE_NIGHTLY.txt"); + size = (u32)size64; + char * buf = (char *)malloc(size + 1); + + if (R_FAILED(FS_Read(archive, "/3ds/3DShell/UPDATE_NIGHTLY.txt", size, buf))) + { + free(buf); + return false; + } + + buf[size] = '\0'; + sscanf(buf, "%s", ver); + free(buf); + + if (strcmp(ver, GITVERSION) != 0) + return true; + + return false; + } + } +} + +void Menu_DisplayUpdate(void) +{ + float err_width = ((320 - Draw_GetTextWidth(0.45f, "No updates available.")) / 2); + + Draw_Rect(0, 0, 320, 20, config_dark_theme? STATUS_BAR_DARK : MENU_BAR_LIGHT); + Draw_Rect(0, 20, 320, 220, config_dark_theme? BLACK_BG : WHITE); + + Menu_DrawMenuBar(); + + Draw_Rect(0, 20, 400, 35, config_dark_theme? MENU_BAR_DARK : STATUS_BAR_LIGHT); // Menu bar + Draw_Text(10, 30, 0.48f, WHITE, "Update Center"); + + if (err) + Draw_Text(err_width, 60, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "No updates available."); + + Draw_Text(122, 85, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Nightly builds"); + Draw_Text(52, 97, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Untested builds that may contain bugs."); + + Draw_Rect(106, 117, (Draw_GetTextWidth(0.45, "Check for updates") + 10), 20, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR); + Draw_Rect(110 - 3, 118, (Draw_GetTextWidth(0.45, "Check for updates") + 8), 18, config_dark_theme? BLACK_BG : WHITE); + Draw_Rect(110 - 2, 119, (Draw_GetTextWidth(0.45, "Check for updates") + 6), 16, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR); + Draw_Text(110, 120, 0.45f, config_dark_theme? BLACK_BG : WHITE, "Check for updates"); + + Draw_Text(115, 155, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Milestone builds"); + Draw_Text(102, 167, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Official release builds."); + + Draw_Rect(106, 187, (Draw_GetTextWidth(0.45, "Check for updates") + 10), 20, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR); + Draw_Rect(110 - 3, 188, (Draw_GetTextWidth(0.45, "Check for updates") + 8), 18, config_dark_theme? BLACK_BG : WHITE); + Draw_Rect(110 - 2, 189, (Draw_GetTextWidth(0.45, "Check for updates") + 6), 16, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR); + Draw_Text(110, 190, 0.45f, config_dark_theme? BLACK_BG : WHITE, "Check for updates"); +} + +void Menu_DisplayUpdate2(void) +{ + float text_width = 0, text2_width = 0;; + float update_confirm_width = 0, update_confirm_height = 0; + float update_cancel_width = 0, update_cancel_height = 0; + + Draw_GetTextSize(0.45f, &text_width, NULL, "This action cannot be undone."); + Draw_GetTextSize(0.45f, &text2_width, NULL, "Do you wish to update?"); + Draw_GetTextSize(0.45f, &update_confirm_width, &update_confirm_height, "YES"); + Draw_GetTextSize(0.45f, &update_cancel_width, &update_cancel_height, "NO"); + + Draw_Image(config_dark_theme? dialog_dark : dialog, ((320 - (dialog.subtex->width)) / 2), ((240 - (dialog.subtex->height)) / 2)); + + Draw_Text(((320 - (dialog.subtex->width)) / 2) + 6, ((240 - (dialog.subtex->height)) / 2) + 6, 0.45f, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR, "Confirm update"); + + Draw_Text(((320 - (text_width)) / 2), ((240 - (dialog.subtex->height)) / 2) + 40, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "This action cannot be undone."); + Draw_Text(((320 - (text2_width)) / 2), ((240 - (dialog.subtex->height)) / 2) + 55, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Do you wish to update?"); + + if (update_dialog_selection == 0) + Draw_Rect((288 - update_cancel_width) - 5, (159 - update_cancel_height) - 5, update_cancel_width + 10, update_cancel_height + 10, config_dark_theme? SELECTOR_COLOUR_DARK : SELECTOR_COLOUR_LIGHT); + else if (update_dialog_selection == 1) + Draw_Rect((248 - (update_confirm_width)) - 5, (159 - update_confirm_height) - 5, update_confirm_width + 10, update_confirm_height + 10, config_dark_theme? SELECTOR_COLOUR_DARK : SELECTOR_COLOUR_LIGHT); + + Draw_Text(248 - (update_confirm_width), (159 - update_confirm_height), 0.45f, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR, "YES"); + Draw_Text(288 - update_cancel_width, (159 - update_cancel_height), 0.45f, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR, "NO"); +} + +void Menu_ControlUpdate(u32 input) +{ + if ((input & KEY_TOUCH) && (TouchInRect(106, 117, (106) + (Draw_GetTextWidth(0.45, "Check for updates") + 10), 137))) + { + wait(1); + Net_DownloadFile("https://github.com/joel16/3DShell/raw/gh-pages/UPDATE_NIGHTLY.txt", "/3ds/3DShell/UPDATE_NIGHTLY.txt"); + if (Menu_ValidateUpdate(true)) + MENU_STATE = MENU_STATE_UPDATE_2; + else + err = true; + } + + else if ((input & KEY_TOUCH) && (TouchInRect(106, 187, (106) + (Draw_GetTextWidth(0.45, "Check for updates") + 10), 207))) + { + wait(1); + Net_DownloadFile("https://github.com/joel16/3DShell/raw/gh-pages/UPDATE_MILESTONE.txt", "/3ds/3DShell/UPDATE_MILESTONE.txt"); + if (Menu_ValidateUpdate(false)) + MENU_STATE = MENU_STATE_UPDATE_2; + else + err = true; + } +} + +void Menu_ControlUpdate2(u32 input) +{ + if (input & KEY_B) + { + wait(1); + update_dialog_selection = 0; + MENU_STATE = MENU_STATE_UPDATE; + } + + if (input & KEY_A) + { + if (update_dialog_selection == 1) + { + if (envIsHomebrew()) + { + if (FS_FileExists(archive, "/3ds/3DShell/3DShell.3dsx")) + FS_Remove(archive, "/3ds/3DShell/3DShell.3dsx"); + Net_DownloadFile("https://github.com/joel16/3DShell/raw/gh-pages/3DShell.3dsx", "/3ds/3DShell/3DShell.3dsx"); + longjmp(exitJmp, 1); + } + else + { + Net_DownloadFile("https://github.com/joel16/3DShell/raw/gh-pages/3DShell.cia", "/3ds/3DShell/3DShell.cia"); + CIA_InstallTitle("/3ds/3DShell/3DShell.cia", MEDIATYPE_SD, true); + } + } + else + { + wait(1); + update_dialog_selection = 0; + MENU_STATE = MENU_STATE_UPDATE; + } + + update_dialog_selection = 0; + } +} From 339067c7b0319fbf8d4288329f54c4715624f7df Mon Sep 17 00:00:00 2001 From: Joel16 Date: Sun, 8 Jul 2018 19:56:54 -0500 Subject: [PATCH 10/33] Test: display version --- source/menus/menu_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/menus/menu_main.c b/source/menus/menu_main.c index e261bfa..cf275ff 100644 --- a/source/menus/menu_main.c +++ b/source/menus/menu_main.c @@ -214,6 +214,7 @@ void Menu_Main(void) if (MENU_STATE == MENU_STATE_HOME) { + Draw_Textf(2, 225, 0.45f, WHITE, "3DShell %d.%d.%d - %s", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO, GITVERSION); Menu_ControlHome(kDown); Menu_ControlBrowseOptions(kDown); } From 3b99726f60f2bf4616a65c3b03e671f9ffa22da9 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Thu, 12 Jul 2018 23:50:53 -0500 Subject: [PATCH 11/33] Audio is implemented Todo: - Fix next/previous - Properly implement shuffle/repeat --- include/audio/audio.h | 31 +- include/audio/flac.h | 42 +- include/audio/mp3.h | 36 +- include/audio/vorbis.h | 14 +- include/audio/wav.h | 42 +- include/menus/menu_music.h | 40 +- include/textures.h | 2 +- res/drawable/btn_playback_forward.png | Bin 1392 -> 941 bytes res/drawable/btn_playback_pause.png | Bin 112 -> 134 bytes res/drawable/btn_playback_play.png | Bin 557 -> 387 bytes res/drawable/btn_playback_repeat.png | Bin 1262 -> 901 bytes res/drawable/btn_playback_repeat_overlay.png | Bin 1262 -> 901 bytes res/drawable/btn_playback_rewind.png | Bin 1327 -> 928 bytes res/drawable/btn_playback_shuffle.png | Bin 1581 -> 1151 bytes res/drawable/btn_playback_shuffle_overlay.png | Bin 1581 -> 1151 bytes res/drawable/default_artwork_blur.png | Bin 0 -> 126563 bytes res/drawable/ic_album_lg.png | Bin 0 -> 1559 bytes res/drawable/ic_music_bg_top.png | Bin 46256 -> 0 bytes res/drawable/sprites.t3s | 3 + source/audio/audio.c | 205 +++++++++- source/audio/flac.c | 44 +-- source/audio/mp3.c | 82 ++-- source/audio/vorbis.c | 56 +-- source/audio/wav.c | 40 +- source/dirbrowse.c | 7 +- source/menus/menu_fileoptions.c | 12 +- source/menus/menu_main.c | 4 +- source/menus/menu_music.c | 369 ++++++++---------- source/menus/menu_settings.c | 6 +- source/textures.c | 4 +- 30 files changed, 590 insertions(+), 449 deletions(-) create mode 100644 res/drawable/default_artwork_blur.png create mode 100644 res/drawable/ic_album_lg.png delete mode 100644 res/drawable/ic_music_bg_top.png diff --git a/include/audio/audio.h b/include/audio/audio.h index 0fde76a..8df2929 100644 --- a/include/audio/audio.h +++ b/include/audio/audio.h @@ -1,16 +1,41 @@ #ifndef AUDIO_H #define AUDIO_H -#include "menu_music.h" +#include <3ds.h> + +extern volatile bool stop; enum channel_e { - BGM = 0, + BGM, SFX }; -bool Audio_IsPlaying(enum channel_e); +enum file_types +{ + FILE_TYPE_ERROR, + FILE_TYPE_BCSTM, + FILE_TYPE_WAV, + FILE_TYPE_FLAC, + FILE_TYPE_VORBIS, + FILE_TYPE_MP3 +}; + +struct decoder_fn +{ + int (* init)(const char *file); + u32 (* rate)(void); + u8 (* channels)(void); + size_t buffSize; + u64 (* decode)(void*); + void (* exit)(void); +}; + bool Audio_IsPaused(enum channel_e); bool Audio_TogglePlayback(enum channel_e); +void Audio_StopPlayback(void); +bool Audio_IsPlaying(void); +enum file_types Audio_GetMusicFileType(const char *file); +void Audio_PlayFile(void *path); #endif diff --git a/include/audio/flac.h b/include/audio/flac.h index 2b23dab..765bd2b 100644 --- a/include/audio/flac.h +++ b/include/audio/flac.h @@ -1,53 +1,53 @@ /* Obtained from ctrmus source with permission. */ -#include "menu_music.h" +#include "audio.h" /** - * Set decoder parameters for flac. + *Set decoder parameters for flac. * - * \param decoder Structure to store parameters. + *\param decoder Structure to store parameters. */ -void setFlac(struct decoder_fn * decoder); +void setFlac(struct decoder_fn *decoder); /** - * Initialise Flac decoder. + *Initialise Flac decoder. * - * \param file Location of flac file to play. - * \return 0 on success, else failure. + *\param file Location of flac file to play. + *\return 0 on success, else failure. */ -int initFlac(const char * file); +int initFlac(const char *file); /** - * Get sampling rate of Flac file. + *Get sampling rate of Flac file. * - * \return Sampling rate. + *\return Sampling rate. */ u32 rateFlac(void); /** - * Get number of channels of Flac file. + *Get number of channels of Flac file. * - * \return Number of channels for opened file. + *\return Number of channels for opened file. */ u8 channelFlac(void); /** - * Decode part of open Flac file. + *Decode part of open Flac file. * - * \param buffer Decoded output. - * \return Samples read for each channel. + *\param buffer Decoded output. + *\return Samples read for each channel. */ -u64 decodeFlac(void * buffer); +u64 decodeFlac(void *buffer); /** - * Free Flac decoder. + *Free Flac decoder. */ void exitFlac(void); /** - * Checks if the input file is Flac + *Checks if the input file is Flac * - * \param in Input file. - * \return 0 if Flac file, else not or failure. + *\param in Input file. + *\return 0 if Flac file, else not or failure. */ -int isFlac(const char * in); \ No newline at end of file +int isFlac(const char *in); \ No newline at end of file diff --git a/include/audio/mp3.h b/include/audio/mp3.h index 881096c..f8cb828 100644 --- a/include/audio/mp3.h +++ b/include/audio/mp3.h @@ -2,7 +2,7 @@ #include -#include "menu_music.h" +#include "audio.h" typedef struct { @@ -16,46 +16,46 @@ typedef struct ID3_Tag ID3; -char * id3_pic; +char *id3_pic; /** - * Set decoder parameters for MP3. + *Set decoder parameters for MP3. * - * \param decoder Structure to store parameters. + *\param decoder Structure to store parameters. */ -void setMp3(struct decoder_fn * decoder); +void setMp3(struct decoder_fn *decoder); /** - * Initialise MP3 decoder. + *Initialise MP3 decoder. * - * \param file Location of MP3 file to play. - * \return 0 on success, else failure. + *\param file Location of MP3 file to play. + *\return 0 on success, else failure. */ -int initMp3(const char * file); +int initMp3(const char *file); /** - * Get sampling rate of MP3 file. + *Get sampling rate of MP3 file. * - * \return Sampling rate. + *\return Sampling rate. */ u32 rateMp3(void); /** - * Get number of channels of MP3 file. + *Get number of channels of MP3 file. * - * \return Number of channels for opened file. + *\return Number of channels for opened file. */ u8 channelMp3(void); /** - * Decode part of open MP3 file. + *Decode part of open MP3 file. * - * \param buffer Decoded output. - * \return Samples read for each channel. + *\param buffer Decoded output. + *\return Samples read for each channel. */ -u64 decodeMp3(void * buffer); +u64 decodeMp3(void *buffer); /** - * Free MP3 decoder. + *Free MP3 decoder. */ void exitMp3(void); \ No newline at end of file diff --git a/include/audio/vorbis.h b/include/audio/vorbis.h index 3b20901..ccc3c7d 100644 --- a/include/audio/vorbis.h +++ b/include/audio/vorbis.h @@ -3,22 +3,22 @@ #include #include -#include "menu_music.h" +#include "audio.h" -void setVorbis(struct decoder_fn * decoder); +void setVorbis(struct decoder_fn *decoder); -int initVorbis(const char * file); +int initVorbis(const char *file); u32 rateVorbis(void); u8 channelVorbis(void); -u64 decodeVorbis(void * buffer); +u64 decodeVorbis(void *buffer); void exitVorbis(void); -int playVorbis(const char * in); +int playVorbis(const char *in); -u64 fillVorbisBuffer(char * bufferOut); +u64 fillVorbisBuffer(char *bufferOut); -int isVorbis(const char * in); \ No newline at end of file +int isVorbis(const char *in); \ No newline at end of file diff --git a/include/audio/wav.h b/include/audio/wav.h index 74bcad0..bbac5a9 100644 --- a/include/audio/wav.h +++ b/include/audio/wav.h @@ -1,51 +1,51 @@ -#include "menu_music.h" +#include "audio.h" /** - * Set decoder parameters for WAV. + *Set decoder parameters for WAV. * - * \param decoder Structure to store parameters. + *\param decoder Structure to store parameters. */ -void setWav(struct decoder_fn * decoder); +void setWav(struct decoder_fn *decoder); /** - * Initialise WAV playback. + *Initialise WAV playback. * - * \param file Location of WAV file to play. - * \return 0 on success, else failure. + *\param file Location of WAV file to play. + *\return 0 on success, else failure. */ -int initWav(const char * file); +int initWav(const char *file); /** - * Get sampling rate of Wav file. + *Get sampling rate of Wav file. * - * \return Sampling rate. + *\return Sampling rate. */ u32 rateWav(void); /** - * Get number of channels of Wav file. + *Get number of channels of Wav file. * - * \return Number of channels for opened file. + *\return Number of channels for opened file. */ u8 channelWav(void); /** - * Decode part of open Wav file. + *Decode part of open Wav file. * - * \param buffer Output. - * \return Samples read for each channel. + *\param buffer Output. + *\return Samples read for each channel. */ -u64 decodeWav(void * buffer); +u64 decodeWav(void *buffer); /** - * Free Wav file. + *Free Wav file. */ void exitWav(void); /** - * Checks if the input file is Wav + *Checks if the input file is Wav * - * \param in Input file. - * \return 0 if Wav file, else not or failure. + *\param in Input file. + *\return 0 if Wav file, else not or failure. */ -int isWav(const char * in); \ No newline at end of file +int isWav(const char *in); \ No newline at end of file diff --git a/include/menus/menu_music.h b/include/menus/menu_music.h index 28770f0..ff7b0e7 100644 --- a/include/menus/menu_music.h +++ b/include/menus/menu_music.h @@ -1,44 +1,6 @@ #ifndef _3D_SHELL_MENU_MUSIC_H #define _3D_SHELL_MENU_MUSIC_H -#include <3ds.h> - -enum file_types -{ - FILE_TYPE_ERROR = 0, - FILE_TYPE_BCSTM, - FILE_TYPE_WAV, - FILE_TYPE_FLAC, - FILE_TYPE_VORBIS, - FILE_TYPE_MP3 -}; - -struct decoder_fn -{ - int (* init)(const char *file); - u32 (* rate)(void); - u8 (* channels)(void); - size_t buffSize; - u64 (* decode)(void*); - void (* exit)(void); -}; - -// Returns true if playing. -bool Music_IsPlaying(void); - -/** - * Obtains file type. - * - * \param file File location. - * \return file_types enum or 0 on error and errno set. - */ -enum file_types Music_GetMusicFileType(const char *file); - -/** - * Play an audio file. - * - * \param path File path. - */ -void Music_Player(char *path); +void Menu_PlayMusic(char *path); #endif diff --git a/include/textures.h b/include/textures.h index 55c0872..0e00b8d 100644 --- a/include/textures.h +++ b/include/textures.h @@ -10,7 +10,7 @@ C2D_Image icon_app, icon_archive, icon_audio, icon_dir, icon_file, icon_image, i icon_home, icon_home_dark, icon_options, icon_options_dark, icon_settings, icon_settings_dark, \ icon_ftp, icon_ftp_dark, icon_sd, icon_sd_dark, icon_secure, icon_secure_dark, icon_search, \ icon_updates, icon_updates_dark, \ - default_artwork, default_artwork_blur, btn_play, btn_pause, btn_rewind, btn_forward, \ + default_artwork, default_artwork_blur, ic_music_bg_bottom, btn_play, btn_pause, btn_rewind, btn_forward, \ btn_repeat, btn_shuffle, btn_repeat_overlay, btn_shuffle_overlay, \ icon_nav_drawer, icon_actions, icon_back; diff --git a/res/drawable/btn_playback_forward.png b/res/drawable/btn_playback_forward.png index ae67862e4ff6ee016109e41c6e0d97e0ae029872..ccdc365cc374261e380def31338bb076bd9c6d0f 100644 GIT binary patch delta 595 zcmV-Z0<8V;3atk$iBL{Q4GJ0x0000DNk~Le0000j0000j2m=5B01d+D#SYSks9Q94(`uG1p2cueGvfTVg|A-m>XpX{nSGEmmY%_1JO{wTxZ%e zq$2f({?bedyfvbPVa!LkGwJi29T0HN`MWZf65lQDf33 z?%hSGuKz2IwxLlcxAaU?Glgz37+c#2nr-jU(`W$3)QMZ18KBG3t=hy205*G%1Iu;_ zmaDfO}?cNnS~8Rg|PM8?J)1Z}9%V2H-z zSQAkblL;CPwkf@sm1KV{?eq+d7OKvsglseBq%(6ojfU*}f&*(J69a zD+Maeu5u*3=BCg_`+6cNr?nfKUUhnFJ0!b7e}E!eNKvNT+1kZfzi=vlQdH&jEkRiC h9Uq^(DqGjz)fa{KM10WQe$oH{002ovPDHLkV1nY22TA|{ delta 901 zcmV;01A6?e2k;6kiBL{Q4GJ0x0000DNk~Le0000y0000y2m=5B01YB3KmY&&0Z>d- zMF0Q*u`y!-kwBY&rI1_R00021bW%=J03%vpK?VUcTRRh7@AkJQ2Z-yfNdxHn!)y+R z?f2JeAphB79)#3_7Xs?_#z+cFcA{MN8r|$HVEMhix}m5 zx<0H>@G`WC%1sUC({f=?&KBP!*c#7S=YT9JrzoWHj5@32nNn3XBegc70007WNkl)512x(S-3=RopMflknAra;f9zjU6qQ6yS z6k9Z^vmqcRHqMrSW}wqUETM|x!;r<@!x9oPLLFVRVp4KSDjTNFii&CJk{N_FE9#47 z@`GgTw9q8{)3dT|31|i;jGSCO&FDNK42h)t0#jQy{F)V$bP5%0kwY8qbf2Q)5(1im z$u+)z)DDYgU_vY_C!|@tqDU$atLDn8YW$l0t?a66*swU;z1GvO&KAFBrTPY2hGBRzzF43f>_#uIumwL(fyxRWQkMPt((+&xJT zn_gg{Zw^&5S$zsh84nAvq^Ty;a1@{%kkV68e>(S!XcQTkpJ!&4;wpS+HG-U-G!vvJDv8VSP)h>+ouxwai`^du^oJxV!_eRt5%z6-=%{SSzFAm8-Co)uUh( b408Yg1>m}5aU<=I00000NkvXXu0mjfisX;# diff --git a/res/drawable/btn_playback_pause.png b/res/drawable/btn_playback_pause.png index e53cf621a790aca09e46804e1d490842fd508fd8..6309fa8b73fa8b2e97ee36f7543cb5d5fca3d2d0 100644 GIT binary patch literal 134 zcmeAS@N?(olHy`uVBq!ia0vp^x**KN3?$u4xHy3nPk>K|E0F#V0~QCYR{;4eB|(0{ z3=R$duXfCt1LUiEx;TbZ%t=mA;NGw{>ZHQfEY6n0fB(xj|NAe$c+SGJj-A1i7#SL~ Wrk`yyUv>egfx*+&&t;ucLK6UL{Vu=& literal 112 zcmeAS@N?(olHy`uVBq!ia0vp^HXzK%3?vzhybXaQTYyi9E0F&G|NqjP^X~##OeH~n z!3_VSCn{|P^29w|978JRBqu2F8~nFV_@6$-Fh$}C14CkNf8h)#=~+M}44$rjF6*2U FngF7+9!vlL diff --git a/res/drawable/btn_playback_play.png b/res/drawable/btn_playback_play.png index 7aabe33cacf6eeb403c91ebd9e248e04f2b39996..a904e0be45ff55e59fb2c295b586925315257cf4 100644 GIT binary patch literal 387 zcmeAS@N?(olHy`uVBq!ia0vp^x**KK3=%mav;s(#2Ka=y0_p#B2T!}x9Do+9mjw9* zGgQ=YH2lBgu}0=I%MFJm67LHxSj-X0IAQX8hQRZfBRV}ik0bVIe0U*zDhsG(nx~6n zNX49|S5NXaIS9B0vgtGN@o;hRJ^1l|{)tPi+{MD-w_Z>7Te|C2slcDxoQoq~NS?ft z;bFY^pB{dezw*qzR_ Y)0MyTy&~%|pbHs1UHx3vIVCg!0H~_u-T(jq literal 557 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw3=&b&bO2Jj0(?STf%N~O2|mfF90a=Ft|Z7W zn8A#H($ty%fAt%mac22_!iMW=Bir>do-bV*by{B!TkK&iIF-fmp-Eol({{Zdn>CsD zr5uQVQ7WDGcAfH4)zDLO&Den^J@IsL45^s&_L@82p#UD104GU~z2EO~?*0BdzI(rG z2ZOOw&WC@V6Za?gAOF1m{?wBmHqH{bnuTBjW%oKG={gstE3rL}mmjMc*m{1 zzxIG6bMd!w z%{0gEC0@nLHZ{ih4mS$=)7973*wos@o&1`hp?n(ve;Vl-p4}`^6E&vU0003@Nklu~#NPhBnaPGkCSk_&_U)NF zdkaBmU@)3Ut#^#sVl}qVHoK$EN$Kl$=;(AcqTAzDXkazH#Rf6EB-IGt!?X0R#%fzp3mi# zY~J1Ts;lh)`-&zH3Wr5qZ7v;gDBGluPvqLuakDbSQt3*jBEeQfovTXryi^dccX3&} zs$EOm+}sqX&`-2L3mE$O1-JJ4R(}Viza%Oz p@F=LO3F_nXtA!@0?;q`h{sIw^MJ_A$icbIl002ovPDHLkV1i0__Qn7J delta 791 zcmV+y1L*vP2kr?hiBL{Q4GJ0x0000DNk~Le0000j0000j2m=5B01d`+Wp zMF0Q*u|ZY=kx-L=Y5!Ud0001_bW%=J01tkP91Nz}WCoP~*Le^NsQ%w?2?v_Y{mpC* z03lLKOi4~gpV}?a@9gXC?$Tx&-OrgT-14K__H-)N*y8uBN(e0FA}1y&BPEyfss-si zoAsnZKcKG!vQa7dqD8$!M;mo*Xe2c*0}Awroq`r=oP!j9Y=_aSkGta{^U!OGrvdBZDz$}6CM10_Xe6`*QvK~*V!B-Lu_8k$;~+UP(>OG}3v40QGM5z5sJxbXuCLnDN0 zV-o_ZxlPTGRGSk}Y++`JP;I3O60v4yx3N)00z5qIwqV6}_708+)lMLnILo`Zy1BU{ z0S`}CFOXtyA4OjVG}V6oSYjstC=nPG%!w=rcDG-D2&QV0P@u_Sa^Wa~?(8^J17qGU zA`&g)qj0HqREds>6=fh&HNQ$+d^oxwPSu=hQRpT!$NlY3p9YLvBeHH5biG)2#lSD#HnV;2=?(NUNu9;q6H<`N(fwzQ5G;+OtfmE004Dj Vj?+5|o4^17002ovPDHLkV1h^HNWTC8 diff --git a/res/drawable/btn_playback_repeat_overlay.png b/res/drawable/btn_playback_repeat_overlay.png index ff598e0cdb127108e7ea3eca801e11dd3a697d32..8caf9de52f9337b3f19ab8e0910b79f9ed6dfaa4 100644 GIT binary patch delta 551 zcmV+?0@(fT355qNiBL{Q4GJ0x0000DNk~Le0000U0000U2m=5B01WP4kN^MyZct2A zMF0Q*u`z1_e}kk%E&u=kdUR4wQvd=XCIkgvj00iyy&?f)|Mz|!6(1gI^0gpWr>m{1 zzxIG6bMd!w z%{0gEC0@nLHZ{ih4mS$=)7973*wos@o&1`hp?n(ve;Vl-p4}`^6E&vU0003@Nklu~#NPhBnaPGkCSk_&_U)NF zdkaBmU@)3Ut#^#sVl}qVHoK$EN$Kl$=;(AcqTAzDXkazH#Rf6EB-IGt!?X0R#%fzp3mi# zY~J1Ts;lh)`-&zH3Wr5qZ7v;gDBGluPvqLuakDbSQt3*jBEeQfovTXryi^dccX3&} zs$EOm+}sqX&`-2L3mE$O1-JJ4R(}Viza%Oz p@F=LO3F_nXtA!@0?;q`h{sIw^MJ_A$icbIl002ovPDHLkV1hg<_PziB delta 791 zcmV+y1L*vP2kr?hiBL{Q4GJ0x0000DNk~Le0000j0000j2m=5B01d`+Wp zMF0Q*u`y-=kwAlgVjeW^0001_bW%=J01tkP91Nz}WCoP~*Le^NsQ%w?2?v_Y{mpC* z03lLKOi4~gpV}?a@9gXC?$Tx&-OrgT-14K__H-)N*y8uBN(e0FA}1y&BPEyfss-si zoAsnZKcKG!vQa7dqD8$!M;mo*Xe2c*0}Awroq`r=oP!j9Y=_aSkGta{^U!OGrvdBZDz$}6CM10_Xe6`*QvK~*V!B-Lu_8k$;~+UP(>OG}3v40QGM5z5sJxbXuCLnDN0 zV-o_ZxlPTGRGSk}Y++`JP;I3O60v4yx3N)00z5qIwqV6}_708+)lMLnILo`Zy1BU{ z0S`}CFOXtyA4OjVG}V6oSYjstC=nPG%!w=rcDG-D2&QV0P@u_Sa^Wa~?(8^J17qGU zA`&g)qj0HqREds>6=fh&HNQ$+d^oxwPSu=hQRpT!$NlY3p9YLvBeHH5biG)2#lSD#HnV;2=?(NUNu9;q6H<`N(fwzQ5G;+OtfmE004Dj Vj?+5|o4^17002ovPDHLkV1i~~NO1rF diff --git a/res/drawable/btn_playback_rewind.png b/res/drawable/btn_playback_rewind.png index fdd4dc8a404894e022accf34df7cdbd0d963a609..9aeaf2e16748a10f28fa9afd6cbd80733ad67311 100644 GIT binary patch delta 585 zcmV-P0=E6H3ZMrpiBL{Q4GJ0x0000DNk~Le0000j0000j2m=5B01d@qdT{r?AH*XS`2b>IOYmiOxd zD7X6nKEeZ8*$?I%kMt|F1VqyaXy58_?V0u}y8K4V3SQ$Agz_Jp_*lG9&u!If2Bi4~ zV&V~i^DMnei|;ffLd;(snzh>wf3ZK&4StwK%2PHwe?VpyPj}q%ntlKP0dGk}K~#90 z?bFv*!axuP;6cD30W%gY*@$KY|%OsTBf7#tDur$c~2L`U*W$rM-(g2yGV}0+U zdOIiT6QgR7PtOG7Y^6NsQI-a{xYV9dvZ!@L_m#lX5cghdXHse1+$LEXMAw}Y$$x`< z|G+Ucz~hsT)XIX-JWGT0@~XX9{TJWzEDfSPU3fROKC&zg^5@sLsTKeEW$9}Dm-+)| X6Q4YZHmGaY00000NkvXXu0mjfIt&oX delta 869 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℘YzAlZ=N; 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 $Q~K7%#e)oh`74v&XQ$ZdCy$HQFs*GP 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 literal 0 HcmV?d00001 diff --git a/res/drawable/ic_music_bg_top.png b/res/drawable/ic_music_bg_top.png deleted file mode 100644 index d822ee339e0d7e2dae547ac0b9bec270dee69fe1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46256 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 q8V2LAj(mnnSJuNXc^#1^*R=PoST0zwS diff --git a/res/drawable/sprites.t3s b/res/drawable/sprites.t3s index b28b26d..b0e023e 100644 --- a/res/drawable/sprites.t3s +++ b/res/drawable/sprites.t3s @@ -25,6 +25,9 @@ ic_material_light_settings_dark.png ic_material_light_settings.png ic_material_light_tab.png ic_material_light_tab_dark.png +default_artwork_blur.png +ic_album_lg.png +ic_music_bg_bottom.png btn_playback_forward.png btn_playback_pause.png btn_playback_play.png diff --git a/source/audio/audio.c b/source/audio/audio.c index c7fbf78..830f18c 100644 --- a/source/audio/audio.c +++ b/source/audio/audio.c @@ -3,11 +3,19 @@ #include #include "audio.h" +#include "flac.h" +#include "mp3.h" +#include "vorbis.h" +#include "wav.h" -bool Audio_IsPlaying(enum channel_e channel) +#include "fs.h" + +volatile bool stop = true; + +/*bool Audio_IsPlaying(enum channel_e channel) { return ndspChnIsPlaying(channel); -} +}*/ bool Audio_IsPaused(enum channel_e channel) { @@ -18,4 +26,197 @@ bool Audio_TogglePlayback(enum channel_e channel) { ndspChnSetPaused(channel, !(ndspChnIsPaused(channel))); return !(ndspChnIsPaused(channel)); +} + +void Audio_StopPlayback(void) +{ + stop = true; +} + +bool Audio_IsPlaying(void) +{ + return !stop; +} + +enum file_types Audio_GetMusicFileType(const char *file) +{ + Handle handle; + + u32 fileSig = 0, bytesRead = 0; + u64 offset = 0; + + enum file_types file_type = FILE_TYPE_ERROR; + + /* Failure opening file */ + if (R_FAILED(FS_Open(&handle, archive, file, FS_OPEN_READ))) + { + FSFILE_Close(handle); + return -1; + } + + if (R_FAILED(FSFILE_Read(handle, &bytesRead, offset, &fileSig, 4))) + { + FSFILE_Close(handle); + return -2; + } + + offset += bytesRead; + + switch(fileSig) + { + // "RIFF" + case 0x46464952: + if (isWav(file) == 0) + file_type = FILE_TYPE_WAV; + break; + + // "fLaC" + case 0x43614c66: + file_type = FILE_TYPE_FLAC; + break; + + // "OggS" + case 0x5367674F: + if (isFlac(file) == 0) + file_type = FILE_TYPE_FLAC; + else if (isVorbis(file) == 0) + file_type = FILE_TYPE_VORBIS; + + break; + + // MP3 file with an ID3v2 container + default: + if ((fileSig << 16) == 0xFBFF0000 || (fileSig << 16) == 0xFAFF0000 || (fileSig << 8) == 0x33444900) + { + file_type = FILE_TYPE_MP3; + break; + } + } + + FSFILE_Close(handle); + return file_type; +} + +void Audio_PlayFile(void *path) +{ + struct decoder_fn decoder; + const char *file = path; + s16 *buffer1 = NULL; + s16 *buffer2 = NULL; + ndspWaveBuf waveBuf[2]; + bool lastbuf = false; + Result ret = -1; + + /* Reset previous stop command */ + stop = false; + + switch(Audio_GetMusicFileType(file)) + { + case FILE_TYPE_WAV: + setWav(&decoder); + break; + + case FILE_TYPE_FLAC: + setFlac(&decoder); + break; + + case FILE_TYPE_MP3: + setMp3(&decoder); + break; + + case FILE_TYPE_VORBIS: + setVorbis(&decoder); + break; + + default: + goto out; + } + + if ((ret = (*decoder.init)(file)) != 0) + goto out; + + if ((*decoder.channels)() > 2 || (*decoder.channels)() < 1) + goto out; + + buffer1 = linearAlloc(decoder.buffSize *sizeof(s16)); + buffer2 = linearAlloc(decoder.buffSize *sizeof(s16)); + + ndspChnReset(SFX); + ndspChnWaveBufClear(SFX); + ndspSetOutputMode(NDSP_OUTPUT_STEREO); + ndspChnSetInterp(SFX, NDSP_INTERP_POLYPHASE); + ndspChnSetRate(SFX, (*decoder.rate)()); + ndspChnSetFormat(SFX, (*decoder.channels)() == 2 ? NDSP_FORMAT_STEREO_PCM16 : NDSP_FORMAT_MONO_PCM16); + + memset(waveBuf, 0, sizeof(waveBuf)); + waveBuf[0].nsamples = (*decoder.decode)(&buffer1[0]) / (*decoder.channels)(); + waveBuf[0].data_vaddr = &buffer1[0]; + ndspChnWaveBufAdd(SFX, &waveBuf[0]); + + waveBuf[1].nsamples = (*decoder.decode)(&buffer2[0]) / (*decoder.channels)(); + waveBuf[1].data_vaddr = &buffer2[0]; + ndspChnWaveBufAdd(SFX, &waveBuf[1]); + + /** + *There may be a chance that the music has not started by the time we get + *to the while loop. So we ensure that music has started here. + */ + while(ndspChnIsPlaying(SFX) == false); + + while(stop == false) + { + svcSleepThread(100 *1000); + + /* When the last buffer has finished playing, break. */ + if ((lastbuf == true) && (waveBuf[0].status == NDSP_WBUF_DONE) && (waveBuf[1].status == NDSP_WBUF_DONE)) + break; + + if ((ndspChnIsPaused(SFX) == true) || (lastbuf == true)) + continue; + + if (waveBuf[0].status == NDSP_WBUF_DONE) + { + size_t read = (*decoder.decode)(&buffer1[0]); + + if (read <= 0) + { + lastbuf = true; + continue; + } + else if (read < decoder.buffSize) + waveBuf[0].nsamples = read / (*decoder.channels)(); + + ndspChnWaveBufAdd(SFX, &waveBuf[0]); + } + + if (waveBuf[1].status == NDSP_WBUF_DONE) + { + size_t read = (*decoder.decode)(&buffer2[0]); + + if (read <= 0) + { + lastbuf = true; + continue; + } + else if (read < decoder.buffSize) + waveBuf[1].nsamples = read / (*decoder.channels)(); + + ndspChnWaveBufAdd(SFX, &waveBuf[1]); + } + + if (R_FAILED(DSP_FlushDataCache(buffer1, decoder.buffSize *sizeof(s16)))) + return; + if (R_FAILED(DSP_FlushDataCache(buffer2, decoder.buffSize *sizeof(s16)))) + return; + } + + (*decoder.exit)(); + +out: + Audio_StopPlayback(); + linearFree(buffer1); + linearFree(buffer2); + + threadExit(0); + return; } \ No newline at end of file diff --git a/source/audio/flac.c b/source/audio/flac.c index 195d5c0..8bcf1ed 100644 --- a/source/audio/flac.c +++ b/source/audio/flac.c @@ -5,16 +5,16 @@ #include "flac.h" -static drflac * pFlac; +static drflac *pFlac; static const drflac_uint64 buffSize = 16 * 1024; static drflac_uint64 samplesRead; /** - * Set decoder parameters for flac. + *Set decoder parameters for flac. * - * \param decoder Structure to store parameters. + *\param decoder Structure to store parameters. */ -void setFlac(struct decoder_fn * decoder) +void setFlac(struct decoder_fn *decoder) { decoder->init = &initFlac; decoder->rate = &rateFlac; @@ -25,12 +25,12 @@ void setFlac(struct decoder_fn * decoder) } /** - * Initialise Flac decoder. + *Initialise Flac decoder. * - * \param file Location of flac file to play. - * \return 0 on success, else failure. + *\param file Location of flac file to play. + *\return 0 on success, else failure. */ -int initFlac(const char * file) +int initFlac(const char *file) { pFlac = drflac_open_file(file); @@ -38,9 +38,9 @@ int initFlac(const char * file) } /** - * Get sampling rate of Flac file. + *Get sampling rate of Flac file. * - * \return Sampling rate. + *\return Sampling rate. */ u32 rateFlac(void) { @@ -48,9 +48,9 @@ u32 rateFlac(void) } /** - * Get number of channels of Flac file. + *Get number of channels of Flac file. * - * \return Number of channels for opened file. + *\return Number of channels for opened file. */ u8 channelFlac(void) { @@ -58,19 +58,19 @@ u8 channelFlac(void) } /** - * Decode part of open Flac file. + *Decode part of open Flac file. * - * \param buffer Decoded output. - * \return Samples read for each channel. + *\param buffer Decoded output. + *\return Samples read for each channel. */ -u64 decodeFlac(void * buffer) +u64 decodeFlac(void *buffer) { samplesRead = drflac_read_s16(pFlac, buffSize, buffer); return samplesRead; } /** - * Free Flac decoder. + *Free Flac decoder. */ void exitFlac(void) { @@ -78,15 +78,15 @@ void exitFlac(void) } /** - * Checks if the input file is Flac + *Checks if the input file is Flac * - * \param in Input file. - * \return 0 if Flac file, else not or failure. + *\param in Input file. + *\return 0 if Flac file, else not or failure. */ -int isFlac(const char * in) +int isFlac(const char *in) { int err = -1; - drflac * pFlac = drflac_open_file(in); + drflac *pFlac = drflac_open_file(in); if (pFlac != NULL) err = 0; diff --git a/source/audio/mp3.c b/source/audio/mp3.c index 4a44636..c33d6f7 100644 --- a/source/audio/mp3.c +++ b/source/audio/mp3.c @@ -26,8 +26,8 @@ typedef intmax_t ssize_p; typedef long ssize_p; #endif -static size_t * buffSize; -static mpg123_handle * mh = NULL; +static size_t *buffSize; +static mpg123_handle *mh = NULL; static u32 rate; static u8 channels; @@ -69,18 +69,18 @@ struct genre genreList[] = }; /** - * Set decoder parameters for MP3. + *Set decoder parameters for MP3. * - * \param decoder Structure to store parameters. + *\param decoder Structure to store parameters. */ -void setMp3(struct decoder_fn * decoder) +void setMp3(struct decoder_fn *decoder) { decoder->init = &initMp3; decoder->rate = &rateMp3; decoder->channels = &channelMp3; /* - * buffSize changes depending on input file. So we set buffSize later when - * decoder is initialised. + *buffSize changes depending on input file. So we set buffSize later when + *decoder is initialised. */ buffSize = &(decoder->buffSize); decoder->decode = &decodeMp3; @@ -88,7 +88,7 @@ void setMp3(struct decoder_fn * decoder) } /* Helper for v1 printing, get these strings their zero byte. */ -void safe_print(char * tag, char * name, char * data, size_t size) +void safe_print(char *tag, char *name, char *data, size_t size) { char safe[31]; if (size > 30) @@ -99,7 +99,7 @@ void safe_print(char * tag, char * name, char * data, size_t size) } /* Print out ID3v1 info. */ -void print_v1(ID3_Tag * ID3tag, mpg123_id3v1 *v1) +void print_v1(ID3_Tag *ID3tag, mpg123_id3v1 *v1) { safe_print(ID3tag->title, "Title", v1->title, sizeof(v1->title)); safe_print(ID3tag->artist, "Artist", v1->artist, sizeof(v1->artist)); @@ -111,12 +111,12 @@ void print_v1(ID3_Tag * ID3tag, mpg123_id3v1 *v1) /* Split up a number of lines separated by \n, \r, both or just zero byte and print out each line with specified prefix. */ -void print_lines(char * data, const char * prefix, mpg123_string * inlines) +void print_lines(char *data, const char *prefix, mpg123_string *inlines) { size_t i; int hadcr = 0, hadlf = 0; - char * lines = NULL; - char * line = NULL; + char *lines = NULL; + char *line = NULL; size_t len = 0; if (inlines != NULL && inlines->fill) @@ -161,7 +161,7 @@ void print_lines(char * data, const char * prefix, mpg123_string * inlines) } /* Print out the named ID3v2 fields. */ -void print_v2(ID3_Tag * ID3tag, mpg123_id3v2 * v2) +void print_v2(ID3_Tag *ID3tag, mpg123_id3v2 *v2) { print_lines(ID3tag->title, "Title: ", v2->title); print_lines(ID3tag->artist, "", v2->artist); @@ -172,7 +172,7 @@ void print_v2(ID3_Tag * ID3tag, mpg123_id3v2 * v2) } /* Easy conversion to string via lookup. */ -const char * pic_types[] = +const char *pic_types[] = { "other" ,"icon" @@ -197,7 +197,7 @@ const char * pic_types[] = ,"publisher logo" }; -static const char * pic_type(int id) +static const char *pic_type(int id) { return (id >= 0 && id < (sizeof(pic_types)/sizeof(char*))) ? pic_types[id] : "invalid type"; } @@ -254,12 +254,12 @@ void print_raw_v2(mpg123_id3v2 *v2) } } -const char * unknown_end = "picture"; +const char *unknown_end = "picture"; -static char * mime2end(mpg123_string * mime) +static char *mime2end(mpg123_string *mime) { size_t len; - char * end; + char *end; if(strncasecmp("image/",mime->p,6)) { len = strlen(unknown_end)+1; @@ -292,10 +292,10 @@ static char * mime2end(mpg123_string * mime) /* Construct a sane file name without introducing spaces, then open. Example: /some/where/some.mp3.front_cover.jpeg If multiple ones are there: some.mp3.front_cover2.jpeg */ -int open_picfile(const char * prefix, mpg123_picture * pic) +int open_picfile(const char *prefix, mpg123_picture *pic) { - char * end, * typestr, * pfn; - const char * pictype; + char *end, *typestr, *pfn; + const char *pictype; size_t i, len; int fd; unsigned long count = 1; @@ -364,7 +364,7 @@ static void store_pictures(const char* prefix, mpg123_id3v2 *v2) fd = open_picfile(prefix, pic); if(fd >= 0) { /* stream I/O for not having to care about interruptions */ - FILE * picfile = fdopen(fd, "w"); + FILE *picfile = fdopen(fd, "w"); if(picfile) { if(fwrite(pic->data, pic->size, 1, picfile) != 1) @@ -381,12 +381,12 @@ static void store_pictures(const char* prefix, mpg123_id3v2 *v2) } /** - * Initialise MP3 decoder. + *Initialise MP3 decoder. * - * \param file Location of MP3 file to play. - * \return 0 on success, else failure. + *\param file Location of MP3 file to play. + *\return 0 on success, else failure. */ -int initMp3(const char * file) +int initMp3(const char *file) { int err = 0; int encoding = 0; @@ -402,8 +402,8 @@ int initMp3(const char * file) mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_PICTURE, 0.); - mpg123_id3v1 * v1; - mpg123_id3v2 * v2; + mpg123_id3v1 *v1; + mpg123_id3v2 *v2; int meta; if (mpg123_open(mh, file) != MPG123_OK || mpg123_getformat(mh, (long *) &rate, (int *) &channels, &encoding) != MPG123_OK) @@ -430,15 +430,15 @@ int initMp3(const char * file) } /* - * Ensure that this output format will not change (it might, when we allow - * it). + *Ensure that this output format will not change (it might, when we allow + *it). */ mpg123_format_none(mh); mpg123_format(mh, rate, channels, encoding); /* - * Buffer could be almost any size here, mpg123_outblock() is just some - * recommendation. The size should be a multiple of the PCM frame size. + *Buffer could be almost any size here, mpg123_outblock() is just some + *recommendation. The size should be a multiple of the PCM frame size. */ *buffSize = (mpg123_outblock(mh) * 16); @@ -446,9 +446,9 @@ int initMp3(const char * file) } /** - * Get sampling rate of MP3 file. + *Get sampling rate of MP3 file. * - * \return Sampling rate. + *\return Sampling rate. */ u32 rateMp3(void) { @@ -456,9 +456,9 @@ u32 rateMp3(void) } /** - * Get number of channels of MP3 file. + *Get number of channels of MP3 file. * - * \return Number of channels for opened file. + *\return Number of channels for opened file. */ u8 channelMp3(void) { @@ -466,12 +466,12 @@ u8 channelMp3(void) } /** - * Decode part of open MP3 file. + *Decode part of open MP3 file. * - * \param buffer Decoded output. - * \return Samples read for each channel. + *\param buffer Decoded output. + *\return Samples read for each channel. */ -u64 decodeMp3(void * buffer) +u64 decodeMp3(void *buffer) { size_t done = 0; mpg123_read(mh, buffer, *buffSize, &done); @@ -479,7 +479,7 @@ u64 decodeMp3(void * buffer) } /** - * Free MP3 decoder. + *Free MP3 decoder. */ void exitMp3(void) { diff --git a/source/audio/vorbis.c b/source/audio/vorbis.c index f4a9354..ebc43d1 100644 --- a/source/audio/vorbis.c +++ b/source/audio/vorbis.c @@ -7,16 +7,16 @@ #include "vorbis.h" static OggVorbis_File vorbisFile; -static vorbis_info * vi; -static FILE * f; +static vorbis_info *vi; +static FILE *f; static const size_t buffSize = (8 * 4096); /** - * Set decoder parameters for Vorbis. + *Set decoder parameters for Vorbis. * - * \param decoder Structure to store parameters. + *\param decoder Structure to store parameters. */ -void setVorbis(struct decoder_fn * decoder) +void setVorbis(struct decoder_fn *decoder) { decoder->init = &initVorbis; decoder->rate = &rateVorbis; @@ -27,12 +27,12 @@ void setVorbis(struct decoder_fn * decoder) } /** - * Initialise Vorbis decoder. + *Initialise Vorbis decoder. * - * \param file Location of vorbis file to play. - * \return 0 on success, else failure. + *\param file Location of vorbis file to play. + *\return 0 on success, else failure. */ -int initVorbis(const char * file) +int initVorbis(const char *file) { int err = -1; @@ -52,9 +52,9 @@ int initVorbis(const char * file) } /** - * Get sampling rate of Vorbis file. + *Get sampling rate of Vorbis file. * - * \return Sampling rate. + *\return Sampling rate. */ u32 rateVorbis(void) { @@ -62,9 +62,9 @@ u32 rateVorbis(void) } /** - * Get number of channels of Vorbis file. + *Get number of channels of Vorbis file. * - * \return Number of channels for opened file. + *\return Number of channels for opened file. */ u8 channelVorbis(void) { @@ -72,19 +72,19 @@ u8 channelVorbis(void) } /** - * Decode part of open Vorbis file. + *Decode part of open Vorbis file. * - * \param buffer Decoded output. - * \return Samples read for each channel. 0 for end of file, negative + *\param buffer Decoded output. + *\return Samples read for each channel. 0 for end of file, negative * for error. */ -u64 decodeVorbis(void * buffer) +u64 decodeVorbis(void *buffer) { return fillVorbisBuffer(buffer); } /** - * Free Vorbis decoder. + *Free Vorbis decoder. */ void exitVorbis(void) { @@ -93,13 +93,13 @@ void exitVorbis(void) } /** - * Decode Vorbis file to fill buffer. + *Decode Vorbis file to fill buffer. * - * \param opusFile File to decode. - * \param bufferOut Pointer to buffer. - * \return Samples read per channel. + *\param opusFile File to decode. + *\param bufferOut Pointer to buffer. + *\return Samples read per channel. */ -u64 fillVorbisBuffer(char * bufferOut) +u64 fillVorbisBuffer(char *bufferOut) { u64 samplesRead = 0; int samplesToRead = buffSize; @@ -124,14 +124,14 @@ u64 fillVorbisBuffer(char * bufferOut) } /** - * Checks if the input file is Vorbis. + *Checks if the input file is Vorbis. * - * \param in Input file. - * \return 0 if Vorbis file, else not or failure. + *\param in Input file. + *\return 0 if Vorbis file, else not or failure. */ -int isVorbis(const char * in) +int isVorbis(const char *in) { - FILE * ft = fopen(in, "r"); + FILE *ft = fopen(in, "r"); OggVorbis_File testvf; if (ft == NULL) diff --git a/source/audio/wav.c b/source/audio/wav.c index 1247908..f676b78 100644 --- a/source/audio/wav.c +++ b/source/audio/wav.c @@ -11,11 +11,11 @@ static drwav* pWav; static drwav_uint64 samplesRead; /** - * Set decoder parameters for WAV. + *Set decoder parameters for WAV. * - * \param decoder Structure to store parameters. + *\param decoder Structure to store parameters. */ -void setWav(struct decoder_fn * decoder) +void setWav(struct decoder_fn *decoder) { decoder->init = &initWav; decoder->rate = &rateWav; @@ -26,10 +26,10 @@ void setWav(struct decoder_fn * decoder) } /** - * Initialise WAV playback. + *Initialise WAV playback. * - * \param file Location of WAV file to play. - * \return 0 on success, else failure. + *\param file Location of WAV file to play. + *\return 0 on success, else failure. */ int initWav(const char* filename) { @@ -39,9 +39,9 @@ int initWav(const char* filename) } /** - * Get sampling rate of Wav file. + *Get sampling rate of Wav file. * - * \return Sampling rate. + *\return Sampling rate. */ u32 rateWav(void) { @@ -49,9 +49,9 @@ u32 rateWav(void) } /** - * Get number of channels of Wav file. + *Get number of channels of Wav file. * - * \return Number of channels for opened file. + *\return Number of channels for opened file. */ u8 channelWav(void) { @@ -59,19 +59,19 @@ u8 channelWav(void) } /** - * Read part of open Wav file. + *Read part of open Wav file. * - * \param buffer Output. - * \return Samples read for each channel. + *\param buffer Output. + *\return Samples read for each channel. */ -u64 decodeWav(void * buffer) +u64 decodeWav(void *buffer) { samplesRead = drwav_read_s16(pWav, buffSize, buffer); return samplesRead; } /** - * Free Wav file. + *Free Wav file. */ void exitWav(void) { @@ -79,15 +79,15 @@ void exitWav(void) } /** - * Checks if the input file is Wav + *Checks if the input file is Wav * - * \param in Input file. - * \return 0 if Wav file, else not or failure. + *\param in Input file. + *\return 0 if Wav file, else not or failure. */ -int isWav(const char * in) +int isWav(const char *in) { int err = -1; - drwav * testWav = drwav_open_file(in); + drwav *testWav = drwav_open_file(in); if (testWav != NULL) err = 0; diff --git a/source/dirbrowse.c b/source/dirbrowse.c index e483d46..476c9f6 100644 --- a/source/dirbrowse.c +++ b/source/dirbrowse.c @@ -209,7 +209,7 @@ void Dirbrowse_DisplayFiles(void) || (strncasecmp(file->ext, "lz4", 3) == 0)) Draw_Image(icon_archive, 30, 56 + (38 * printed)); else if ((strncasecmp(file->ext, "mp3", 3) == 0) || (strncasecmp(file->ext, "ogg", 3) == 0) - || (strncasecmp(file->ext, "wav", 3) == 0) || (strncasecmp(file->ext, "mod", 3) == 0)) + || (strncasecmp(file->ext, "wav", 3) == 0) || (strncasecmp(file->ext, "fla", 3) == 0)) Draw_Image(icon_audio, 30, 56 + (38 * printed)); else if ((strncasecmp(file->ext, "png", 3) == 0) || (strncasecmp(file->ext, "jpg", 3) == 0) || (strncasecmp(file->ext, "bmp", 3) == 0) || (strncasecmp(file->ext, "gif", 3) == 0)) @@ -304,11 +304,10 @@ void Dirbrowse_OpenFile(void) Archive_ExtractZip(path, cwd); Dirbrowse_PopulateFiles(true); } - /* else if ((strncasecmp(file->ext, "mp3", 3) == 0) || (strncasecmp(file->ext, "ogg", 3) == 0) - || (strncasecmp(file->ext, "wav", 3) == 0) || (strncasecmp(file->ext, "mod", 3) == 0)) + || (strncasecmp(file->ext, "wav", 3) == 0) || (strncasecmp(file->ext, "fla", 3) == 0)) Menu_PlayMusic(path); - else if ((strncasecmp(file->ext, "pdf", 3) == 0) || (strncasecmp(file->ext, "cbz", 3) == 0) + /*else if ((strncasecmp(file->ext, "pdf", 3) == 0) || (strncasecmp(file->ext, "cbz", 3) == 0) || (strncasecmp(file->ext, "fb2", 3) == 0) || (strncasecmp(file->ext, "epub", 4) == 0)) Menu_OpenBook(path);*/ } diff --git a/source/menus/menu_fileoptions.c b/source/menus/menu_fileoptions.c index 0ad7058..f2c69fa 100644 --- a/source/menus/menu_fileoptions.c +++ b/source/menus/menu_fileoptions.c @@ -454,7 +454,7 @@ void Menu_DisplayProperties(void) strcpy(path, cwd); strcpy(path + strlen(path), file->name); - Draw_Image(config_dark_theme? properties_dialog_dark : properties_dialog, 54.5, 30); + Draw_Image(config_dark_theme? properties_dialog_dark : properties_dialog, 54, 30); Draw_Text(61, 37, 0.45f, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR, "Properties"); char utils_size[16]; @@ -477,13 +477,13 @@ void Menu_DisplayProperties(void) static void HandleCopy() { - if (copy_status == false && cut_status == false) + if ((!copy_status) && (!cut_status)) { copy_status = true; FileOptions_Copy(COPY_KEEP_ON_FINISH); MENU_STATE = MENU_STATE_HOME; } - else if (copy_status == true) + else if (copy_status) { if ((multi_select_index > 0) && (strlen(multi_select_dir) != 0)) { @@ -520,13 +520,13 @@ static void HandleCopy() static void HandleCut() { - if (cut_status == false && copy_status == false) + if ((!cut_status) && (!copy_status)) { cut_status = true; FileOptions_Copy(COPY_DELETE_ON_FINISH); MENU_STATE = MENU_STATE_HOME; } - else if (cut_status == true) + else if (cut_status) { char dest[512]; @@ -613,7 +613,7 @@ void Menu_ControlFileOptions(u32 input) void Menu_DisplayFileOptions(void) { - Draw_Image(config_dark_theme? options_dialog_dark : options_dialog, 54.5, 30); + Draw_Image(config_dark_theme? options_dialog_dark : options_dialog, 54, 30); Draw_Text(61, 37, 0.45f, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR, "Actions"); Draw_GetTextSize(0.45f, &options_cancel_width, &options_cancel_height, "CANCEL"); diff --git a/source/menus/menu_main.c b/source/menus/menu_main.c index cf275ff..1875fc6 100644 --- a/source/menus/menu_main.c +++ b/source/menus/menu_main.c @@ -96,11 +96,11 @@ static void Menu_ControlHome(u32 input) void Menu_DrawMenuBar(void) { - Draw_Image(MENU_STATE == MENU_STATE_HOME? icon_home_dark : icon_home, 0, -2.5); + Draw_Image(MENU_STATE == MENU_STATE_HOME? icon_home_dark : icon_home, 0, -2); Draw_Image((MENU_STATE == MENU_STATE_FILEOPTIONS) || (MENU_STATE == MENU_STATE_PROPERTIES)? icon_options_dark : icon_options, 25, 0); Draw_Image((MENU_STATE == MENU_STATE_SETTINGS) || (MENU_STATE == MENU_STATE_SORT)? icon_settings_dark : icon_settings, 50, 0); Draw_Image(MENU_STATE == MENU_STATE_FTP? icon_ftp_dark : icon_ftp, 75, 0); - Draw_Image((MENU_STATE == MENU_STATE_UPDATE) || (MENU_STATE == MENU_STATE_UPDATE_2)? icon_updates_dark : icon_updates, 100, 0.5); + Draw_Image((MENU_STATE == MENU_STATE_UPDATE) || (MENU_STATE == MENU_STATE_UPDATE_2)? icon_updates_dark : icon_updates, 100, 1); Draw_Image(BROWSE_STATE == BROWSE_STATE_SD? icon_sd_dark : icon_sd, 250, 0); Draw_Image(BROWSE_STATE == BROWSE_STATE_NAND? icon_secure_dark : icon_secure, 275, 0); Draw_Image(icon_search, 300, 0); diff --git a/source/menus/menu_music.c b/source/menus/menu_music.c index 740c601..7259964 100644 --- a/source/menus/menu_music.c +++ b/source/menus/menu_music.c @@ -1,13 +1,13 @@ -#include -#include +#include + +#include <3ds.h> #include "audio.h" -#include "flac.h" #include "mp3.h" -#include "vorbis.h" -#include "wav.h" +#include "C2D_helper.h" #include "common.h" +#include "config.h" #include "dirbrowse.h" #include "fs.h" #include "menu_music.h" @@ -17,264 +17,210 @@ #include "touch.h" #include "utils.h" -static volatile bool stop = true; +#define MUSIC_GENRE_COLOUR C2D_Color32(97, 97, 97, 255) +#define MUSIC_STATUS_BG_COLOUR C2D_Color32(43, 53, 61, 255) +#define MUSIC_SEPARATOR_COLOUR C2D_Color32(34, 41, 48, 255) -static void Music_StopPlayback(void) +typedef enum { - stop = true; -} + MUSIC_STATE_NONE, // 0 + MUSIC_STATE_REPEAT, // 1 + MUSIC_STATE_SHUFFLE // 2 +} Music_State; -bool Music_IsPlaying(void) -{ - return !stop; -} +static char playlist[512][512]; +static int count = 0, selection = 0, state = 0; -enum file_types Music_GetMusicFileType(const char *file) +static Result Menu_GetMusicList(void) { - Handle handle; - - u32 fileSig = 0, bytesRead = 0; - u64 offset = 0; - - enum file_types file_type = FILE_TYPE_ERROR; - - /* Failure opening file */ - if (R_FAILED(FS_Open(&handle, archive, file, FS_OPEN_READ))) - { - FSFILE_Close(handle); - return -1; - } - - if (R_FAILED(FSFILE_Read(handle, &bytesRead, offset, &fileSig, 4))) - { - FSFILE_Close(handle); - return -2; - } - - offset += bytesRead; + Handle dir; + Result ret = 0; - switch(fileSig) + if (R_SUCCEEDED(ret = FSUSER_OpenDirectory(&dir, archive, fsMakePath(PATH_ASCII, cwd)))) { - // "RIFF" - case 0x46464952: - if (isWav(file) == 0) - file_type = FILE_TYPE_WAV; - break; - - // "fLaC" - case 0x43614c66: - file_type = FILE_TYPE_FLAC; - break; - - // "OggS" - case 0x5367674F: - if (isFlac(file) == 0) - file_type = FILE_TYPE_FLAC; - else if (isVorbis(file) == 0) - file_type = FILE_TYPE_VORBIS; + u32 entryCount = 0; + FS_DirectoryEntry* entries = (FS_DirectoryEntry*) calloc(MAX_FILES, sizeof(FS_DirectoryEntry)); + + if (R_SUCCEEDED(ret = FSDIR_Read(dir, &entryCount, MAX_FILES, entries))) + { + qsort(entries, entryCount, sizeof(FS_DirectoryEntry), Utils_Alphasort); + u8 name[256] = {'\0'}; - break; - - // MP3 file with an ID3v2 container - default: - if ((fileSig << 16) == 0xFBFF0000 || (fileSig << 16) == 0xFAFF0000 || (fileSig << 8) == 0x33444900) + for (u32 i = 0; i < entryCount; i++) { - file_type = FILE_TYPE_MP3; - break; + Utils_U16_To_U8(&name[0], entries[i].name, 255); + int length = strlen(name); + + if ((strncasecmp(entries[i].shortExt, "mp3", 3) == 0) || (strncasecmp(entries[i].shortExt, "ogg", 3) == 0) + || (strncasecmp(entries[i].shortExt, "fla", 3) == 0) || (strncasecmp(entries[i].shortExt, "wav", 3) == 0)) + { + strcpy(playlist[count], cwd); + strcpy(playlist[count] + strlen(playlist[count]), name); + count++; + } } - } + } + else + { + free(entries); + return ret; + } + + free(entries); - FSFILE_Close(handle); - return file_type; + if (R_FAILED(ret = FSDIR_Close(dir))) // Close directory + return ret; + } + else + return ret; } -static void Music_PlayFile(void *pathIn) +static int Music_GetCurrentIndex(char *path) { - struct decoder_fn decoder; - const char *file = pathIn; - s16 *buffer1 = NULL; - s16 *buffer2 = NULL; - ndspWaveBuf waveBuf[2]; - bool lastbuf = false; - Result ret = -1; - - /* Reset previous stop command */ - stop = false; - - switch(Music_GetMusicFileType(file)) + for(int i = 0; i < count; ++i) { - case FILE_TYPE_WAV: - setWav(&decoder); - break; - - case FILE_TYPE_FLAC: - setFlac(&decoder); - break; - - case FILE_TYPE_MP3: - setMp3(&decoder); - break; - - case FILE_TYPE_VORBIS: - setVorbis(&decoder); - break; - - default: - goto out; + if (!strcmp(playlist[i], path)) + return i; } +} - if ((ret = (*decoder.init)(file)) != 0) - goto out; - - if ((*decoder.channels)() > 2 || (*decoder.channels)() < 1) - goto out; - - buffer1 = linearAlloc(decoder.buffSize *sizeof(s16)); - buffer2 = linearAlloc(decoder.buffSize *sizeof(s16)); - - ndspChnReset(SFX); - ndspChnWaveBufClear(SFX); - ndspSetOutputMode(NDSP_OUTPUT_STEREO); - ndspChnSetInterp(SFX, NDSP_INTERP_POLYPHASE); - ndspChnSetRate(SFX, (*decoder.rate)()); - ndspChnSetFormat(SFX, (*decoder.channels)() == 2 ? NDSP_FORMAT_STEREO_PCM16 : NDSP_FORMAT_MONO_PCM16); - - memset(waveBuf, 0, sizeof(waveBuf)); - waveBuf[0].nsamples = (*decoder.decode)(&buffer1[0]) / (*decoder.channels)(); - waveBuf[0].data_vaddr = &buffer1[0]; - ndspChnWaveBufAdd(SFX, &waveBuf[0]); - - waveBuf[1].nsamples = (*decoder.decode)(&buffer2[0]) / (*decoder.channels)(); - waveBuf[1].data_vaddr = &buffer2[0]; - ndspChnWaveBufAdd(SFX, &waveBuf[1]); - - /** - *There may be a chance that the music has not started by the time we get - *to the while loop. So we ensure that music has started here. - */ - while(ndspChnIsPlaying(SFX) == false); - - while(stop == false) +static void Music_HandleNext(bool forward, int state) +{ + if (state == MUSIC_STATE_NONE) { - svcSleepThread(100 *1000); - - /* When the last buffer has finished playing, break. */ - if ((lastbuf == true) && (waveBuf[0].status == NDSP_WBUF_DONE) && (waveBuf[1].status == NDSP_WBUF_DONE)) - break; - - if ((ndspChnIsPaused(SFX) == true) || (lastbuf == true)) - continue; - - if (waveBuf[0].status == NDSP_WBUF_DONE) - { - size_t read = (*decoder.decode)(&buffer1[0]); - - if (read <= 0) - { - lastbuf = true; - continue; - } - else if (read < decoder.buffSize) - waveBuf[0].nsamples = read / (*decoder.channels)(); - - ndspChnWaveBufAdd(SFX, &waveBuf[0]); - } - - if (waveBuf[1].status == NDSP_WBUF_DONE) - { - size_t read = (*decoder.decode)(&buffer2[0]); - - if (read <= 0) - { - lastbuf = true; - continue; - } - else if (read < decoder.buffSize) - waveBuf[1].nsamples = read / (*decoder.channels)(); - - ndspChnWaveBufAdd(SFX, &waveBuf[1]); - } + if (forward) + selection++; + else + selection--; + } + else if (state == MUSIC_STATE_SHUFFLE) + { + int old_selection = selection; + time_t t; + srand((unsigned) time(&t)); + selection = rand() % (count - 1); - if (R_FAILED(DSP_FlushDataCache(buffer1, decoder.buffSize *sizeof(s16)))) - return; - if (R_FAILED(DSP_FlushDataCache(buffer2, decoder.buffSize *sizeof(s16)))) - return; + if (selection == old_selection) + selection++; } - (*decoder.exit)(); + Utils_SetMax(&selection, 0, (count - 1)); + Utils_SetMin(&selection, (count - 1), 0); -out: - Music_StopPlayback(); - linearFree(buffer1); - linearFree(buffer2); + wait(10); + Audio_StopPlayback(); - threadExit(0); - return; + // Clear ID3 + memset(ID3.artist, 0, 30); + memset(ID3.title, 0, 30); + memset(ID3.album, 0, 30); + memset(ID3.year, 0, 4); + memset(ID3.genre, 0, 30); + + Menu_PlayMusic(playlist[selection]); } -void Music_Player(char *path) +void Menu_PlayMusic(char *path) { s32 prio; static Thread thread = NULL; /* Reset previous stop command */ stop = false; - + svcGetThreadPriority(&prio, CUR_THREAD_HANDLE); - thread = threadCreate(Music_PlayFile, path, 32 *1024, prio - 1, -2, false); + thread = threadCreate(Audio_PlayFile, path, 32 *1024, prio - 1, -2, false); File *file = Dirbrowse_GetFileIndex(position); bool isMP3 = (strncasecmp(file->ext, "mp3", 3) == 0); - while (Music_IsPlaying()) + while (aptMainLoop()) { - hidScanInput(); + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + C2D_TargetClear(RENDER_TOP, config_dark_theme? BLACK_BG : WHITE); + C2D_TargetClear(RENDER_BOTTOM, config_dark_theme? BLACK_BG : WHITE); + C2D_SceneBegin(RENDER_TOP); - u32 kDown = hidKeysDown(); - u32 kHeld = hidKeysHeld(); + Draw_Image(default_artwork_blur, 0, 0); + Draw_Rect(0, 0, 400, 18, MUSIC_GENRE_COLOUR); // Status bar + Draw_Rect(0, 55, 400, 2, MUSIC_SEPARATOR_COLOUR); // Separating line - //if ((kDown & KEY_A) || ((TouchInRect(114, 76, 204, 164)) && (kDown & KEY_TOUCH))) - // Audio_TogglePlayback(SFX); + StatusBar_DisplayTime(); - //pp2d_begin_draw(GFX_BOTTOM, GFX_LEFT); + Draw_Rect(178, 57, 222, 175, C2D_Color32(45, 48, 50, 255)); + Draw_Rect(183, 62, 212, 165, C2D_Color32(46, 49, 51, 255)); - //pp2d_draw_texture(TEXTURE_MUSIC_BOTTOM_BG, 0, 0); + if (isMP3) // Only print out ID3 tag info for MP3 + { + Draw_Text(5, 22, 0.5f, WHITE, strupr(fileName)); + Draw_Text(5, 38, 0.45f, WHITE, strupr(ID3.artist)); + + Draw_Textf(184, 64, 0.5f, WHITE, "%.30s", ID3.title); + Draw_Textf(184, 84, 0.5f, WHITE, "%.30s", ID3.album); + Draw_Textf(184, 104, 0.5f, WHITE, "%.30s", ID3.year); + Draw_Textf(184, 124, 0.5f, WHITE, "%.30s", ID3.genre); + } + else + Draw_Text(5, 25, 0.5f, WHITE, fileName); - /*if (!(Audio_IsPaused(SFX))) - //pp2d_draw_texture(TEXTURE_MUSIC_PAUSE, ((320.0 - //pp2d_get_texture_width(TEXTURE_MUSIC_PAUSE)) / 2.0) - 2, - ((240.0 - //pp2d_get_texture_height(TEXTURE_MUSIC_PAUSE)) / 2.0)); - else - //pp2d_draw_texture(TEXTURE_MUSIC_PLAY, ((320.0 - //pp2d_get_texture_width(TEXTURE_MUSIC_PLAY)) / 2.0), - ((240.0 - //pp2d_get_texture_height(TEXTURE_MUSIC_PLAY)) / 2.0));*/ + Draw_Rect(0, 57, 175, 175, MUSIC_GENRE_COLOUR); + Draw_Image(default_artwork, 0, 57); - //pp2d_end_draw(); + C2D_SceneBegin(RENDER_BOTTOM); - //pp2d_begin_draw(GFX_TOP, GFX_LEFT); + Draw_Image(ic_music_bg_bottom, 0, 0); - //pp2d_draw_texture(TEXTURE_MUSIC_TOP_BG, 0, 0); + hidScanInput(); - StatusBar_DisplayTime(); + u32 kDown = hidKeysDown(); + u32 kHeld = hidKeysHeld(); - if (isMP3) // Only print out ID3 tag info for MP3 - { - //pp2d_draw_textf(5, 20, 0.5f, 0.5f, RGBA8(255, 255, 255, 255), "%s", fileName); - //pp2d_draw_textf(5, 36, 0.45f, 0.45f, RGBA8(255, 255, 255, 255), "%s", ID3.artist); - - //pp2d_draw_textf(184, 64, 0.5f, 0.5f, RGBA8(255, 255, 255, 255), "%.30s", ID3.title); - //pp2d_draw_textf(184, 84, 0.5f, 0.5f, RGBA8(255, 255, 255, 255), "%.30s", ID3.album); - //pp2d_draw_textf(184, 104, 0.5f, 0.5f, RGBA8(255, 255, 255, 255), "%.30s", ID3.year); - //pp2d_draw_textf(184, 124, 0.5f, 0.5f, RGBA8(255, 255, 255, 255), "%.30s", ID3.genre); - } - + if ((kDown & KEY_A) || ((TouchInRect(114, 76, 204, 164)) && (kDown & KEY_TOUCH))) + Audio_TogglePlayback(SFX); + + if (kDown & KEY_Y) + { + if (state == MUSIC_STATE_REPEAT) + state = MUSIC_STATE_NONE; else - //pp2d_draw_textf(5, 25, 0.5f, 0.5f, RGBA8(255, 255, 255, 255), "%s", fileName); + state = MUSIC_STATE_REPEAT; + } + else if (kDown & KEY_X) + { + if (state == MUSIC_STATE_SHUFFLE) + state = MUSIC_STATE_NONE; + else + state = MUSIC_STATE_SHUFFLE; + } + + if ((kDown & KEY_LEFT) || (kDown & KEY_L)) + { + wait(1); + Music_HandleNext(false, MUSIC_STATE_NONE); + } + else if ((kDown & KEY_RIGHT) || (kDown & KEY_R)) + { + wait(1); + Music_HandleNext(true, MUSIC_STATE_NONE); + } + + if (!(Audio_IsPaused(SFX))) + Draw_Image(btn_pause, ((320 - btn_pause.subtex->width) / 2) - 2, ((240 - btn_pause.subtex->height) / 2)); + else + Draw_Image(btn_play, ((320 - btn_play.subtex->width) / 2), ((240 - btn_play.subtex->height) / 2)); + + Draw_Image(btn_rewind, ((320 - btn_rewind.subtex->width) / 2) - 80, ((240 - btn_rewind.subtex->height) / 2)); + Draw_Image(btn_forward, ((320 - btn_forward.subtex->width) / 2) + 80, ((240 - btn_forward.subtex->height) / 2)); + + Draw_Image(state == MUSIC_STATE_SHUFFLE? btn_shuffle_overlay : btn_shuffle, ((320 - btn_shuffle.subtex->width) / 2) - 65, ((240 - btn_shuffle.subtex->height) / 2) + 35); + Draw_Image(state == MUSIC_STATE_REPEAT? btn_repeat_overlay : btn_repeat, ((320 - btn_repeat.subtex->width) / 2) + 65, ((240 - btn_repeat.subtex->height) / 2) + 35); - //pp2d_end_draw(); + Draw_EndFrame(); if (kDown & KEY_B) { wait(10); - Music_StopPlayback(); + Audio_StopPlayback(); break; } @@ -292,5 +238,8 @@ void Music_Player(char *path) memset(ID3.year, 0, 4); memset(ID3.genre, 0, 30); + memset(playlist, 0, sizeof(playlist[0][0]) * 512 * 512); + count = 0; + return; } \ No newline at end of file diff --git a/source/menus/menu_settings.c b/source/menus/menu_settings.c index b7ae31c..836a89a 100644 --- a/source/menus/menu_settings.c +++ b/source/menus/menu_settings.c @@ -95,11 +95,11 @@ void Menu_DisplaySettings(void) Draw_Text(10, 195, 0.45f, config_dark_theme? WHITE : BLACK, "Details about application."); if (config_dark_theme) - Draw_Image(config_dark_theme? icon_toggle_dark_on : icon_toggle_on, 270, 97.5); + Draw_Image(config_dark_theme? icon_toggle_dark_on : icon_toggle_on, 270, 97); else - Draw_Image(icon_toggle_off, 270, 97.5); + Draw_Image(icon_toggle_off, 270, 97); - Draw_Image(config_hidden_files? (config_dark_theme? icon_toggle_dark_on : icon_toggle_on) : icon_toggle_off, 270, 137.5); + Draw_Image(config_hidden_files? (config_dark_theme? icon_toggle_dark_on : icon_toggle_on) : icon_toggle_off, 270, 137); } void Menu_ControlSettings(u32 input) diff --git a/source/textures.c b/source/textures.c index 69ce56d..4a50644 100644 --- a/source/textures.c +++ b/source/textures.c @@ -35,7 +35,6 @@ void Textures_Load(void) icon_toggle_on = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_material_light_toggle_on_normal_idx); icon_toggle_dark_on = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_material_light_toggle_on_normal_dark_idx); icon_toggle_off = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_material_light_toggle_off_normal_idx); - icon_home = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_home_idx); icon_home_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_home_dark_idx); icon_options = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_filesystem_idx); @@ -51,6 +50,9 @@ void Textures_Load(void) icon_search = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_search_idx); icon_updates = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_tab_idx); icon_updates_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_tab_dark_idx); + default_artwork_blur = C2D_SpriteSheetGetImage(spritesheet, sprites_default_artwork_blur_idx); + default_artwork = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_album_lg_idx); + ic_music_bg_bottom = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_music_bg_bottom_idx); btn_play = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_playback_play_idx); btn_pause = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_playback_pause_idx); btn_rewind = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_playback_rewind_idx); From 3b6b258737d9c411170bea5d2c706156eedda7f8 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Sat, 14 Jul 2018 01:23:57 -0500 Subject: [PATCH 12/33] Music player: Add shuffle/repeat/next and previous track --- source/audio/mp3.c | 22 +++---- source/menus/menu_music.c | 127 ++++++++++++++++++++++++-------------- 2 files changed, 92 insertions(+), 57 deletions(-) diff --git a/source/audio/mp3.c b/source/audio/mp3.c index c33d6f7..32825d3 100644 --- a/source/audio/mp3.c +++ b/source/audio/mp3.c @@ -101,12 +101,12 @@ void safe_print(char *tag, char *name, char *data, size_t size) /* Print out ID3v1 info. */ void print_v1(ID3_Tag *ID3tag, mpg123_id3v1 *v1) { - safe_print(ID3tag->title, "Title", v1->title, sizeof(v1->title)); - safe_print(ID3tag->artist, "Artist", v1->artist, sizeof(v1->artist)); - safe_print(ID3tag->album, "Album", v1->album, sizeof(v1->album)); - safe_print(ID3tag->year, "Year", v1->year, sizeof(v1->year)); - safe_print(ID3tag->comment, "Comment", v1->comment, sizeof(v1->comment)); - safe_print(ID3tag->genre, "Genre", genreList[v1->genre].text, sizeof(genreList[v1->genre].text)); + safe_print(ID3tag->title, "", v1->title, sizeof(v1->title)); + safe_print(ID3tag->artist, "", v1->artist, sizeof(v1->artist)); + safe_print(ID3tag->album, "", v1->album, sizeof(v1->album)); + safe_print(ID3tag->year, "", v1->year, sizeof(v1->year)); + safe_print(ID3tag->comment, "", v1->comment, sizeof(v1->comment)); + safe_print(ID3tag->genre, "", genreList[v1->genre].text, sizeof(genreList[v1->genre].text)); } /* Split up a number of lines separated by \n, \r, both or just zero byte @@ -163,12 +163,12 @@ void print_lines(char *data, const char *prefix, mpg123_string *inlines) /* Print out the named ID3v2 fields. */ void print_v2(ID3_Tag *ID3tag, mpg123_id3v2 *v2) { - print_lines(ID3tag->title, "Title: ", v2->title); + print_lines(ID3tag->title, "", v2->title); print_lines(ID3tag->artist, "", v2->artist); - print_lines(ID3tag->album, "Album: ", v2->album); - print_lines(ID3tag->year, "Year: ", v2->year); - print_lines(ID3tag->comment, "Comment: ", v2->comment); - print_lines(ID3tag->genre, "Genre: ", v2->genre); + print_lines(ID3tag->album, "", v2->album); + print_lines(ID3tag->year, "", v2->year); + print_lines(ID3tag->comment, "", v2->comment); + print_lines(ID3tag->genre, "", v2->genre); } /* Easy conversion to string via lookup. */ diff --git a/source/menus/menu_music.c b/source/menus/menu_music.c index 7259964..13e42c0 100644 --- a/source/menus/menu_music.c +++ b/source/menus/menu_music.c @@ -1,3 +1,4 @@ +#include #include #include <3ds.h> @@ -28,7 +29,9 @@ typedef enum MUSIC_STATE_SHUFFLE // 2 } Music_State; -static char playlist[512][512]; +static Thread thread = NULL; +static bool isMP3 = false; +static char playlist[512][512], title[128]; static int count = 0, selection = 0, state = 0; static Result Menu_GetMusicList(void) @@ -84,6 +87,24 @@ static int Music_GetCurrentIndex(char *path) } } +static void Music_Play(char *path) +{ + Menu_GetMusicList(); + + /* Reset previous stop command */ + stop = false; + + s32 prio = 0; + svcGetThreadPriority(&prio, CUR_THREAD_HANDLE); + thread = threadCreate(Audio_PlayFile, path, 32 *1024, prio - 1, -2, false); + + selection = Music_GetCurrentIndex(path); + strncpy(title, strlen(ID3.title) == 0? strupr(Utils_Basename(path)) : strupr(ID3.title), strlen(ID3.title) == 0? strlen(Utils_Basename(path)) + 1 : strlen(ID3.title) + 1); + + isMP3 = (strncasecmp(&path[strlen(path)-3], "mp3", 3) == 0); +} + + static void Music_HandleNext(bool forward, int state) { if (state == MUSIC_STATE_NONE) @@ -107,32 +128,25 @@ static void Music_HandleNext(bool forward, int state) Utils_SetMax(&selection, 0, (count - 1)); Utils_SetMin(&selection, (count - 1), 0); - wait(10); + wait(1); Audio_StopPlayback(); + memset(title, 0, sizeof(title)); - // Clear ID3 memset(ID3.artist, 0, 30); memset(ID3.title, 0, 30); memset(ID3.album, 0, 30); memset(ID3.year, 0, 4); memset(ID3.genre, 0, 30); - Menu_PlayMusic(playlist[selection]); + threadJoin(thread, U64_MAX); + threadFree(thread); + + Music_Play(playlist[selection]); } void Menu_PlayMusic(char *path) { - s32 prio; - static Thread thread = NULL; - - /* Reset previous stop command */ - stop = false; - - svcGetThreadPriority(&prio, CUR_THREAD_HANDLE); - thread = threadCreate(Audio_PlayFile, path, 32 *1024, prio - 1, -2, false); - - File *file = Dirbrowse_GetFileIndex(position); - bool isMP3 = (strncasecmp(file->ext, "mp3", 3) == 0); + Music_Play(path); while (aptMainLoop()) { @@ -152,16 +166,15 @@ void Menu_PlayMusic(char *path) if (isMP3) // Only print out ID3 tag info for MP3 { - Draw_Text(5, 22, 0.5f, WHITE, strupr(fileName)); + Draw_Text(5, 22, 0.5f, WHITE, strupr(title)); Draw_Text(5, 38, 0.45f, WHITE, strupr(ID3.artist)); - - Draw_Textf(184, 64, 0.5f, WHITE, "%.30s", ID3.title); - Draw_Textf(184, 84, 0.5f, WHITE, "%.30s", ID3.album); - Draw_Textf(184, 104, 0.5f, WHITE, "%.30s", ID3.year); - Draw_Textf(184, 124, 0.5f, WHITE, "%.30s", ID3.genre); + + Draw_Textf(184, 64, 0.5f, WHITE, "%.30s", ID3.album); + Draw_Textf(184, 84, 0.5f, WHITE, "%.30s", ID3.year); + Draw_Textf(184, 104, 0.5f, WHITE, "%.30s", ID3.genre); } else - Draw_Text(5, 25, 0.5f, WHITE, fileName); + Draw_Text(5, ((37 - Draw_GetTextHeight(0.5f, title)) / 2) + 18, 0.5f, WHITE, title); Draw_Rect(0, 57, 175, 175, MUSIC_GENRE_COLOUR); Draw_Image(default_artwork, 0, 57); @@ -170,6 +183,19 @@ void Menu_PlayMusic(char *path) Draw_Image(ic_music_bg_bottom, 0, 0); + if (!(Audio_IsPaused(SFX))) + Draw_Image(btn_pause, ((320 - btn_pause.subtex->width) / 2) - 2, ((240 - btn_pause.subtex->height) / 2)); + else + Draw_Image(btn_play, ((320 - btn_play.subtex->width) / 2), ((240 - btn_play.subtex->height) / 2)); + + Draw_Image(btn_rewind, ((320 - btn_rewind.subtex->width) / 2) - 80, ((240 - btn_rewind.subtex->height) / 2)); + Draw_Image(btn_forward, ((320 - btn_forward.subtex->width) / 2) + 80, ((240 - btn_forward.subtex->height) / 2)); + + Draw_Image(state == MUSIC_STATE_SHUFFLE? btn_shuffle_overlay : btn_shuffle, ((320 - btn_shuffle.subtex->width) / 2) - 65, ((240 - btn_shuffle.subtex->height) / 2) + 35); + Draw_Image(state == MUSIC_STATE_REPEAT? btn_repeat_overlay : btn_repeat, ((320 - btn_repeat.subtex->width) / 2) + 65, ((240 - btn_repeat.subtex->height) / 2) + 35); + + Draw_EndFrame(); + hidScanInput(); u32 kDown = hidKeysDown(); @@ -178,14 +204,16 @@ void Menu_PlayMusic(char *path) if ((kDown & KEY_A) || ((TouchInRect(114, 76, 204, 164)) && (kDown & KEY_TOUCH))) Audio_TogglePlayback(SFX); - if (kDown & KEY_Y) + if ((kDown & KEY_Y) || ((TouchInRect(((320 - btn_repeat.subtex->width) / 2) + 65, ((240 - btn_shuffle.subtex->height) / 2) + 35, + (((320 - btn_repeat.subtex->width) / 2) + 65) + 30, (((240 - btn_shuffle.subtex->height) / 2) + 35) + 30)) && (kDown & KEY_TOUCH))) { if (state == MUSIC_STATE_REPEAT) state = MUSIC_STATE_NONE; else state = MUSIC_STATE_REPEAT; } - else if (kDown & KEY_X) + else if ((kDown & KEY_X) || ((TouchInRect(((320 - btn_shuffle.subtex->width) / 2) - 65, ((240 - btn_shuffle.subtex->height) / 2) + 35, + (((320 - btn_shuffle.subtex->width) / 2) - 65) + 30, (((240 - btn_shuffle.subtex->height) / 2) + 35) + 30)) && (kDown & KEY_TOUCH))) { if (state == MUSIC_STATE_SHUFFLE) state = MUSIC_STATE_NONE; @@ -193,37 +221,41 @@ void Menu_PlayMusic(char *path) state = MUSIC_STATE_SHUFFLE; } - if ((kDown & KEY_LEFT) || (kDown & KEY_L)) + if ((kDown & KEY_LEFT) || (kDown & KEY_L) || ((TouchInRect(((320 - btn_rewind.subtex->width) / 2) - 80, ((240 - btn_rewind.subtex->height) / 2), + (((320 - btn_rewind.subtex->width) / 2) - 80) + 45, ((240 - btn_rewind.subtex->height) / 2) + 45)) && (kDown & KEY_TOUCH))) { wait(1); Music_HandleNext(false, MUSIC_STATE_NONE); } - else if ((kDown & KEY_RIGHT) || (kDown & KEY_R)) + else if ((kDown & KEY_RIGHT) || (kDown & KEY_R) || ((TouchInRect(((320 - btn_forward.subtex->width) / 2) + 80, ((240 - btn_forward.subtex->height) / 2), + (((320 - btn_forward.subtex->width) / 2) + 80) + 45, ((240 - btn_forward.subtex->height) / 2) + 45)) && (kDown & KEY_TOUCH))) { wait(1); Music_HandleNext(true, MUSIC_STATE_NONE); } - if (!(Audio_IsPaused(SFX))) - Draw_Image(btn_pause, ((320 - btn_pause.subtex->width) / 2) - 2, ((240 - btn_pause.subtex->height) / 2)); - else - Draw_Image(btn_play, ((320 - btn_play.subtex->width) / 2), ((240 - btn_play.subtex->height) / 2)); - - Draw_Image(btn_rewind, ((320 - btn_rewind.subtex->width) / 2) - 80, ((240 - btn_rewind.subtex->height) / 2)); - Draw_Image(btn_forward, ((320 - btn_forward.subtex->width) / 2) + 80, ((240 - btn_forward.subtex->height) / 2)); - - Draw_Image(state == MUSIC_STATE_SHUFFLE? btn_shuffle_overlay : btn_shuffle, ((320 - btn_shuffle.subtex->width) / 2) - 65, ((240 - btn_shuffle.subtex->height) / 2) + 35); - Draw_Image(state == MUSIC_STATE_REPEAT? btn_repeat_overlay : btn_repeat, ((320 - btn_repeat.subtex->width) / 2) + 65, ((240 - btn_repeat.subtex->height) / 2) + 35); - - Draw_EndFrame(); - if (kDown & KEY_B) { - wait(10); + wait(1); Audio_StopPlayback(); break; } + if (!Audio_IsPlaying()) + { + wait(1); + + if (state == MUSIC_STATE_NONE) + { + Audio_StopPlayback(); + break; + } + else if (state == MUSIC_STATE_REPEAT) + Music_HandleNext(false, MUSIC_STATE_REPEAT); + else if (state == MUSIC_STATE_SHUFFLE) + Music_HandleNext(false, MUSIC_STATE_SHUFFLE); + } + if (((kHeld & KEY_L) && (kDown & KEY_R)) || ((kHeld & KEY_R) && (kDown & KEY_L))) Screenshot_Capture(); } @@ -232,14 +264,17 @@ void Menu_PlayMusic(char *path) threadFree(thread); // Clear ID3 - memset(ID3.artist, 0, 30); - memset(ID3.title, 0, 30); - memset(ID3.album, 0, 30); - memset(ID3.year, 0, 4); - memset(ID3.genre, 0, 30); + if (isMP3) + { + memset(ID3.artist, 0, 30); + memset(ID3.title, 0, 30); + memset(ID3.album, 0, 30); + memset(ID3.year, 0, 4); + memset(ID3.genre, 0, 30); + } + memset(title, 0, sizeof(title)); memset(playlist, 0, sizeof(playlist[0][0]) * 512 * 512); count = 0; - return; } \ No newline at end of file From 73d76c93e78c9e67f084b018376ca051d8b68b7a Mon Sep 17 00:00:00 2001 From: Joel16 Date: Sat, 14 Jul 2018 11:51:27 -0500 Subject: [PATCH 13/33] Add touch controls to settings and about menu --- include/common.h | 1 + include/menus/menu_settings.h | 2 + source/menus/menu_main.c | 6 +- source/menus/menu_settings.c | 113 ++++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 1 deletion(-) diff --git a/include/common.h b/include/common.h index 548ef62..b251500 100644 --- a/include/common.h +++ b/include/common.h @@ -21,6 +21,7 @@ #define MENU_STATE_PROPERTIES 8 #define MENU_STATE_UPDATE 9 #define MENU_STATE_UPDATE_2 10 +#define MENU_STATE_ABOUT 11 #define BROWSE_STATE_SD 0 #define BROWSE_STATE_NAND 1 diff --git a/include/menus/menu_settings.h b/include/menus/menu_settings.h index a47812b..700351b 100644 --- a/include/menus/menu_settings.h +++ b/include/menus/menu_settings.h @@ -3,6 +3,8 @@ #include <3ds.h> +void Menu_DisplayAbout(void); +void Menu_ControlAbout(u32 input); void Menu_DisplaySortSettings(void); void Menu_ControlSortSettings(u32 input); void Menu_DisplaySettings(void); diff --git a/source/menus/menu_main.c b/source/menus/menu_main.c index 1875fc6..e026997 100644 --- a/source/menus/menu_main.c +++ b/source/menus/menu_main.c @@ -214,7 +214,6 @@ void Menu_Main(void) if (MENU_STATE == MENU_STATE_HOME) { - Draw_Textf(2, 225, 0.45f, WHITE, "3DShell %d.%d.%d - %s", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO, GITVERSION); Menu_ControlHome(kDown); Menu_ControlBrowseOptions(kDown); } @@ -243,6 +242,11 @@ void Menu_Main(void) Menu_DisplaySortSettings(); Menu_ControlSortSettings(kDown); } + else if (MENU_STATE == MENU_STATE_ABOUT) + { + Menu_DisplayAbout(); + Menu_ControlAbout(kDown); + } else if (MENU_STATE == MENU_STATE_UPDATE) { Menu_DisplayUpdate(); diff --git a/source/menus/menu_settings.c b/source/menus/menu_settings.c index 836a89a..517fb3f 100644 --- a/source/menus/menu_settings.c +++ b/source/menus/menu_settings.c @@ -6,9 +6,42 @@ #include "menu_settings.h" #include "status_bar.h" #include "textures.h" +#include "touch.h" #include "utils.h" static int selection = 0, max_items = 3; +static float confirm_width = 0, confirm_height = 0; + +void Menu_DisplayAbout(void) +{ + float text_width1 = 0, text_width2 = 0, text_width3 = 0, text_width4 = 0; + Draw_GetTextSize(0.45f, &text_width1, NULL, "3D Shell vx.x.x - xxxxxxx"); + Draw_GetTextSize(0.45f, &text_width2, NULL, "Author: Joel16"); + Draw_GetTextSize(0.45f, &text_width3, NULL, "Assets: Preetisketch/CyanogenMod/LineageOS"); + Draw_GetTextSize(0.45f, &text_width4, NULL, "Music player: deltabeard"); + Draw_GetTextSize(0.45f, &confirm_width, &confirm_height, "OK"); + + Draw_Image(config_dark_theme? dialog_dark : dialog, ((320 - (dialog.subtex->width)) / 2), ((240 - (dialog.subtex->height)) / 2)); + + Draw_Text(((320 - (dialog.subtex->width)) / 2) + 6, ((240 - (dialog.subtex->height)) / 2) + 6, 0.45f, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR, "About"); + + Draw_Textf(((320 - (text_width1)) / 2), ((240 - (dialog.subtex->height)) / 2) + 20, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "3D Shell v%d.%d.%d - %s", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO, GITVERSION); + Draw_Text(((320 - (text_width2)) / 2), ((240 - (dialog.subtex->height)) / 2) + 34, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Author: Joel16"); + Draw_Text(((320 - (text_width3)) / 2), ((240 - (dialog.subtex->height)) / 2) + 48, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Assets: Preetisketch/CyanogenMod/LineageOS"); + Draw_Text(((320 - (text_width4)) / 2), ((240 - (dialog.subtex->height)) / 2) + 62, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Music player: deltabeard"); + + Draw_Rect((288 - confirm_width) - 5, (159 - confirm_height) - 5, confirm_width + 10, confirm_height + 10, config_dark_theme? SELECTOR_COLOUR_DARK : SELECTOR_COLOUR_LIGHT); + Draw_Text(288 - confirm_width, (159 - confirm_height), 0.45f, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR, "OK"); +} + +void Menu_ControlAbout(u32 input) +{ + if ((input & KEY_B) || (input & KEY_A)) + MENU_STATE = MENU_STATE_SETTINGS; + + if (TouchInRect((288 - confirm_width) - 5, (159 - confirm_height) - 5, ((288 - confirm_width) - 5) + confirm_width + 10, ((159 - confirm_height) - 5) + confirm_height + 10)) + MENU_STATE = MENU_STATE_SETTINGS; +} void Menu_DisplaySortSettings(void) { @@ -69,6 +102,50 @@ void Menu_ControlSortSettings(u32 input) else if (input & KEY_DUP) selection--; + if (TouchInRect(0, 55, 320, 94)) + { + selection = 0; + + if (input & KEY_TOUCH) + { + config_sort_by = 0; + Dirbrowse_PopulateFiles(true); + Config_Save(config_dark_theme, config_hidden_files, config_sort_by); + } + } + else if (TouchInRect(0, 95, 320, 134)) + { + selection = 1; + if (input & KEY_TOUCH) + { + config_sort_by = 1; + Dirbrowse_PopulateFiles(true); + Config_Save(config_dark_theme, config_hidden_files, config_sort_by); + } + } + else if (TouchInRect(0, 135, 320, 174)) + { + selection = 2; + + if (input & KEY_TOUCH) + { + config_sort_by = 2; + Dirbrowse_PopulateFiles(true); + Config_Save(config_dark_theme, config_hidden_files, config_sort_by); + } + } + else if (TouchInRect(0, 175, 320, 215)) + { + selection = 3; + + if (input & KEY_TOUCH) + { + config_sort_by = 3; + Dirbrowse_PopulateFiles(true); + Config_Save(config_dark_theme, config_hidden_files, config_sort_by); + } + } + Utils_SetMax(&selection, 0, max_items); Utils_SetMin(&selection, max_items, 0); } @@ -131,6 +208,42 @@ void Menu_ControlSettings(u32 input) else if (input & KEY_DUP) selection--; + if (TouchInRect(0, 55, 320, 94)) + { + selection = 0; + + if (input & KEY_TOUCH) + MENU_STATE = MENU_STATE_SORT; + } + else if (TouchInRect(0, 95, 320, 134)) + { + selection = 1; + + if (input & KEY_TOUCH) + { + config_dark_theme = !config_dark_theme; + Config_Save(config_dark_theme, config_hidden_files, config_sort_by); + } + } + else if (TouchInRect(0, 135, 320, 174)) + { + selection = 2; + + if (input & KEY_TOUCH) + { + config_hidden_files = !config_hidden_files; + Config_Save(config_dark_theme, config_hidden_files, config_sort_by); + Dirbrowse_PopulateFiles(true); + } + } + else if (TouchInRect(0, 175, 320, 215)) + { + selection = 3; + + if (input & KEY_TOUCH) + MENU_STATE = MENU_STATE_ABOUT; + } + Utils_SetMax(&selection, 0, max_items); Utils_SetMin(&selection, max_items, 0); } From 1da95f55888b2e60c662896b04c9bcf4dd24d43c Mon Sep 17 00:00:00 2001 From: Joel16 Date: Sat, 14 Jul 2018 12:35:33 -0500 Subject: [PATCH 14/33] Add touch controls to file options --- source/menus/menu_fileoptions.c | 85 +++++++++++++++++++++++++++++++++ source/menus/menu_settings.c | 3 +- 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/source/menus/menu_fileoptions.c b/source/menus/menu_fileoptions.c index f2c69fa..8b05a37 100644 --- a/source/menus/menu_fileoptions.c +++ b/source/menus/menu_fileoptions.c @@ -414,6 +414,27 @@ void Menu_ControlDeleteDialog(u32 input) delete_dialog_selection = 0; } + + if (TouchInRect((288 - delete_cancel_width) - 5, (159 - delete_cancel_height) - 5, ((288 - delete_cancel_width) - 5) + delete_cancel_width + 10, ((159 - delete_cancel_height) - 5) + delete_cancel_height + 10)) + { + delete_dialog_selection = 0; + + if (input & KEY_TOUCH) + { + MENU_STATE = MENU_STATE_FILEOPTIONS; + delete_dialog_selection = 0; + } + } + else if (TouchInRect((248 - (delete_confirm_width)) - 5, (159 - delete_confirm_height) - 5, ((248 - (delete_confirm_width)) - 5) + delete_confirm_width + 10, ((159 - delete_confirm_height) - 5) + delete_confirm_height + 10)) + { + delete_dialog_selection = 1; + + if (input & KEY_TOUCH) + { + HandleDelete(); + delete_dialog_selection = 0; + } + } } void Menu_DisplayDeleteDialog(void) @@ -443,6 +464,10 @@ void Menu_ControlProperties(u32 input) { if ((input & KEY_A) || (input & KEY_B)) MENU_STATE = MENU_STATE_FILEOPTIONS; + + if (TouchInRect((253 - properties_ok_width) - 5, (218 - properties_ok_height) - 5, ((253 - properties_ok_width) - 5) + properties_ok_width + 10, ((218 - properties_ok_height) - 5) + properties_ok_height + 10)) + if (input & KEY_TOUCH) + MENU_STATE = MENU_STATE_FILEOPTIONS; } void Menu_DisplayProperties(void) @@ -609,6 +634,66 @@ void Menu_ControlFileOptions(u32 input) if (input & KEY_X) MENU_STATE = MENU_STATE_HOME; + + if (TouchInRect(56, 69, 159, 104)) + { + row = 0; + column = 0; + + if (input & KEY_TOUCH) + MENU_STATE = MENU_STATE_PROPERTIES; + } + else if (TouchInRect(160, 69, 263, 104)) + { + row = 1; + column = 0; + + if (input & KEY_TOUCH) + FileOptions_CreateFolder(); + } + else if (TouchInRect(56, 105, 159, 141)) + { + row = 0; + column = 1; + + if (input & KEY_TOUCH) + FileOptions_Rename(); + } + else if (TouchInRect(160, 105, 263, 141)) + { + row = 1; + column = 1; + + if (input & KEY_TOUCH) + HandleCopy(); + } + else if (TouchInRect(56, 142, 159, 178)) + { + row = 0; + column = 2; + + if (input & KEY_TOUCH) + HandleCut(); + } + else if (TouchInRect(160, 142, 263, 178)) + { + row = 1; + column = 2; + + if (input & KEY_TOUCH) + MENU_STATE = MENU_STATE_DIALOG; + } + else if (TouchInRect(258 - options_cancel_width, 223 - options_cancel_height, (258 - options_cancel_width) + options_cancel_width, (223 - options_cancel_height) + options_cancel_height)) + { + if (input & KEY_TOUCH) + { + copy_status = false; + cut_status = false; + row = 0; + column = 0; + MENU_STATE = MENU_STATE_HOME; + } + } } void Menu_DisplayFileOptions(void) diff --git a/source/menus/menu_settings.c b/source/menus/menu_settings.c index 517fb3f..48a66d3 100644 --- a/source/menus/menu_settings.c +++ b/source/menus/menu_settings.c @@ -40,7 +40,8 @@ void Menu_ControlAbout(u32 input) MENU_STATE = MENU_STATE_SETTINGS; if (TouchInRect((288 - confirm_width) - 5, (159 - confirm_height) - 5, ((288 - confirm_width) - 5) + confirm_width + 10, ((159 - confirm_height) - 5) + confirm_height + 10)) - MENU_STATE = MENU_STATE_SETTINGS; + if (input & KEY_TOUCH) + MENU_STATE = MENU_STATE_SETTINGS; } void Menu_DisplaySortSettings(void) From 3e1be4e3ca773edf3a10baba176a6297d24ccda0 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Sat, 14 Jul 2018 15:04:09 -0500 Subject: [PATCH 15/33] Fix updater and misc changes --- include/common.h | 2 +- include/fs.h | 2 +- source/config.c | 4 ++-- source/fs.c | 41 +++++++++++++++------------------ source/menus/menu_fileoptions.c | 11 +++++---- source/menus/menu_main.c | 8 ++++--- source/menus/menu_music.c | 1 - source/menus/menu_settings.c | 3 +++ source/menus/menu_update.c | 15 +++++++++--- source/net.c | 2 +- 10 files changed, 50 insertions(+), 39 deletions(-) diff --git a/include/common.h b/include/common.h index b251500..1d8c568 100644 --- a/include/common.h +++ b/include/common.h @@ -17,7 +17,7 @@ #define MENU_STATE_FTP 4 #define MENU_STATE_SORT 5 #define MENU_STATE_THEMES 6 -#define MENU_STATE_DIALOG 7 +#define MENU_STATE_DELETE 7 #define MENU_STATE_PROPERTIES 8 #define MENU_STATE_UPDATE 9 #define MENU_STATE_UPDATE_2 10 diff --git a/include/fs.h b/include/fs.h index b3900b9..f630992 100644 --- a/include/fs.h +++ b/include/fs.h @@ -12,7 +12,7 @@ Result FS_RecursiveMakeDir(FS_Archive archive, const char *dir); bool FS_FileExists(FS_Archive archive, const char *path); bool FS_DirExists(FS_Archive archive, const char *path); char* FS_GetFileModifiedTime(char *path); -u64 FS_GetFileSize(FS_Archive archive, const char *path); +Result FS_GetFileSize(FS_Archive archive, const char *path, u64 *size); u64 FS_GetFreeStorage(FS_SystemMediaType mediaType); u64 FS_GetTotalStorage(FS_SystemMediaType mediaType); u64 FS_GetUsedStorage(FS_SystemMediaType mediaType); diff --git a/source/config.c b/source/config.c index 7732dce..5f52c50 100644 --- a/source/config.c +++ b/source/config.c @@ -45,7 +45,7 @@ Result Config_Load(void) u64 size64 = 0; u32 size = 0; - size64 = FS_GetFileSize(archive, "/3ds/3DShell/config.cfg"); + FS_GetFileSize(archive, "/3ds/3DShell/config.cfg", &size64); size = (u32)size64; char *buf = (char *)malloc(size + 1); @@ -78,7 +78,7 @@ Result Config_GetLastDirectory(void) u64 size64 = 0; u32 size = 0; - size64 = FS_GetFileSize(archive, "/3ds/3DShell/lastdir.txt"); + FS_GetFileSize(archive, "/3ds/3DShell/lastdir.txt", &size64); size = (u32)size64; char *buf = (char *)malloc(size + 1); diff --git a/source/fs.c b/source/fs.c index e79a38a..31cd198 100644 --- a/source/fs.c +++ b/source/fs.c @@ -5,7 +5,7 @@ #include "fs.h" #include "utils.h" -Result FS_OpenArchive(FS_Archive * archive, FS_ArchiveID archiveID) +Result FS_OpenArchive(FS_Archive *archive, FS_ArchiveID archiveID) { Result ret = 0; @@ -25,7 +25,7 @@ Result FS_CloseArchive(FS_Archive archive) return 0; } -Result FS_MakeDir(FS_Archive archive, const char * path) +Result FS_MakeDir(FS_Archive archive, const char *path) { Result ret = 0; @@ -35,7 +35,7 @@ Result FS_MakeDir(FS_Archive archive, const char * path) return 0; } -Result FS_RecursiveMakeDir(FS_Archive archive, const char * dir) +Result FS_RecursiveMakeDir(FS_Archive archive, const char *dir) { Result ret = 0; char buf[256]; @@ -65,7 +65,7 @@ Result FS_RecursiveMakeDir(FS_Archive archive, const char * dir) return ret; } -bool FS_FileExists(FS_Archive archive, const char * path) +bool FS_FileExists(FS_Archive archive, const char *path) { Handle handle; @@ -78,7 +78,7 @@ bool FS_FileExists(FS_Archive archive, const char * path) return true; } -bool FS_DirExists(FS_Archive archive, const char * path) +bool FS_DirExists(FS_Archive archive, const char *path) { Handle handle; @@ -91,7 +91,7 @@ bool FS_DirExists(FS_Archive archive, const char * path) return true; } -/*char * FS_GetFileModifiedTime(char * path) +/*char *FS_GetFileModifiedTime(char *path) { static char timeStr[20]; u64 mtime = 0; @@ -127,22 +127,19 @@ bool FS_DirExists(FS_Archive archive, const char * path) return timeStr; }*/ -u64 FS_GetFileSize(FS_Archive archive, const char * path) +Result FS_GetFileSize(FS_Archive archive, const char *path, u64 *size) { Handle handle; Result ret = 0; - u64 st_size = 0; if (R_FAILED(ret = FSUSER_OpenFile(&handle, archive, fsMakePath(PATH_ASCII, path), FS_OPEN_READ, 0))) return ret; - if (R_FAILED(ret = FSFILE_GetSize(handle, &st_size))) + if (R_FAILED(ret = FSFILE_GetSize(handle, size))) return ret; if (R_FAILED(ret = FSFILE_Close(handle))) return ret; - - return st_size; } u64 FS_GetFreeStorage(FS_SystemMediaType mediaType) @@ -150,7 +147,7 @@ u64 FS_GetFreeStorage(FS_SystemMediaType mediaType) FS_ArchiveResource resource = {0}; if (R_SUCCEEDED(FSUSER_GetArchiveResource(&resource, mediaType))) - return (((u64) resource.freeClusters * (u64) resource.clusterSize)); + return (((u64) resource.freeClusters *(u64) resource.clusterSize)); return 0; } @@ -160,7 +157,7 @@ u64 FS_GetTotalStorage(FS_SystemMediaType mediaType) FS_ArchiveResource resource = {0}; if (R_SUCCEEDED(FSUSER_GetArchiveResource(&resource, mediaType))) - return (((u64) resource.totalClusters * (u64) resource.clusterSize)); + return (((u64) resource.totalClusters *(u64) resource.clusterSize)); return 0; } @@ -170,12 +167,12 @@ u64 FS_GetUsedStorage(FS_SystemMediaType mediaType) FS_ArchiveResource resource = {0}; if (R_SUCCEEDED(FSUSER_GetArchiveResource(&resource, mediaType))) - return ((((u64) resource.totalClusters * (u64) resource.clusterSize)) - (((u64) resource.freeClusters * (u64) resource.clusterSize))); + return ((((u64) resource.totalClusters *(u64) resource.clusterSize)) - (((u64) resource.freeClusters *(u64) resource.clusterSize))); return 0; } -Result FS_Remove(FS_Archive archive, const char * filename) +Result FS_Remove(FS_Archive archive, const char *filename) { Result ret = 0; @@ -185,7 +182,7 @@ Result FS_Remove(FS_Archive archive, const char * filename) return 0; } -Result FS_Rmdir(FS_Archive archive, const char * path) +Result FS_Rmdir(FS_Archive archive, const char *path) { Result ret = 0; @@ -195,7 +192,7 @@ Result FS_Rmdir(FS_Archive archive, const char * path) return 0; } -Result FS_RmdirRecursive(FS_Archive archive, const char * path) +Result FS_RmdirRecursive(FS_Archive archive, const char *path) { Result ret = 0; @@ -205,7 +202,7 @@ Result FS_RmdirRecursive(FS_Archive archive, const char * path) return 0; } -Result FS_RenameFile(FS_Archive archive, const char * old_filename, const char * new_filename) +Result FS_RenameFile(FS_Archive archive, const char *old_filename, const char *new_filename) { Result ret = 0; @@ -215,7 +212,7 @@ Result FS_RenameFile(FS_Archive archive, const char * old_filename, const char * return 0; } -Result FS_RenameDir(FS_Archive archive, const char * old_filename, const char * new_filename) +Result FS_RenameDir(FS_Archive archive, const char *old_filename, const char *new_filename) { Result ret = 0; @@ -225,7 +222,7 @@ Result FS_RenameDir(FS_Archive archive, const char * old_filename, const char * return 0; } -Result FS_Open(Handle * handle, FS_Archive archive, const char * path, u32 flags) +Result FS_Open(Handle *handle, FS_Archive archive, const char *path, u32 flags) { Result ret = 0; @@ -235,7 +232,7 @@ Result FS_Open(Handle * handle, FS_Archive archive, const char * path, u32 flags return 0; } -Result FS_Read(FS_Archive archive, const char * path, u64 size, char * buf) +Result FS_Read(FS_Archive archive, const char *path, u64 size, char *buf) { Handle handle; Result ret = 0; @@ -254,7 +251,7 @@ Result FS_Read(FS_Archive archive, const char * path, u64 size, char * buf) return 0; } -Result FS_Write(FS_Archive archive, const char * path, const char * buf) +Result FS_Write(FS_Archive archive, const char *path, const char *buf) { Handle handle; Result ret = 0; diff --git a/source/menus/menu_fileoptions.c b/source/menus/menu_fileoptions.c index 8b05a37..3016c2a 100644 --- a/source/menus/menu_fileoptions.c +++ b/source/menus/menu_fileoptions.c @@ -152,7 +152,8 @@ static int FileOptions_CopyFile(char *src, char *dst, bool displayAnim) int result = 0; // Result int in = open(src, O_RDONLY, 0777); // Open file for reading - u64 size = FS_GetFileSize(archive, src); + u64 size = 0; + FS_GetFileSize(archive, src, &size); // Opened file for reading if (in >= 0) @@ -485,9 +486,9 @@ void Menu_DisplayProperties(void) char utils_size[16]; u64 size = 0; - if (file->isDir) + if (!file->isDir) { - size = FS_GetFileSize(archive, path); + FS_GetFileSize(archive, path, &size); Utils_GetSizeString(utils_size, size); } @@ -620,7 +621,7 @@ void Menu_ControlFileOptions(u32 input) else if (row == 0 && column == 2) HandleCut(); else if (row == 1 && column == 2) - MENU_STATE = MENU_STATE_DIALOG; + MENU_STATE = MENU_STATE_DELETE; } if (input & KEY_B) @@ -681,7 +682,7 @@ void Menu_ControlFileOptions(u32 input) column = 2; if (input & KEY_TOUCH) - MENU_STATE = MENU_STATE_DIALOG; + MENU_STATE = MENU_STATE_DELETE; } else if (TouchInRect(258 - options_cancel_width, 223 - options_cancel_height, (258 - options_cancel_width) + options_cancel_width, (223 - options_cancel_height) + options_cancel_height)) { diff --git a/source/menus/menu_main.c b/source/menus/menu_main.c index e026997..e3c223d 100644 --- a/source/menus/menu_main.c +++ b/source/menus/menu_main.c @@ -97,8 +97,10 @@ static void Menu_ControlHome(u32 input) void Menu_DrawMenuBar(void) { Draw_Image(MENU_STATE == MENU_STATE_HOME? icon_home_dark : icon_home, 0, -2); - Draw_Image((MENU_STATE == MENU_STATE_FILEOPTIONS) || (MENU_STATE == MENU_STATE_PROPERTIES)? icon_options_dark : icon_options, 25, 0); - Draw_Image((MENU_STATE == MENU_STATE_SETTINGS) || (MENU_STATE == MENU_STATE_SORT)? icon_settings_dark : icon_settings, 50, 0); + Draw_Image((MENU_STATE == MENU_STATE_FILEOPTIONS) || (MENU_STATE == MENU_STATE_PROPERTIES) || (MENU_STATE == MENU_STATE_DELETE)? + icon_options_dark : icon_options, 25, 0); + Draw_Image((MENU_STATE == MENU_STATE_SETTINGS) || (MENU_STATE == MENU_STATE_SORT) || (MENU_STATE == MENU_STATE_ABOUT)? + icon_settings_dark : icon_settings, 50, 0); Draw_Image(MENU_STATE == MENU_STATE_FTP? icon_ftp_dark : icon_ftp, 75, 0); Draw_Image((MENU_STATE == MENU_STATE_UPDATE) || (MENU_STATE == MENU_STATE_UPDATE_2)? icon_updates_dark : icon_updates, 100, 1); Draw_Image(BROWSE_STATE == BROWSE_STATE_SD? icon_sd_dark : icon_sd, 250, 0); @@ -227,7 +229,7 @@ void Menu_Main(void) Menu_DisplayProperties(); Menu_ControlProperties(kDown); } - else if (MENU_STATE == MENU_STATE_DIALOG) + else if (MENU_STATE == MENU_STATE_DELETE) { Menu_DisplayDeleteDialog(); Menu_ControlDeleteDialog(kDown); diff --git a/source/menus/menu_music.c b/source/menus/menu_music.c index 13e42c0..d6a5357 100644 --- a/source/menus/menu_music.c +++ b/source/menus/menu_music.c @@ -1,4 +1,3 @@ -#include #include #include <3ds.h> diff --git a/source/menus/menu_settings.c b/source/menus/menu_settings.c index 48a66d3..e7d20a6 100644 --- a/source/menus/menu_settings.c +++ b/source/menus/menu_settings.c @@ -201,6 +201,9 @@ void Menu_ControlSettings(u32 input) Config_Save(config_dark_theme, config_hidden_files, config_sort_by); Dirbrowse_PopulateFiles(true); break; + case 3: + MENU_STATE = MENU_STATE_ABOUT; + break; } } diff --git a/source/menus/menu_update.c b/source/menus/menu_update.c index 1029949..ebd7fd0 100644 --- a/source/menus/menu_update.c +++ b/source/menus/menu_update.c @@ -25,9 +25,9 @@ static bool Menu_ValidateUpdate(bool nighlty) u64 size64 = 0; u32 size = 0; - size64 = FS_GetFileSize(archive, "/3ds/3DShell/UPDATE_NIGHTLY.txt"); + FS_GetFileSize(archive, "/3ds/3DShell/UPDATE_NIGHTLY.txt", &size64); size = (u32)size64; - char * buf = (char *)malloc(size + 1); + char *buf = (char *)malloc(size + 1); if (R_FAILED(FS_Read(archive, "/3ds/3DShell/UPDATE_NIGHTLY.txt", size, buf))) { @@ -39,7 +39,7 @@ static bool Menu_ValidateUpdate(bool nighlty) sscanf(buf, "%s", ver); free(buf); - if (strcmp(ver, GITVERSION) != 0) + if (strcmp(ver, GITVERSION) != 0) return true; return false; @@ -112,6 +112,7 @@ void Menu_ControlUpdate(u32 input) { wait(1); Net_DownloadFile("https://github.com/joel16/3DShell/raw/gh-pages/UPDATE_NIGHTLY.txt", "/3ds/3DShell/UPDATE_NIGHTLY.txt"); + if (Menu_ValidateUpdate(true)) MENU_STATE = MENU_STATE_UPDATE_2; else @@ -131,6 +132,14 @@ void Menu_ControlUpdate(u32 input) void Menu_ControlUpdate2(u32 input) { + if (input & KEY_RIGHT) + update_dialog_selection++; + else if (input & KEY_LEFT) + update_dialog_selection--; + + Utils_SetMax(&update_dialog_selection, 0, 1); + Utils_SetMin(&update_dialog_selection, 1, 0); + if (input & KEY_B) { wait(1); diff --git a/source/net.c b/source/net.c index fe7a8ba..1a32641 100644 --- a/source/net.c +++ b/source/net.c @@ -140,7 +140,7 @@ Result Net_DownloadFile(const char *url, const char *path) FSFILE_Write(fileHandle, &bytesWritten, offset, result_buf, result_written, 0); - ProgressBar_DisplayProgress("Downloading", Utils_Basename(path), 0, 0); + //ProgressBar_DisplayProgress("Downloading", Utils_Basename(path), 0, 0); u64 endTime = osGetTime(); u64 totalTime = endTime - startTime; From 63713c38969cfc7fda754f88e2f5a81acef15f46 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Sun, 15 Jul 2018 01:49:09 -0500 Subject: [PATCH 16/33] Loading png/jpg now works for image viewer --- Makefile | 6 +- include/graphics/libnsbmp.h | 250 + include/{menus => graphics}/lodepng.h | 0 include/graphics/stb_image.h | 7462 +++++++++++++++++++++++++ source/graphics/libnsbmp.c | 1371 +++++ source/{menus => graphics}/lodepng.c | 167 +- source/menus/menu_gallery.c | 375 +- 7 files changed, 9425 insertions(+), 206 deletions(-) create mode 100644 include/graphics/libnsbmp.h rename include/{menus => graphics}/lodepng.h (100%) create mode 100644 include/graphics/stb_image.h create mode 100644 source/graphics/libnsbmp.c rename source/{menus => graphics}/lodepng.c (97%) diff --git a/Makefile b/Makefile index 6933e17..139ddbf 100644 --- a/Makefile +++ b/Makefile @@ -33,9 +33,9 @@ include $(DEVKITARM)/3ds_rules #--------------------------------------------------------------------------------- TARGET := $(notdir $(CURDIR)) BUILD := build -SOURCES := source source/audio source/ftp source/menus source/minizip source/menus/menu_book_reader +SOURCES := source source/audio source/ftp source/graphics source/menus source/minizip DATA := data -INCLUDES := include include/audio include/dr_libs include/ftp include/menus include/minizip mupdf/include mupdf/source/fitz +INCLUDES := include include/audio include/dr_libs include/ftp include/graphics include/menus include/minizip GRAPHICS := res/drawable ROMFS := romfs GFXBUILD := $(ROMFS)/res/drawable @@ -67,7 +67,7 @@ ASFLAGS := -g $(ARCH) LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) LIBS := -lcurl -lmbedtls -lmbedx509 -lmbedcrypto -lmpg123 -lvorbisidec -logg \ - -lturbojpeg -lpng -lcitro2d -lcitro3d -lctru -lm -lz + -lcitro2d -lcitro3d -lctru -lm -lz #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing diff --git a/include/graphics/libnsbmp.h b/include/graphics/libnsbmp.h new file mode 100644 index 0000000..7e90b4a --- /dev/null +++ b/include/graphics/libnsbmp.h @@ -0,0 +1,250 @@ +/* + * Copyright 2006 Richard Wilson + * Copyright 2008 Sean Fox + * + * This file is part of NetSurf's libnsbmp, http://www.netsurf-browser.org/ + * Licenced under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + */ + +/** + * \file + * Bitmap file decoding interface. + */ + +#ifndef libnsbmp_h_ +#define libnsbmp_h_ + +#include +#include +#include + +/* bmp flags */ +#define BMP_NEW 0 +/** image is opaque (as opposed to having an alpha mask) */ +#define BMP_OPAQUE (1 << 0) +/** memory should be wiped */ +#define BMP_CLEAR_MEMORY (1 << 1) + +/** + * error return values + */ +typedef enum { + BMP_OK = 0, + BMP_INSUFFICIENT_MEMORY = 1, + BMP_INSUFFICIENT_DATA = 2, + BMP_DATA_ERROR = 3 +} bmp_result; + +/** + * encoding types + */ +typedef enum { + BMP_ENCODING_RGB = 0, + BMP_ENCODING_RLE8 = 1, + BMP_ENCODING_RLE4 = 2, + BMP_ENCODING_BITFIELDS = 3 +} bmp_encoding; + +/* API for Bitmap callbacks */ +typedef void* (*bmp_bitmap_cb_create)(int width, int height, unsigned int state); +typedef void (*bmp_bitmap_cb_destroy)(void *bitmap); +typedef unsigned char* (*bmp_bitmap_cb_get_buffer)(void *bitmap); +typedef size_t (*bmp_bitmap_cb_get_bpp)(void *bitmap); + +/** + * The Bitmap callbacks function table + */ +typedef struct bmp_bitmap_callback_vt_s { + /** Callback to allocate bitmap storage. */ + bmp_bitmap_cb_create bitmap_create; + /** Called to free bitmap storage. */ + bmp_bitmap_cb_destroy bitmap_destroy; + /** Return a pointer to the pixel data in a bitmap. */ + bmp_bitmap_cb_get_buffer bitmap_get_buffer; + /** Find the width of a pixel row in bytes. */ + bmp_bitmap_cb_get_bpp bitmap_get_bpp; +} bmp_bitmap_callback_vt; + +/** + * bitmap image + */ +typedef struct bmp_image { + /** callbacks for bitmap functions */ + bmp_bitmap_callback_vt bitmap_callbacks; + /** pointer to BMP data */ + uint8_t *bmp_data; + /** width of BMP (valid after _analyse) */ + uint32_t width; + /** heigth of BMP (valid after _analyse) */ + uint32_t height; + /** whether the image has been decoded */ + bool decoded; + /** decoded image */ + void *bitmap; + + /* Internal members are listed below */ + /** total number of bytes of BMP data available */ + uint32_t buffer_size; + /** pixel encoding type */ + bmp_encoding encoding; + /** offset of bitmap data */ + uint32_t bitmap_offset; + /** bits per pixel */ + uint16_t bpp; + /** number of colours */ + uint32_t colours; + /** colour table */ + uint32_t *colour_table; + /** whether to use bmp's limited transparency */ + bool limited_trans; + /** colour to display for "transparent" pixels when using limited + * transparency + */ + uint32_t trans_colour; + /** scanlines are top to bottom */ + bool reversed; + /** image is part of an ICO, mask follows */ + bool ico; + /** true if the bitmap does not contain an alpha channel */ + bool opaque; + /** four bitwise mask */ + uint32_t mask[4]; + /** four bitwise shifts */ + int32_t shift[4]; + /** colour representing "transparency" in the bitmap */ + uint32_t transparent_index; +} bmp_image; + +typedef struct ico_image { + bmp_image bmp; + struct ico_image *next; +} ico_image; + +/** + * icon image collection + */ +typedef struct ico_collection { + /** callbacks for bitmap functions */ + bmp_bitmap_callback_vt bitmap_callbacks; + /** width of largest BMP */ + uint16_t width; + /** heigth of largest BMP */ + uint16_t height; + + /* Internal members are listed below */ + /** pointer to ICO data */ + uint8_t *ico_data; + /** total number of bytes of ICO data available */ + uint32_t buffer_size; + /** root of linked list of images */ + ico_image *first; +} ico_collection; + +/** + * Initialises bitmap ready for analysing the bitmap. + * + * \param bmp The Bitmap to initialise + * \param callbacks The callbacks the library will call on operations. + * \return BMP_OK on success or appropriate error code. + */ +bmp_result bmp_create(bmp_image *bmp, bmp_bitmap_callback_vt *callbacks); + +/** + * Initialises icon ready for analysing the icon + * + * \param bmp The Bitmap to initialise + * \param callbacks The callbacks the library will call on operations. + * \return BMP_OK on success or appropriate error code. + */ +bmp_result ico_collection_create(ico_collection *ico, + bmp_bitmap_callback_vt *callbacks); + +/** + * Analyse a BMP prior to decoding. + * + * This will scan the data provided and perform checks to ensure the data is a + * valid BMP and prepare the bitmap image structure ready for decode. + * + * This function must be called and resturn BMP_OK before bmp_decode() as it + * prepares the bmp internal state for the decode process. + * + * \param bmp the BMP image to analyse. + * \param size The size of data in cdata. + * \param data The bitmap source data. + * \return BMP_OK on success or error code on faliure. + */ +bmp_result bmp_analyse(bmp_image *bmp, size_t size, uint8_t *data); + +/** + * Analyse an ICO prior to decoding. + * + * This function will scan the data provided and perform checks to ensure the + * data is a valid ICO. + * + * This function must be called before ico_find(). + * + * \param ico the ICO image to analyse + * \param size The size of data in cdata. + * \param data The bitmap source data. + * \return BMP_OK on success + */ +bmp_result ico_analyse(ico_collection *ico, size_t size, uint8_t *data); + +/** + * Decode a BMP + * + * This function decodes the BMP data such that bmp->bitmap is a valid + * image. The state of bmp->decoded is set to TRUE on exit such that it + * can easily be identified which BMPs are in a fully decoded state. + * + * \param bmp the BMP image to decode + * \return BMP_OK on success + */ +bmp_result bmp_decode(bmp_image *bmp); + +/** + * Decode a BMP using "limited transparency" + * + * Bitmaps do not have native transparency support. However, there is a + * "trick" that is used in some instances in which the first pixel of the + * bitmap becomes the "transparency index". The decoding application can + * replace this index with whatever background colour it chooses to + * create the illusion of transparency. + * + * When to use transparency is at the discretion of the decoding + * application. + * + * \param bmp the BMP image to decode + * \param colour the colour to use as "transparent" + * \return BMP_OK on success + */ +bmp_result bmp_decode_trans(bmp_image *bmp, uint32_t transparent_colour); + +/** + * Finds the closest BMP within an ICO collection + * + * This function finds the BMP with dimensions as close to a specified set + * as possible from the images in the collection. + * + * \param ico the ICO collection to examine + * \param width the preferred width (0 to use ICO header width) + * \param height the preferred height (0 to use ICO header height) + */ +bmp_image *ico_find(ico_collection *ico, uint16_t width, uint16_t height); + +/** + * Finalise a BMP prior to destruction. + * + * \param bmp the BMP image to finalise. + */ +void bmp_finalise(bmp_image *bmp); + +/** + * Finalise an ICO prior to destruction. + * + * \param ico the ICO image to finalise, + */ +void ico_finalise(ico_collection *ico); + +#endif diff --git a/include/menus/lodepng.h b/include/graphics/lodepng.h similarity index 100% rename from include/menus/lodepng.h rename to include/graphics/lodepng.h diff --git a/include/graphics/stb_image.h b/include/graphics/stb_image.h new file mode 100644 index 0000000..d9c21bc --- /dev/null +++ b/include/graphics/stb_image.h @@ -0,0 +1,7462 @@ +/* stb_image - v2.19 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8/16-bit-per-channel + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + Arseny Kapoulkine + John-Mark Allen + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan + Dave Moore Roy Eltham Hayaki Saito Nathan Reed + Won Chun Luke Graham Johan Duparc Nick Verigakis + the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar + Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex + Ryamond Barbiero Paul Du Bois Engin Manap github:grim210 + Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw + Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus + Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo + Christian Floisand Kevin Schmidt github:darealshinji + Blazej Dariusz Roszkowski github:Michaelangel007 +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *channels_in_file will be unchanged. The function +// stbi_failure_reason() can be queried for an extremely brief, end-user +// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS +// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy to use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// make more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small source code footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB, even though +// they are internally encoded differently. You can disable this conversion +// by by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through (which +// is BGR stored in RGB). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// + + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp, pow +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +// assume GCC or Clang on ARM targets +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +// this is not threadsafe +static const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load = flag_true_if_should_flip; +} + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 8) { + STBI_ASSERT(ri.bits_per_channel == 16); + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 16) { + STBI_ASSERT(ri.bits_per_channel == 8); + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) || !defined(STBI_NO_LINEAR) +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s,f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +static void stbi__skip(stbi__context *s, int n) +{ + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} + +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + return z + (stbi__get16le(s) << 16); +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + k = stbi_lrot(j->code_buffer, n); + STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & ~sgn); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static const stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc << j->succ_low); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) << shift); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) << shift); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) * 4096) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4]; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[288]; + stbi__uint16 value[288]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + STBI_ASSERT(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) stbi__fill_bits(a); + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = old_limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 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,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 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,0,0 }; + +static const int stbi__zdist_base[32] = { 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,0,0}; + +static const int stbi__zdist_extra[32] = +{ 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}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) + c = stbi__zreceive(a,3)+3; + else { + STBI_ASSERT(c == 18); + c = stbi__zreceive(a,7)+11; + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + STBI_ASSERT(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[288] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + STBI_ASSERT(img_width_bytes <= x); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load = 0; +static int stbi__de_iphone_flag = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth < 8) + ri->bits_per_channel = 8; + else + ri->bits_per_channel = p->depth; + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. +static int stbi__shiftsigned(int v, int shift, int bits) +{ + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v >= 0 && v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; +} stbi__bmp_data; + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - 14 - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - 14 - info.hsz) >> 2; + } + + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - 14 - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + int idx; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; + + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + g->background = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + g->history = (stbi_uc *) stbi__malloc(g->w * g->h); + if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "tranparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to teh color that was there the previous frame. + memset( g->out, 0x00, 4 * g->w * g->h ); + memset( g->background, 0x00, 4 * g->w * g->h ); // state of the background (starts transparent) + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispoase of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); + } + + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (o == NULL) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + out = (stbi_uc*) STBI_REALLOC( out, layers * stride ); + if (delays) { + *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + stbi__rewind( s ); + if (p == NULL) + return 0; + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) *comp = info.ma ? 4 : 3; + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + (void) stbi__get32be(s); + (void) stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) +// Does not support 16-bit-per-channel + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + return 0; + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + + if (maxv > 255) + return stbi__err("max value > 255", "PPM image not 8-bit"); + else + return 1; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + return 0; +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +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. +------------------------------------------------------------------------------ +*/ diff --git a/source/graphics/libnsbmp.c b/source/graphics/libnsbmp.c new file mode 100644 index 0000000..173581c --- /dev/null +++ b/source/graphics/libnsbmp.c @@ -0,0 +1,1371 @@ +/* + * Copyright 2006 Richard Wilson + * Copyright 2008 Sean Fox + * + * This file is part of NetSurf's libnsbmp, http://www.netsurf-browser.org/ + * Licenced under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + */ + +/** + * \file + * BMP decoding implementation + * + * This library decode windows bitmaps and icons from their disc images. + * + * The image format is described in several documents: + * https://msdn.microsoft.com/en-us/library/dd183391(v=vs.85).aspx + * http://www.fileformat.info/format/bmp/egff.htm + * https://en.wikipedia.org/wiki/BMP_file_format + * + * Despite the format being clearly defined many bitmaps found on the web are + * not compliant and this implementation attempts to cope with as many issues + * as possible rather than simply failing. + */ + +#include +#include +#include +#include +#include +#include + +#include + +/* squashes unused variable compiler warnings */ +#define UNUSED(x) ((x)=(x)) + +/* BMP entry sizes */ +#define BMP_FILE_HEADER_SIZE 14 +#define ICO_FILE_HEADER_SIZE 6 +#define ICO_DIR_ENTRY_SIZE 16 + +/* the bitmap information header types (encoded as lengths) */ +#define BITMAPCOREHEADER 12 + +#ifdef WE_NEED_INT8_READING_NOW +static inline int8_t read_int8(uint8_t *data, unsigned int o) { + return (int8_t) data[o]; +} +#endif + +static inline uint8_t read_uint8(uint8_t *data, unsigned int o) { + return (uint8_t) data[o]; +} + +static inline int16_t read_int16(uint8_t *data, unsigned int o) { + return (int16_t) (data[o] | (data[o+1] << 8)); +} + +static inline uint16_t read_uint16(uint8_t *data, unsigned int o) { + return (uint16_t) (data[o] | (data[o+1] << 8)); +} + +static inline int32_t read_int32(uint8_t *data, unsigned int o) { + return (int32_t) (data[o] | (data[o+1] << 8) | (data[o+2] << 16) | (data[o+3] << 24)); +} + +static inline uint32_t read_uint32(uint8_t *data, unsigned int o) { + return (uint32_t) (data[o] | (data[o+1] << 8) | (data[o+2] << 16) | (data[o+3] << 24)); +} + + +/** + * Parse the bitmap info header + */ +static bmp_result bmp_info_header_parse(bmp_image *bmp, uint8_t *data) +{ + uint32_t header_size; + uint32_t i; + uint8_t j; + int32_t width, height; + uint8_t palette_size; + unsigned int flags = 0; + + /* must be at least enough data for a core header */ + if (bmp->buffer_size < (BMP_FILE_HEADER_SIZE + BITMAPCOREHEADER)) { + return BMP_INSUFFICIENT_DATA; + } + + header_size = read_uint32(data, 0); + + /* ensure there is enough data for the declared header size*/ + if ((bmp->buffer_size - BMP_FILE_HEADER_SIZE) < header_size) { + return BMP_INSUFFICIENT_DATA; + } + + /* a variety of different bitmap headers can follow, depending + * on the BMP variant. The header length field determines the type. + */ + if (header_size == BITMAPCOREHEADER) { + /* the following header is for os/2 and windows 2.x and consists of: + * + * +0 UINT32 size of this header (in bytes) + * +4 INT16 image width (in pixels) + * +6 INT16 image height (in pixels) + * +8 UINT16 number of colour planes (always 1) + * +10 UINT16 number of bits per pixel + */ + width = read_int16(data, 4); + height = read_int16(data, 6); + if ((width <= 0) || (height == 0)) + return BMP_DATA_ERROR; + if (height < 0) { + bmp->reversed = true; + height = -height; + } + /* ICOs only support 256*256 resolutions + * In the case of the ICO header, the height is actually the added + * height of XOR-Bitmap and AND-Bitmap (double the visible height) + * Technically we could remove this check and ICOs with bitmaps + * of any size could be processed; this is to conform to the spec. + */ + if (bmp->ico) { + if ((width > 256) || (height > 512)) { + return BMP_DATA_ERROR; + } else { + bmp->width = width; + bmp->height = height / 2; + } + } else { + bmp->width = width; + bmp->height = height; + } + if (read_uint16(data, 8) != 1) + return BMP_DATA_ERROR; + bmp->bpp = read_uint16(data, 10); + /** + * The bpp value should be in the range 1-32, but the only + * values considered legal are: + * RGB ENCODING: 1, 4, 8, 16, 24 and 32 + */ + if ((bmp->bpp != 1) && (bmp->bpp != 4) && + (bmp->bpp != 8) && + (bmp->bpp != 16) && + (bmp->bpp != 24) && + (bmp->bpp != 32)) + return BMP_DATA_ERROR; + bmp->colours = (1 << bmp->bpp); + palette_size = 3; + } else if (header_size < 40) { + return BMP_DATA_ERROR; + } else { + /* the following header is for windows 3.x and onwards. it is a + * minimum of 40 bytes and (as of Windows 95) a maximum of 108 bytes. + * + * +0 UINT32 size of this header (in bytes) + * +4 INT32 image width (in pixels) + * +8 INT32 image height (in pixels) + * +12 UINT16 number of colour planes (always 1) + * +14 UINT16 number of bits per pixel + * +16 UINT32 compression methods used + * +20 UINT32 size of bitmap (in bytes) + * +24 UINT32 horizontal resolution (in pixels per meter) + * +28 UINT32 vertical resolution (in pixels per meter) + * +32 UINT32 number of colours in the image + * +36 UINT32 number of important colours + * +40 UINT32 mask identifying bits of red component + * +44 UINT32 mask identifying bits of green component + * +48 UINT32 mask identifying bits of blue component + * +52 UINT32 mask identifying bits of alpha component + * +56 UINT32 color space type + * +60 UINT32 x coordinate of red endpoint + * +64 UINT32 y coordinate of red endpoint + * +68 UINT32 z coordinate of red endpoint + * +72 UINT32 x coordinate of green endpoint + * +76 UINT32 y coordinate of green endpoint + * +80 UINT32 z coordinate of green endpoint + * +84 UINT32 x coordinate of blue endpoint + * +88 UINT32 y coordinate of blue endpoint + * +92 UINT32 z coordinate of blue endpoint + * +96 UINT32 gamma red coordinate scale value + * +100 UINT32 gamma green coordinate scale value + * +104 UINT32 gamma blue coordinate scale value + */ + width = read_int32(data, 4); + height = read_int32(data, 8); + if ((width <= 0) || (height == 0)) + return BMP_DATA_ERROR; + if (height < 0) { + bmp->reversed = true; + if (height <= -INT32_MAX) { + height = INT32_MAX; + } else { + height = -height; + } + } + /* ICOs only support 256*256 resolutions + * In the case of the ICO header, the height is actually the added + * height of XOR-Bitmap and AND-Bitmap (double the visible height) + * Technically we could remove this check and ICOs with bitmaps + * of any size could be processed; this is to conform to the spec. + */ + if (bmp->ico) { + if ((width > 256) || (height > 512)) { + return BMP_DATA_ERROR; + } else { + bmp->width = width; + bmp->height = height / 2; + } + } else { + bmp->width = width; + bmp->height = height; + } + if (read_uint16(data, 12) != 1) + return BMP_DATA_ERROR; + bmp->bpp = read_uint16(data, 14); + if (bmp->bpp == 0) + bmp->bpp = 8; + bmp->encoding = read_uint32(data, 16); + /** + * The bpp value should be in the range 1-32, but the only + * values considered legal are: + * RGB ENCODING: 1, 4, 8, 16, 24 and 32 + * RLE4 ENCODING: 4 + * RLE8 ENCODING: 8 + * BITFIELD ENCODING: 16 and 32 + */ + switch (bmp->encoding) { + case BMP_ENCODING_RGB: + if ((bmp->bpp != 1) && (bmp->bpp != 4) && + (bmp->bpp != 8) && + (bmp->bpp != 16) && + (bmp->bpp != 24) && + (bmp->bpp != 32)) + return BMP_DATA_ERROR; + break; + case BMP_ENCODING_RLE8: + if (bmp->bpp != 8) + return BMP_DATA_ERROR; + break; + case BMP_ENCODING_RLE4: + if (bmp->bpp != 4) + return BMP_DATA_ERROR; + break; + case BMP_ENCODING_BITFIELDS: + if ((bmp->bpp != 16) && (bmp->bpp != 32)) + return BMP_DATA_ERROR; + break; + /* invalid encoding */ + default: + return BMP_DATA_ERROR; + break; + } + /* Bitfield encoding means we have red, green, blue, and alpha masks. + * Here we acquire the masks and determine the required bit shift to + * align them in our 24-bit color 8-bit alpha format. + */ + if (bmp->encoding == BMP_ENCODING_BITFIELDS) { + if (header_size == 40) { + header_size += 12; + if (bmp->buffer_size < (14 + header_size)) + return BMP_INSUFFICIENT_DATA; + for (i = 0; i < 3; i++) + bmp->mask[i] = read_uint32(data, 40 + (i << 2)); + } else { + for (i = 0; i < 4; i++) + bmp->mask[i] = read_uint32(data, 40 + (i << 2)); + } + for (i = 0; i < 4; i++) { + if (bmp->mask[i] == 0) + break; + for (j = 31; j > 0; j--) + if (bmp->mask[i] & (1 << j)) { + if ((j - 7) > 0) + bmp->mask[i] &= 0xff << (j - 7); + else + bmp->mask[i] &= 0xff >> (-(j - 7)); + bmp->shift[i] = (i << 3) - (j - 7); + break; + } + } + } + bmp->colours = read_uint32(data, 32); + if (bmp->colours == 0) + bmp->colours = (1 << bmp->bpp); + palette_size = 4; + } + data += header_size; + + /* if there's no alpha mask, flag the bmp opaque */ + if ((!bmp->ico) && (bmp->mask[3] == 0)) { + flags |= BMP_OPAQUE; + bmp->opaque = true; + } + + /* we only have a palette for <16bpp */ + if (bmp->bpp < 16) { + /* we now have a series of palette entries of the format: + * + * +0 BYTE blue + * +1 BYTE green + * +2 BYTE red + * + * if the palette is from an OS/2 or Win2.x file then the entries + * are padded with an extra byte. + */ + + /* boundary checking */ + if (bmp->buffer_size < (14 + header_size + ((uint64_t)4 * bmp->colours))) + return BMP_INSUFFICIENT_DATA; + + /* create the colour table */ + bmp->colour_table = (uint32_t *)malloc(bmp->colours * 4); + if (!bmp->colour_table) + return BMP_INSUFFICIENT_MEMORY; + for (i = 0; i < bmp->colours; i++) { + bmp->colour_table[i] = data[2] | (data[1] << 8) | (data[0] << 16); + if (bmp->opaque) + bmp->colour_table[i] |= (0xff << 24); + data += palette_size; + bmp->colour_table[i] = read_uint32((uint8_t *)&bmp->colour_table[i],0); + } + + /* some bitmaps have a bad offset if there is a pallete, work + * round this by fixing up the data offset to after the palette + * but only if there is data following the palette as some + * bitmaps encode data in the palette! + */ + if ((bmp->bitmap_offset < (uint32_t)(data - bmp->bmp_data)) && + ((bmp->buffer_size - (data - bmp->bmp_data)) > 0)) { + bmp->bitmap_offset = data - bmp->bmp_data; + } + } + + /* create our bitmap */ + flags |= BMP_NEW | BMP_CLEAR_MEMORY; + bmp->bitmap = bmp->bitmap_callbacks.bitmap_create(bmp->width, bmp->height, flags); + if (!bmp->bitmap) { + if (bmp->colour_table) + free(bmp->colour_table); + bmp->colour_table = NULL; + return BMP_INSUFFICIENT_MEMORY; + } + /* BMPs within ICOs don't have BMP file headers, so the image data should + * always be right after the colour table. + */ + if (bmp->ico) + bmp->bitmap_offset = (intptr_t)data - (intptr_t)bmp->bmp_data; + return BMP_OK; +} + + +/** + * Parse the bitmap file header + * + * \param bmp The bitmap. + * \param data The data for the file header + * \return BMP_OK on success or error code on faliure + */ +static bmp_result bmp_file_header_parse(bmp_image *bmp, uint8_t *data) +{ + /* standard 14-byte BMP file header is: + * + * +0 UINT16 File Type ('BM') + * +2 UINT32 Size of File (in bytes) + * +6 INT16 Reserved Field (1) + * +8 INT16 Reserved Field (2) + * +10 UINT32 Starting Position of Image Data (offset in bytes) + */ + if (bmp->buffer_size < BMP_FILE_HEADER_SIZE) + return BMP_INSUFFICIENT_DATA; + + if ((data[0] != (uint8_t)'B') || (data[1] != (uint8_t)'M')) + return BMP_DATA_ERROR; + + bmp->bitmap_offset = read_uint32(data, 10); + + /* check the offset to data lies within the file */ + if (bmp->bitmap_offset >= bmp->buffer_size) { + return BMP_INSUFFICIENT_DATA; + } + + return BMP_OK; +} + + +/** + * Allocates memory for the next BMP in an ICO collection + * + * Sets proper structure values + * + * \param ico the ICO collection to add the image to + * \param image a pointer to the ICO image to be initialised + */ +static bmp_result next_ico_image(ico_collection *ico, ico_image *image) { + bmp_create(&image->bmp, &ico->bitmap_callbacks); + image->next = ico->first; + ico->first = image; + return BMP_OK; +} + + +/** + * Parse the icon file header + * + * \param ico The icon collection. + * \param data The header data to parse. + * \return BMP_OK on successful parse else error code + */ +static bmp_result ico_header_parse(ico_collection *ico, uint8_t *data) +{ + uint16_t count, i; + bmp_result result; + int area, max_area = 0; + + /* 6-byte ICO file header is: + * + * +0 INT16 Reserved (should be 0) + * +2 UINT16 Type (1 for ICO, 2 for CUR) + * +4 UINT16 Number of BMPs to follow + */ + if (ico->buffer_size < ICO_FILE_HEADER_SIZE) + return BMP_INSUFFICIENT_DATA; + // if (read_int16(data, 2) != 0x0000) + // return BMP_DATA_ERROR; + if (read_uint16(data, 2) != 0x0001) + return BMP_DATA_ERROR; + count = read_uint16(data, 4); + if (count == 0) + return BMP_DATA_ERROR; + data += ICO_FILE_HEADER_SIZE; + + /* check if we have enough data for the directory */ + if (ico->buffer_size < (uint32_t)(ICO_FILE_HEADER_SIZE + (ICO_DIR_ENTRY_SIZE * count))) + return BMP_INSUFFICIENT_DATA; + + /* Decode the BMP files. + * + * 16-byte ICO directory entry is: + * + * +0 UINT8 Width (0 for 256 pixels) + * +1 UINT8 Height (0 for 256 pixels) + * +2 UINT8 Colour count (0 if more than 256 colours) + * +3 INT8 Reserved (should be 0, but may not be) + * +4 UINT16 Colour Planes (should be 0 or 1) + * +6 UINT16 Bits Per Pixel + * +8 UINT32 Size of BMP info header + bitmap data in bytes + * +12 UINT32 Offset (points to the BMP info header, not the bitmap data) + */ + for (i = 0; i < count; i++) { + ico_image *image; + image = calloc(1, sizeof(ico_image)); + if (!image) + return BMP_INSUFFICIENT_MEMORY; + result = next_ico_image(ico, image); + if (result != BMP_OK) + return result; + image->bmp.width = read_uint8(data, 0); + if (image->bmp.width == 0) + image->bmp.width = 256; + image->bmp.height = read_uint8(data, 1); + if (image->bmp.height == 0) + image->bmp.height = 256; + image->bmp.buffer_size = read_uint32(data, 8); + image->bmp.bmp_data = ico->ico_data + read_uint32(data, 12); + image->bmp.ico = true; + data += ICO_DIR_ENTRY_SIZE; + + /* Ensure that the bitmap data resides in the buffer */ + if (image->bmp.bmp_data - ico->ico_data >= 0 && + (uint32_t)(image->bmp.bmp_data - + ico->ico_data) >= ico->buffer_size) + return BMP_DATA_ERROR; + + /* Ensure that we have sufficient data to read the bitmap */ + if (image->bmp.buffer_size - ICO_DIR_ENTRY_SIZE >= + ico->buffer_size - (ico->ico_data - data)) + return BMP_INSUFFICIENT_DATA; + + result = bmp_info_header_parse(&image->bmp, + image->bmp.bmp_data); + if (result != BMP_OK) + return result; + + /* adjust the size based on the images available */ + area = image->bmp.width * image->bmp.height; + if (area > max_area) { + ico->width = image->bmp.width; + ico->height = image->bmp.height; + max_area = area; + } + } + return BMP_OK; +} + + +/** + * Decode BMP data stored in 32bpp colour. + * + * \param bmp the BMP image to decode + * \param start the data to decode, updated to last byte read on success + * \param bytes the number of bytes of data available + * \return BMP_OK on success + * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; + * in this case, the image may be partially viewable + */ +static bmp_result bmp_decode_rgb32(bmp_image *bmp, uint8_t **start, int bytes) +{ + uint8_t *top, *bottom, *end, *data; + uint32_t *scanline; + uint32_t x, y; + uint32_t swidth; + uint8_t i; + uint32_t word; + + assert(bmp->bpp == 32); + + data = *start; + swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; + top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); + if (!top) + return BMP_INSUFFICIENT_MEMORY; + bottom = top + (uint64_t)swidth * (bmp->height - 1); + end = data + bytes; + bmp->decoded = true; + + /* Determine transparent index */ + if (bmp->limited_trans) { + if ((data + 4) > end) + return BMP_INSUFFICIENT_DATA; + if (bmp->encoding == BMP_ENCODING_BITFIELDS) + bmp->transparent_index = read_uint32(data, 0); + else + bmp->transparent_index = data[2] | (data[1] << 8) | (data[0] << 16); + } + + for (y = 0; y < bmp->height; y++) { + if ((data + (4 * bmp->width)) > end) + return BMP_INSUFFICIENT_DATA; + if (bmp->reversed) + scanline = (void *)(top + (y * swidth)); + else + scanline = (void *)(bottom - (y * swidth)); + if (bmp->encoding == BMP_ENCODING_BITFIELDS) { + for (x = 0; x < bmp->width; x++) { + word = read_uint32(data, 0); + for (i = 0; i < 4; i++) + if (bmp->shift[i] > 0) + scanline[x] |= ((word & bmp->mask[i]) << bmp->shift[i]); + else + scanline[x] |= ((word & bmp->mask[i]) >> (-bmp->shift[i])); + /* 32-bit BMPs have alpha masks, but sometimes they're not utilized */ + if (bmp->opaque) + scanline[x] |= (0xff << 24); + data += 4; + scanline[x] = read_uint32((uint8_t *)&scanline[x],0); + } + } else { + for (x = 0; x < bmp->width; x++) { + scanline[x] = data[2] | (data[1] << 8) | (data[0] << 16); + if ((bmp->limited_trans) && (scanline[x] == bmp->transparent_index)) { + scanline[x] = bmp->trans_colour; + } + if (bmp->opaque) { + scanline[x] |= (0xff << 24); + } else { + scanline[x] |= data[3] << 24; + } + data += 4; + scanline[x] = read_uint32((uint8_t *)&scanline[x],0); + } + } + } + *start = data; + return BMP_OK; +} + + +/** + * Decode BMP data stored in 24bpp colour. + * + * \param bmp the BMP image to decode + * \param start the data to decode, updated to last byte read on success + * \param bytes the number of bytes of data available + * \return BMP_OK on success + * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; + * in this case, the image may be partially viewable + */ +static bmp_result bmp_decode_rgb24(bmp_image *bmp, uint8_t **start, int bytes) +{ + uint8_t *top, *bottom, *end, *data; + uint32_t *scanline; + uint32_t x, y; + uint32_t swidth; + intptr_t addr; + + assert(bmp->encoding == BMP_ENCODING_RGB); + assert(bmp->bpp == 24); + + data = *start; + swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; + top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); + if (!top) { + return BMP_INSUFFICIENT_MEMORY; + } + + bottom = top + (uint64_t)swidth * (bmp->height - 1); + end = data + bytes; + addr = ((intptr_t)data) & 3; + bmp->decoded = true; + + /* Determine transparent index */ + if (bmp->limited_trans) { + if ((data + 3) > end) { + return BMP_INSUFFICIENT_DATA; + } + + bmp->transparent_index = data[2] | (data[1] << 8) | (data[0] << 16); + } + + for (y = 0; y < bmp->height; y++) { + if ((data + (3 * bmp->width)) > end) { + return BMP_INSUFFICIENT_DATA; + } + + if (bmp->reversed) { + scanline = (void *)(top + (y * swidth)); + } else { + scanline = (void *)(bottom - (y * swidth)); + } + + for (x = 0; x < bmp->width; x++) { + scanline[x] = data[2] | (data[1] << 8) | (data[0] << 16); + if ((bmp->limited_trans) && (scanline[x] == bmp->transparent_index)) { + scanline[x] = bmp->trans_colour; + } else { + scanline[x] |= (0xff << 24); + } + data += 3; + scanline[x] = read_uint32((uint8_t *)&scanline[x],0); + } + + while (addr != (((intptr_t)data) & 3)) { + data++; + } + } + *start = data; + return BMP_OK; +} + + +/** + * Decode BMP data stored in 16bpp colour. + * + * \param bmp the BMP image to decode + * \param start the data to decode, updated to last byte read on success + * \param bytes the number of bytes of data available + * \return BMP_OK on success + * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; + * in this case, the image may be partially viewable + */ +static bmp_result bmp_decode_rgb16(bmp_image *bmp, uint8_t **start, int bytes) +{ + uint8_t *top, *bottom, *end, *data; + uint32_t *scanline; + uint32_t x, y, swidth; + intptr_t addr; + uint8_t i; + uint16_t word; + + data = *start; + swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; + top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); + if (!top) + return BMP_INSUFFICIENT_MEMORY; + bottom = top + (uint64_t)swidth * (bmp->height - 1); + end = data + bytes; + addr = ((intptr_t)data) & 3; + bmp->decoded = true; + + /* Determine transparent index */ + if (bmp->limited_trans) { + if ((data + 2) > end) + return BMP_INSUFFICIENT_DATA; + bmp->transparent_index = read_uint16(data, 0); + } + + for (y = 0; y < bmp->height; y++) { + if ((data + (2 * bmp->width)) > end) + return BMP_INSUFFICIENT_DATA; + if (bmp->reversed) + scanline = (void *)(top + (y * swidth)); + else + scanline = (void *)(bottom - (y * swidth)); + if (bmp->encoding == BMP_ENCODING_BITFIELDS) { + for (x = 0; x < bmp->width; x++) { + word = read_uint16(data, 0); + if ((bmp->limited_trans) && (word == bmp->transparent_index)) + scanline[x] = bmp->trans_colour; + else { + scanline[x] = 0; + for (i = 0; i < 4; i++) + if (bmp->shift[i] > 0) + scanline[x] |= ((word & bmp->mask[i]) << bmp->shift[i]); + else + scanline[x] |= ((word & bmp->mask[i]) >> (-bmp->shift[i])); + if (bmp->opaque) + scanline[x] |= (0xff << 24); + } + data += 2; + scanline[x] = read_uint32((uint8_t *)&scanline[x],0); + } + } else { + for (x = 0; x < bmp->width; x++) { + word = read_uint16(data, 0); + if ((bmp->limited_trans) && (word == bmp->transparent_index)) + scanline[x] = bmp->trans_colour; + else { + /* 16-bit RGB defaults to RGB555 */ + scanline[x] = ((word & (31 << 0)) << 19) | + ((word & (31 << 5)) << 6) | + ((word & (31 << 10)) >> 7); + } + if (bmp->opaque) + scanline[x] |= (0xff << 24); + data += 2; + scanline[x] = read_uint32((uint8_t *)&scanline[x],0); + } + } + while (addr != (((intptr_t)data) & 3)) + data += 2; + } + *start = data; + return BMP_OK; +} + + +/** + * Decode BMP data stored with a palette and in 8bpp colour or less. + * + * \param bmp the BMP image to decode + * \param start the data to decode, updated to last byte read on success + * \param bytes the number of bytes of data available + * \return BMP_OK on success + * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; + * in this case, the image may be partially viewable + */ +static bmp_result bmp_decode_rgb(bmp_image *bmp, uint8_t **start, int bytes) +{ + uint8_t *top, *bottom, *end, *data; + uint32_t *scanline; + intptr_t addr; + uint32_t x, y, swidth; + uint8_t bit_shifts[8]; + uint8_t ppb = 8 / bmp->bpp; + uint8_t bit_mask = (1 << bmp->bpp) - 1; + uint8_t cur_byte = 0, bit, i; + + for (i = 0; i < ppb; i++) + bit_shifts[i] = 8 - ((i + 1) * bmp->bpp); + + data = *start; + swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; + top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); + if (!top) + return BMP_INSUFFICIENT_MEMORY; + bottom = top + (uint64_t)swidth * (bmp->height - 1); + end = data + bytes; + addr = ((intptr_t)data) & 3; + bmp->decoded = true; + + /* Determine transparent index */ + if (bmp->limited_trans) { + uint32_t idx = (*data >> bit_shifts[0]) & bit_mask; + if (idx >= bmp->colours) + return BMP_DATA_ERROR; + bmp->transparent_index = bmp->colour_table[idx]; + } + + for (y = 0; y < bmp->height; y++) { + bit = 8; + if ((data + (bmp->width / ppb)) > end) + return BMP_INSUFFICIENT_DATA; + if (bmp->reversed) + scanline = (void *)(top + (y * swidth)); + else + scanline = (void *)(bottom - (y * swidth)); + for (x = 0; x < bmp->width; x++) { + uint32_t idx; + if (bit >= ppb) { + bit = 0; + cur_byte = *data++; + } + idx = (cur_byte >> bit_shifts[bit++]) & bit_mask; + if (idx < bmp->colours) { + /* ensure colour table index is in bounds */ + scanline[x] = bmp->colour_table[idx]; + if ((bmp->limited_trans) && + (scanline[x] == bmp->transparent_index)) { + scanline[x] = bmp->trans_colour; + } + } + } + while (addr != (((intptr_t)data) & 3)) + data++; + } + *start = data; + return BMP_OK; +} + + +/** + * Decode a 1bpp mask for an ICO + * + * \param bmp the BMP image to decode + * \param data the data to decode + * \param bytes the number of bytes of data available + * \return BMP_OK on success + */ +static bmp_result bmp_decode_mask(bmp_image *bmp, uint8_t *data, int bytes) +{ + uint8_t *top, *bottom, *end; + uint32_t *scanline; + intptr_t addr; + uint32_t x, y, swidth; + uint32_t cur_byte = 0; + + swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; + top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); + if (!top) + return BMP_INSUFFICIENT_MEMORY; + bottom = top + (uint64_t)swidth * (bmp->height - 1); + end = data + bytes; + + addr = ((intptr_t)data) & 3; + + for (y = 0; y < bmp->height; y++) { + if ((data + (bmp->width >> 3)) > end) + return BMP_INSUFFICIENT_DATA; + scanline = (void *)(bottom - (y * swidth)); + for (x = 0; x < bmp->width; x++) { + if ((x & 7) == 0) + cur_byte = *data++; + scanline[x] = read_uint32((uint8_t *)&scanline[x], 0); + if ((cur_byte & 128) == 0) { + scanline[x] |= (0xff << 24); + } else { + scanline[x] &= 0xffffff; + } + scanline[x] = read_uint32((uint8_t *)&scanline[x], 0); + cur_byte = cur_byte << 1; + } + while (addr != (((intptr_t)data) & 3)) + data++; + } + return BMP_OK; +} + + +/** + * Decode BMP data stored encoded in RLE8. + * + * \param bmp the BMP image to decode + * \param data the data to decode + * \param bytes the number of bytes of data available + * \return BMP_OK on success + * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; + * in this case, the image may be partially viewable + */ +static bmp_result +bmp_decode_rle8(bmp_image *bmp, uint8_t *data, int bytes) +{ + uint8_t *top, *bottom, *end; + uint32_t *scanline; + uint32_t swidth; + uint32_t i, length, pixels_left; + uint32_t x = 0, y = 0, last_y = 0; + uint32_t pixel = 0; + + if (bmp->ico) + return BMP_DATA_ERROR; + + swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; + top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); + if (!top) + return BMP_INSUFFICIENT_MEMORY; + bottom = top + (uint64_t)swidth * (bmp->height - 1); + end = data + bytes; + bmp->decoded = true; + + do { + if (data + 2 > end) + return BMP_INSUFFICIENT_DATA; + length = *data++; + if (length == 0) { + length = *data++; + switch (length) { + case 0: + /* 00 - 00 means end of scanline */ + x = 0; + if (last_y == y) { + y++; + if (y >= bmp->height) + return BMP_DATA_ERROR; + } + last_y = y; + break; + + case 1: + /* 00 - 01 means end of RLE data */ + return BMP_OK; + + case 2: + /* 00 - 02 - XX - YY means move cursor */ + if (data + 2 > end) + return BMP_INSUFFICIENT_DATA; + x += *data++; + if (x >= bmp->width) + return BMP_DATA_ERROR; + y += *data++; + if (y >= bmp->height) + return BMP_DATA_ERROR; + break; + + default: + /* 00 - NN means escape NN pixels */ + if (bmp->reversed) { + pixels_left = (bmp->height - y) * bmp->width - x; + scanline = (void *)(top + (y * swidth)); + } else { + pixels_left = (y + 1) * bmp->width - x; + scanline = (void *)(bottom - (y * swidth)); + } + if (length > pixels_left) + length = pixels_left; + if (data + length > end) + return BMP_INSUFFICIENT_DATA; + + /* the following code could be easily optimised + * by simply checking the bounds on entry and + * using some simple copying routines if so + */ + for (i = 0; i < length; i++) { + uint32_t idx = (uint32_t) *data++; + if (x >= bmp->width) { + x = 0; + y++; + if (y >= bmp->height) + return BMP_DATA_ERROR; + if (bmp->reversed) { + scanline += bmp->width; + } else { + scanline -= bmp->width; + } + } + if (idx >= bmp->colours) + return BMP_DATA_ERROR; + scanline[x++] = bmp->colour_table[idx]; + } + + if ((length & 1) && (*data++ != 0x00)) + return BMP_DATA_ERROR; + + break; + } + } else { + uint32_t idx; + + /* NN means perform RLE for NN pixels */ + if (bmp->reversed) { + pixels_left = (bmp->height - y) * bmp->width - x; + scanline = (void *)(top + (y * swidth)); + } else { + pixels_left = (y + 1) * bmp->width - x; + scanline = (void *)(bottom - (y * swidth)); + } + if (length > pixels_left) + length = pixels_left; + + /* boundary checking */ + if (data + 1 > end) + return BMP_INSUFFICIENT_DATA; + + /* the following code could be easily optimised by + * simply checking the bounds on entry and using some + * simply copying routines if so + */ + idx = (uint32_t) *data++; + if (idx >= bmp->colours) + return BMP_DATA_ERROR; + + pixel = bmp->colour_table[idx]; + for (i = 0; i < length; i++) { + if (x >= bmp->width) { + x = 0; + y++; + if (y >= bmp->height) + return BMP_DATA_ERROR; + if (bmp->reversed) { + scanline += bmp->width; + } else { + scanline -= bmp->width; + } + } + scanline[x++] = pixel; + } + } + } while (data < end); + + return BMP_OK; +} + + +/** + * Decode BMP data stored encoded in RLE4. + * + * \param bmp the BMP image to decode + * \param data the data to decode + * \param bytes the number of bytes of data available + * \return BMP_OK on success + * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; + * in this case, the image may be partially viewable + */ +static bmp_result +bmp_decode_rle4(bmp_image *bmp, uint8_t *data, int bytes) +{ + uint8_t *top, *bottom, *end; + uint32_t *scanline; + uint32_t swidth; + uint32_t i, length, pixels_left; + uint32_t x = 0, y = 0, last_y = 0; + uint32_t pixel = 0, pixel2; + + if (bmp->ico) + return BMP_DATA_ERROR; + + swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; + top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); + if (!top) + return BMP_INSUFFICIENT_MEMORY; + bottom = top + (uint64_t)swidth * (bmp->height - 1); + end = data + bytes; + bmp->decoded = true; + + do { + if (data + 2 > end) + return BMP_INSUFFICIENT_DATA; + length = *data++; + if (length == 0) { + length = *data++; + switch (length) { + case 0: + /* 00 - 00 means end of scanline */ + x = 0; + if (last_y == y) { + y++; + if (y >= bmp->height) + return BMP_DATA_ERROR; + } + last_y = y; + break; + + case 1: + /* 00 - 01 means end of RLE data */ + return BMP_OK; + + case 2: + /* 00 - 02 - XX - YY means move cursor */ + if (data + 2 > end) + return BMP_INSUFFICIENT_DATA; + x += *data++; + if (x >= bmp->width) + return BMP_DATA_ERROR; + y += *data++; + if (y >= bmp->height) + return BMP_DATA_ERROR; + break; + + default: + /* 00 - NN means escape NN pixels */ + if (bmp->reversed) { + pixels_left = (bmp->height - y) * bmp->width - x; + scanline = (void *)(top + (y * swidth)); + } else { + pixels_left = (y + 1) * bmp->width - x; + scanline = (void *)(bottom - (y * swidth)); + } + if (length > pixels_left) + length = pixels_left; + if (data + ((length + 1) / 2) > end) + return BMP_INSUFFICIENT_DATA; + + /* the following code could be easily optimised + * by simply checking the bounds on entry and + * using some simple copying routines + */ + + for (i = 0; i < length; i++) { + if (x >= bmp->width) { + x = 0; + y++; + if (y >= bmp->height) + return BMP_DATA_ERROR; + if (bmp->reversed) { + scanline += bmp->width; + } else { + scanline -= bmp->width; + } + + } + if ((i & 1) == 0) { + pixel = *data++; + if ((pixel >> 4) >= bmp->colours) + return BMP_DATA_ERROR; + scanline[x++] = bmp->colour_table + [pixel >> 4]; + } else { + if ((pixel & 0xf) >= bmp->colours) + return BMP_DATA_ERROR; + scanline[x++] = bmp->colour_table + [pixel & 0xf]; + } + } + length = (length + 1) >> 1; + + if ((length & 1) && (*data++ != 0x00)) + return BMP_DATA_ERROR; + + break; + } + } else { + /* NN means perform RLE for NN pixels */ + if (bmp->reversed) { + pixels_left = (bmp->height - y) * bmp->width - x; + scanline = (void *)(top + (y * swidth)); + } else { + pixels_left = (y + 1) * bmp->width - x; + scanline = (void *)(bottom - (y * swidth)); + } + if (length > pixels_left) + length = pixels_left; + + /* boundary checking */ + if (data + 1 > end) + return BMP_INSUFFICIENT_DATA; + + /* the following code could be easily optimised by + * simply checking the bounds on entry and using some + * simple copying routines + */ + + pixel2 = *data++; + if ((pixel2 >> 4) >= bmp->colours || + (pixel2 & 0xf) >= bmp->colours) + return BMP_DATA_ERROR; + pixel = bmp->colour_table[pixel2 >> 4]; + pixel2 = bmp->colour_table[pixel2 & 0xf]; + for (i = 0; i < length; i++) { + if (x >= bmp->width) { + x = 0; + y++; + if (y >= bmp->height) + return BMP_DATA_ERROR; + if (bmp->reversed) { + scanline += bmp->width; + } else { + scanline -= bmp->width; + } + } + if ((i & 1) == 0) + scanline[x++] = pixel; + else + scanline[x++] = pixel2; + } + + } + } while (data < end); + + return BMP_OK; +} + + +/* exported interface documented in libnsbmp.h */ +bmp_result +bmp_create(bmp_image *bmp, + bmp_bitmap_callback_vt *bitmap_callbacks) +{ + memset(bmp, 0, sizeof(bmp_image)); + bmp->bitmap_callbacks = *bitmap_callbacks; + + return BMP_OK; +} + + +/* exported interface documented in libnsbmp.h */ +bmp_result +ico_collection_create(ico_collection *ico, + bmp_bitmap_callback_vt *bitmap_callbacks) +{ + + memset(ico, 0, sizeof(ico_collection)); + ico->bitmap_callbacks = *bitmap_callbacks; + + return BMP_OK; +} + + +/* exported interface documented in libnsbmp.h */ +bmp_result bmp_analyse(bmp_image *bmp, size_t size, uint8_t *data) +{ + bmp_result res; + + /* ensure we aren't already initialised */ + if (bmp->bitmap) { + return BMP_OK; + } + + /* initialize source data values */ + bmp->buffer_size = size; + bmp->bmp_data = data; + + res = bmp_file_header_parse(bmp, data); + if (res == BMP_OK) { + res = bmp_info_header_parse(bmp, data + BMP_FILE_HEADER_SIZE); + } + return res; +} + + +/* exported interface documented in libnsbmp.h */ +bmp_result ico_analyse(ico_collection *ico, size_t size, uint8_t *data) +{ + /* ensure we aren't already initialised */ + if (ico->first) + return BMP_OK; + + /* initialize values */ + ico->buffer_size = size; + ico->ico_data = data; + + return ico_header_parse(ico, data); +} + + +/* exported interface documented in libnsbmp.h */ +bmp_result bmp_decode(bmp_image *bmp) +{ + uint8_t *data; + uint32_t bytes; + bmp_result result = BMP_OK; + + assert(bmp->bitmap); + + data = bmp->bmp_data + bmp->bitmap_offset; + bytes = bmp->buffer_size - bmp->bitmap_offset; + + switch (bmp->encoding) { + case BMP_ENCODING_RGB: + switch (bmp->bpp) { + case 32: + result = bmp_decode_rgb32(bmp, &data, bytes); + break; + + case 24: + result = bmp_decode_rgb24(bmp, &data, bytes); + break; + + case 16: + result = bmp_decode_rgb16(bmp, &data, bytes); + break; + + default: + result = bmp_decode_rgb(bmp, &data, bytes); + break; + } + break; + + case BMP_ENCODING_RLE8: + result = bmp_decode_rle8(bmp, data, bytes); + break; + + case BMP_ENCODING_RLE4: + result = bmp_decode_rle4(bmp, data, bytes); + break; + + case BMP_ENCODING_BITFIELDS: + switch (bmp->bpp) { + case 32: + result = bmp_decode_rgb32(bmp, &data, bytes); + break; + + case 16: + result = bmp_decode_rgb16(bmp, &data, bytes); + break; + + default: + result = BMP_DATA_ERROR; + break; + } + break; + } + + /* icons with less than 32bpp have a 1bpp alpha mask */ + if ((result == BMP_OK) && (bmp->ico) && (bmp->bpp != 32)) { + bytes = (uintptr_t)bmp->bmp_data + bmp->buffer_size - (uintptr_t)data; + result = bmp_decode_mask(bmp, data, bytes); + } + return result; +} + + +/* exported interface documented in libnsbmp.h */ +bmp_result bmp_decode_trans(bmp_image *bmp, uint32_t colour) +{ + bmp->limited_trans = true; + bmp->trans_colour = colour; + return bmp_decode(bmp); +} + + +/* exported interface documented in libnsbmp.h */ +bmp_image *ico_find(ico_collection *ico, uint16_t width, uint16_t height) +{ + bmp_image *bmp = NULL; + ico_image *image; + int x, y, cur, distance = (1 << 24); + + if (width == 0) + width = ico->width; + if (height == 0) + height = ico->height; + for (image = ico->first; image; image = image->next) { + if ((image->bmp.width == width) && (image->bmp.height == height)) + return &image->bmp; + x = image->bmp.width - width; + y = image->bmp.height - height; + cur = (x * x) + (y * y); + if (cur < distance) { + distance = cur; + bmp = &image->bmp; + } + } + return bmp; +} + + +/* exported interface documented in libnsbmp.h */ +void bmp_finalise(bmp_image *bmp) +{ + if (bmp->bitmap) + bmp->bitmap_callbacks.bitmap_destroy(bmp->bitmap); + bmp->bitmap = NULL; + if (bmp->colour_table) + free(bmp->colour_table); + bmp->colour_table = NULL; +} + + +/* exported interface documented in libnsbmp.h */ +void ico_finalise(ico_collection *ico) +{ + ico_image *image; + + for (image = ico->first; image; image = image->next) + bmp_finalise(&image->bmp); + while (ico->first) { + image = ico->first; + ico->first = image->next; + free(image); + } +} diff --git a/source/menus/lodepng.c b/source/graphics/lodepng.c similarity index 97% rename from source/menus/lodepng.c rename to source/graphics/lodepng.c index 98a8376..6568bba 100644 --- a/source/menus/lodepng.c +++ b/source/graphics/lodepng.c @@ -1,5 +1,5 @@ /* -LodePNG version 20180114 +LodePNG version 20180611 Copyright (c) 2005-2018 Lode Vandevenne @@ -39,7 +39,7 @@ Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for #pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/ #endif /*_MSC_VER */ -const char* LODEPNG_VERSION_STRING = "20180114"; +const char* LODEPNG_VERSION_STRING = "20180611"; /* This source file is built up in the following large parts. The code sections @@ -62,11 +62,17 @@ from here.*/ #ifdef LODEPNG_COMPILE_ALLOCATORS static void* lodepng_malloc(size_t size) { +#ifdef LODEPNG_MAX_ALLOC + if(size > LODEPNG_MAX_ALLOC) return 0; +#endif return malloc(size); } static void* lodepng_realloc(void* ptr, size_t new_size) { +#ifdef LODEPNG_MAX_ALLOC + if(new_size > LODEPNG_MAX_ALLOC) return 0; +#endif return realloc(ptr, new_size); } @@ -86,6 +92,8 @@ void lodepng_free(void* ptr); /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ +#define LODEPNG_MAX(a, b) (((a) > (b)) ? (a) : (b)) + /* Often in case of an error a value is assigned to a variable and then it breaks out of a loop (to go to the cleanup phase of a function). This macro does that. @@ -1455,11 +1463,11 @@ static void updateHashChain(Hash* hash, size_t wpos, unsigned hashval, unsigned { hash->val[wpos] = (int)hashval; if(hash->head[hashval] != -1) hash->chain[wpos] = hash->head[hashval]; - hash->head[hashval] = (unsigned)wpos; + hash->head[hashval] = (int)wpos; hash->zeros[wpos] = numzeros; if(hash->headz[numzeros] != -1) hash->chainz[wpos] = hash->headz[numzeros]; - hash->headz[numzeros] = (unsigned)wpos; + hash->headz[numzeros] = (int)wpos; } /* @@ -2090,13 +2098,13 @@ static unsigned deflate(unsigned char** out, size_t* outsize, static unsigned update_adler32(unsigned adler, const unsigned char* data, unsigned len) { - unsigned s1 = adler & 0xffff; - unsigned s2 = (adler >> 16) & 0xffff; + unsigned s1 = adler & 0xffff; + unsigned s2 = (adler >> 16) & 0xffff; while(len > 0) { - /*at least 5550 sums can be done before the sums overflow, saving a lot of module divisions*/ - unsigned amount = len > 5550 ? 5550 : len; + /*at least 5552 sums can be done before the sums overflow, saving a lot of module divisions*/ + unsigned amount = len > 5552 ? 5552 : len; len -= amount; while(amount > 0) { @@ -2621,15 +2629,10 @@ static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColo if(a->key_g != b->key_g) return 0; if(a->key_b != b->key_b) return 0; } - /*if one of the palette sizes is 0, then we consider it to be the same as the - other: it means that e.g. the palette was not given by the user and should be - considered the same as the palette inside the PNG.*/ - if(1/*a->palettesize != 0 && b->palettesize != 0*/) { - if(a->palettesize != b->palettesize) return 0; - for(i = 0; i != a->palettesize * 4; ++i) - { - if(a->palette[i] != b->palette[i]) return 0; - } + if(a->palettesize != b->palettesize) return 0; + for(i = 0; i != a->palettesize * 4; ++i) + { + if(a->palette[i] != b->palette[i]) return 0; } return 1; } @@ -2705,32 +2708,75 @@ unsigned lodepng_can_have_alpha(const LodePNGColorMode* info) || lodepng_has_palette_alpha(info); } -size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color) +size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { - /*will not overflow for any color type if roughly w * h < 268435455*/ - size_t bpp = lodepng_get_bpp(color); - size_t n = w * h; + size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth); + size_t n = (size_t)w * (size_t)h; return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8; } -size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) +size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color) { - /*will not overflow for any color type if roughly w * h < 268435455*/ - size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth); - size_t n = w * h; - return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8; + return lodepng_get_raw_size_lct(w, h, color->colortype, color->bitdepth); } #ifdef LODEPNG_COMPILE_PNG #ifdef LODEPNG_COMPILE_DECODER -/*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer*/ + +/*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer, +and in addition has one extra byte per line: the filter byte. So this gives a larger +result than lodepng_get_raw_size. */ static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, const LodePNGColorMode* color) { - /*will not overflow for any color type if roughly w * h < 268435455*/ size_t bpp = lodepng_get_bpp(color); - size_t line = ((w / 8) * bpp) + ((w & 7) * bpp + 7) / 8; - return h * line; + /* + 1 for the filter byte, and possibly plus padding bits per line */ + size_t line = ((size_t)(w / 8) * bpp) + 1 + ((w & 7) * bpp + 7) / 8; + return (size_t)h * line; +} + +/* Safely check if multiplying two integers will overflow (no undefined +behavior, compiler removing the code, etc...) and output result. */ +static int lodepng_mulofl(size_t a, size_t b, size_t* result) +{ + *result = a * b; /* Unsigned multiplication is well defined and safe in C90 */ + return (a != 0 && *result / a != b); +} + +/* Safely check if adding two integers will overflow (no undefined +behavior, compiler removing the code, etc...) and output result. */ +static int lodepng_addofl(size_t a, size_t b, size_t* result) +{ + *result = a + b; /* Unsigned addition is well defined and safe in C90 */ + return *result < a; +} + +/*Safely checks whether size_t overflow can be caused due to amount of pixels. +This check is overcautious rather than precise. If this check indicates no overflow, +you can safely compute in a size_t (but not an unsigned): +-(size_t)w * (size_t)h * 8 +-amount of bytes in IDAT (including filter, padding and Adam7 bytes) +-amount of bytes in raw color model +Returns 1 if overflow possible, 0 if not. +*/ +static int lodepng_pixel_overflow(unsigned w, unsigned h, + const LodePNGColorMode* pngcolor, const LodePNGColorMode* rawcolor) +{ + size_t bpp = LODEPNG_MAX(lodepng_get_bpp(pngcolor), lodepng_get_bpp(rawcolor)); + size_t numpixels, total; + size_t line; /* bytes per line in worst case */ + + if(lodepng_mulofl((size_t)w, (size_t)h, &numpixels)) return 1; + if(lodepng_mulofl(numpixels, 8, &total)) return 1; /* bit pointer with 8-bit color, or 8 bytes per channel color */ + + /* Bytes per scanline with the expression "(w / 8) * bpp) + ((w & 7) * bpp + 7) / 8" */ + if(lodepng_mulofl((size_t)(w / 8), bpp, &line)) return 1; + if(lodepng_addofl(line, ((w & 7) * bpp + 7) / 8, &line)) return 1; + + if(lodepng_addofl(line, 5, &line)) return 1; /* 5 bytes overhead per line: 1 filterbyte, 4 for Adam7 worst case */ + if(lodepng_mulofl(line, h, &total)) return 1; /* Total bytes in worst case */ + + return 0; /* no overflow */ } #endif /*LODEPNG_COMPILE_DECODER*/ #endif /*LODEPNG_COMPILE_PNG*/ @@ -3462,7 +3508,7 @@ unsigned lodepng_convert(unsigned char* out, const unsigned char* in, { size_t i; ColorTree tree; - size_t numpixels = w * h; + size_t numpixels = (size_t)w * (size_t)h; unsigned error = 0; if(lodepng_color_mode_equal(mode_out, mode_in)) @@ -3476,7 +3522,7 @@ unsigned lodepng_convert(unsigned char* out, const unsigned char* in, { size_t palettesize = mode_out->palettesize; const unsigned char* palette = mode_out->palette; - size_t palsize = 1u << mode_out->bitdepth; + size_t palsize = (size_t)1u << mode_out->bitdepth; /*if the user specified output palette but did not give the values, assume they want the values of the input color type (assuming that one is palette). Note that we never create a new palette ourselves.*/ @@ -3484,6 +3530,15 @@ unsigned lodepng_convert(unsigned char* out, const unsigned char* in, { palettesize = mode_in->palettesize; palette = mode_in->palette; + /*if the input was also palette with same bitdepth, then the color types are also + equal, so copy literally. This to preserve the exact indices that were in the PNG + even in case there are duplicate colors in the palette.*/ + if (mode_in->colortype == LCT_PALETTE && mode_in->bitdepth == mode_out->bitdepth) + { + size_t numbytes = lodepng_get_raw_size(w, h, mode_in); + for(i = 0; i != numbytes; ++i) out[i] = in[i]; + return 0; + } } if(palettesize < palsize) palsize = palettesize; color_tree_init(&tree); @@ -3573,7 +3628,7 @@ unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, unsigned error = 0; size_t i; ColorTree tree; - size_t numpixels = w * h; + size_t numpixels = (size_t)w * (size_t)h; unsigned colored_done = lodepng_is_greyscale_type(mode) ? 1 : 0; unsigned alpha_done = lodepng_can_have_alpha(mode) ? 0 : 1; @@ -3768,14 +3823,16 @@ unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, { LodePNGColorProfile prof; unsigned error = 0; - unsigned i, n, palettebits, palette_ok; + unsigned palettebits, palette_ok; + size_t i, n; + size_t numpixels = (size_t)w * (size_t)h; lodepng_color_profile_init(&prof); error = lodepng_get_color_profile(&prof, image, w, h, mode_in); if(error) return error; mode_out->key_defined = 0; - if(prof.key && w * h <= 16) + if(prof.key && numpixels <= 16) { prof.alpha = 1; /*too few pixels to justify tRNS chunk overhead*/ prof.key = 0; @@ -3784,7 +3841,7 @@ unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, n = prof.numcolors; palettebits = n <= 2 ? 1 : (n <= 4 ? 2 : (n <= 16 ? 4 : 8)); palette_ok = n <= 256 && prof.bits <= 8; - if(w * h < n * 2) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/ + if(numpixels < n * 2) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/ if(!prof.colored && prof.bits <= palettebits) palette_ok = 0; /*grey is less overhead*/ if(palette_ok) @@ -4531,7 +4588,6 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, ucvector idat; /*the data from idat chunks*/ ucvector scanlines; size_t predict; - size_t numpixels; size_t outsize = 0; /*for unknown chunk order*/ @@ -4546,13 +4602,10 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/ if(state->error) return; - numpixels = *w * *h; - - /*multiplication overflow*/ - if(*h != 0 && numpixels / *h != *w) CERROR_RETURN(state->error, 92); - /*multiplication overflow possible further below. Allows up to 2^31-1 pixel - bytes with 16-bit RGBA, the rest is room for filter bytes.*/ - if(numpixels > 268435455) CERROR_RETURN(state->error, 92); + if(lodepng_pixel_overflow(*w, *h, &state->info_png.color, &state->info_raw)) + { + CERROR_RETURN(state->error, 92); /*overflow possible due to amount of pixels*/ + } ucvector_init(&idat); chunk = &in[33]; /*first byte of the first chunk after the header*/ @@ -4591,7 +4644,9 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, if(lodepng_chunk_type_equals(chunk, "IDAT")) { size_t oldsize = idat.size; - if(!ucvector_resize(&idat, oldsize + chunkLength)) CERROR_BREAK(state->error, 83 /*alloc fail*/); + size_t newsize; + if(lodepng_addofl(oldsize, chunkLength, &newsize)) CERROR_BREAK(state->error, 95); + if(!ucvector_resize(&idat, newsize)) CERROR_BREAK(state->error, 83 /*alloc fail*/); for(i = 0; i != chunkLength; ++i) idat.data[oldsize + i] = data[i]; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS critical_pos = 3; @@ -4694,21 +4749,20 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, If the decompressed size does not match the prediction, the image must be corrupt.*/ if(state->info_png.interlace_method == 0) { - /*The extra *h is added because this are the filter bytes every scanline starts with*/ - predict = lodepng_get_raw_size_idat(*w, *h, &state->info_png.color) + *h; + predict = lodepng_get_raw_size_idat(*w, *h, &state->info_png.color); } else { /*Adam-7 interlaced: predicted size is the sum of the 7 sub-images sizes*/ const LodePNGColorMode* color = &state->info_png.color; predict = 0; - predict += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3); - if(*w > 4) predict += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3); - predict += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, color) + ((*h + 3) >> 3); - if(*w > 2) predict += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, color) + ((*h + 3) >> 2); - predict += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, color) + ((*h + 1) >> 2); - if(*w > 1) predict += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, color) + ((*h + 1) >> 1); - predict += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, color) + ((*h + 0) >> 1); + predict += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, color); + if(*w > 4) predict += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, color); + predict += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, color); + if(*w > 2) predict += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, color); + predict += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, color); + if(*w > 1) predict += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, color); + predict += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, color); } if(!state->error && !ucvector_reserve(&scanlines, predict)) state->error = 83; /*alloc fail*/ if(!state->error) @@ -5697,7 +5751,7 @@ unsigned lodepng_encode(unsigned char** out, size_t* outsize, if(!lodepng_color_mode_equal(&state->info_raw, &info.color)) { unsigned char* converted; - size_t size = (w * h * (size_t)lodepng_get_bpp(&info.color) + 7) / 8; + size_t size = ((size_t)w * (size_t)h * (size_t)lodepng_get_bpp(&info.color) + 7) / 8; converted = (unsigned char*)lodepng_malloc(size); if(!converted && size) state->error = 83; /*alloc fail*/ @@ -6010,9 +6064,10 @@ const char* lodepng_error_text(unsigned code) /*the windowsize in the LodePNGCompressSettings. Requiring POT(==> & instead of %) makes encoding 12% faster.*/ case 90: return "windowsize must be a power of two"; case 91: return "invalid decompressed idat size"; - case 92: return "too many pixels, not supported"; + case 92: return "integer overflow due to too many pixels"; case 93: return "zero width or height is invalid"; case 94: return "header chunk must have a size of 13 bytes"; + case 95: return "integer overflow with combined idat chunk size"; } return "unknown error code"; } diff --git a/source/menus/menu_gallery.c b/source/menus/menu_gallery.c index ff94b24..cb6f06b 100644 --- a/source/menus/menu_gallery.c +++ b/source/menus/menu_gallery.c @@ -1,20 +1,28 @@ #include #include <3ds.h> -#include -#include - #include "C2D_helper.h" #include "common.h" #include "config.h" #include "fs.h" #include "menu_gallery.h" + +#include "libnsbmp.h" +#include "lodepng.h" +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" +#undef STB_IMAGE_IMPLEMENTATION + #include "touch.h" #include "utils.h" #define EXTENSION_SIZE 4 -#define PNG_SIG_SIZE 8 +#define DIMENSION_DEFAULT 0 +#define DIMENSION_NINTENDO_SCREENSHOT 1 +#define DIMENSION_NINTENDO_PICTURE 2 +#define DIMENSION_3DSHELL_SCREENSHOT 3 +#define DIMENSION_OTHER 4 static char album[512][512]; static int count = 0, selection = 0; @@ -33,186 +41,210 @@ static u32 Gallery_GetNextPowerOf2(u32 v) // from pp2d return v >= 64 ? v : 64; } +static void *Gallery_CreateBitmap(int width, int height, unsigned int state) +{ + (void) state; /* unused */ + return calloc(width * height, 4); +} + +static unsigned char *Gallery_GetBitmapBuf(void *bitmap) +{ + return (unsigned char *)bitmap; +} + +static size_t Gallery_GetBitmapBPP(void *bitmap) +{ + (void) bitmap; /* unused */ + return 4; +} + +void Gallery_FreeBitmap(void *bitmap) +{ + free(bitmap); +} + +static void *Gallery_BitmapToBuf(const char *path, u32 *size) +{ + FILE *fd = fopen(path, "rb"); + + if (fd == NULL) + return NULL; + + u8 *buffer; + long long_size; + fseek(fd, 0, SEEK_END); + long_size = ftell(fd); + rewind(fd); + + buffer = (u8 *)malloc(long_size); + + if (size) + *size = long_size; + + if (!buffer) + { + fclose(fd); + return NULL; + } + + fread(buffer, 1, long_size, fd); + fclose(fd); + return buffer; +} + // Thanks to LiquidFenrir static C2D_Image *Gallery_LoadImage(const char *path) { u32* outBuf = NULL; + u32 size = 0; int width = 0, height = 0; + GPU_TEXCOLOR format; char extension[EXTENSION_SIZE+1] = {0}; strncpy(extension, &path[strlen(path)-EXTENSION_SIZE], EXTENSION_SIZE); - if (!strncmp(extension, ".png", EXTENSION_SIZE)) + if (!strncasecmp(extension, ".png", EXTENSION_SIZE)) { - FILE* fh = fopen(path, "rb"); - if (!fh) - return NULL; + unsigned char *texture; + lodepng_decode32_file(&texture, &width, &height, path); - u8 sig[PNG_SIG_SIZE] = {0}; - size_t size = fread(sig, sizeof(u8), PNG_SIG_SIZE, fh); - fseek(fh, 0, SEEK_SET); - - if (size < PNG_SIG_SIZE || png_sig_cmp(sig, 0, PNG_SIG_SIZE)) + for (u32 i = 0; i < width; i++) { - fclose(fh); - return NULL; - } - - png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + for (u32 j = 0; j < height; j++) + { + u32 p = (i + j* width) * 4; - png_infop info = png_create_info_struct(png); + u8 r = *(u8*)(texture + p); + u8 g = *(u8*)(texture + p + 1); + u8 b = *(u8*)(texture + p + 2); + u8 a = *(u8*)(texture + p + 3); - if (setjmp(png_jmpbuf(png))) - { - png_destroy_read_struct(&png, &info, NULL); - fclose(fh); - return NULL; + *(texture + p) = a; + *(texture + p + 1) = b; + *(texture + p + 2) = g; + *(texture + p + 3) = r; + } } - png_init_io(png, fh); - png_read_info(png, info); - - width = png_get_image_width(png, info); - height = png_get_image_height(png, info); - - png_byte color_type = png_get_color_type(png, info); - png_byte bit_depth = png_get_bit_depth(png, info); - - // Read any color_type into 8bit depth, ABGR format. - // See http://www.libpng.org/pub/png/libpng-manual.txt - - if (bit_depth == 16) - png_set_strip_16(png); - - if (color_type == PNG_COLOR_TYPE_PALETTE) - png_set_palette_to_rgb(png); - - // PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth. - if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) - png_set_expand_gray_1_2_4_to_8(png); - - if (png_get_valid(png, info, PNG_INFO_tRNS)) - png_set_tRNS_to_alpha(png); - - // These color_type don't have an alpha channel then fill it with 0xff. - if (color_type == PNG_COLOR_TYPE_RGB || - color_type == PNG_COLOR_TYPE_GRAY || - color_type == PNG_COLOR_TYPE_PALETTE) - png_set_filler(png, 0xFF, PNG_FILLER_AFTER); - - if (color_type == PNG_COLOR_TYPE_GRAY || - color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - png_set_gray_to_rgb(png); - - //output ABGR - png_set_bgr(png); - png_set_swap_alpha(png); - - png_read_update_info(png, info); + size = (u32)(width * height * 4); + outBuf = (u32 *)malloc(size); + memcpy(outBuf, texture, size); + format = GPU_RGBA8; + } + else if (!strncasecmp(extension, ".jpg", EXTENSION_SIZE) || !strncasecmp(extension, ".jpeg", EXTENSION_SIZE)) + { + int channel = 0; + stbi_uc *texture = stbi_load(path, &width, &height, &channel, STBI_rgb); - png_bytep* row_pointers = malloc(height*sizeof(png_bytep)); - if (row_pointers == NULL) - { - png_destroy_read_struct(&png, &info, NULL); + if ((texture == NULL) || (channel != STBI_rgb)) return NULL; - } - for(int y = 0; y < height; y++) - row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(png,info)); + for (u32 x = 0; x < width; x++) + { + for (u32 y = 0; y < height; y++) + { - png_read_image(png, row_pointers); + u32 pos = (y * width + x) * channel; - fclose(fh); - png_destroy_read_struct(&png, &info, NULL); + u8 c1 = texture[pos + 0]; + u8 c2 = texture[pos + 1]; + u8 c3 = texture[pos + 2]; - outBuf = malloc(width*height*sizeof(u32)); - for(int j = 0; j < height; j++) - { - png_bytep row = row_pointers[j]; - for(int i = 0; i < width; i++) - { - png_bytep px = &(row[i * 4]); - memcpy(&outBuf[j*width +i], px, sizeof(u32)); + texture[pos + 0] = c3; + texture[pos + 1] = c2; + texture[pos + 2] = c1; } - free(row_pointers[j]); // free the completed row, to avoid having to loop over the whole thing again } - free(row_pointers); + + size = (u32)(width * height * channel); + outBuf = (u32 *)malloc(size); + memcpy(outBuf, texture, size); + stbi_image_free(texture); + format = GPU_RGB8; } - else if (!strncmp(extension, ".jpg", EXTENSION_SIZE) || !strncmp(extension, ".joeg", EXTENSION_SIZE)) + else if (!strncasecmp(extension, ".bmp", EXTENSION_SIZE)) { - FILE* fh = fopen(path, "rb"); - if (fh == NULL) - return NULL; - - fseek(fh, 0, SEEK_END); - long int jpegSize = ftell(fh); - fseek(fh, 0, SEEK_SET); + u32 size; + u8 *buf = (u8 *)Gallery_BitmapToBuf(path, &size); - unsigned char* jpegBuf = (unsigned char*)malloc(jpegSize); - if (jpegBuf == NULL) + bmp_bitmap_callback_vt bitmap_callbacks = { - fclose(fh); - return NULL; - } + Gallery_CreateBitmap, + Gallery_FreeBitmap, + Gallery_GetBitmapBuf, + Gallery_GetBitmapBPP + }; + + bmp_result code; + bmp_image bmp; + + /* create our bmp image */ + bmp_create(&bmp, &bitmap_callbacks); - fread(jpegBuf, jpegSize, sizeof(unsigned char), fh); - fclose(fh); + /* analyse the BMP */ + code = bmp_analyse(&bmp, size, buf); - tjhandle handle = tjInitDecompress(); - if (handle == NULL) + if (code != BMP_OK) { - free(jpegBuf); + bmp_finalise(&bmp); return NULL; } - if (tjDecompressHeader(handle, jpegBuf, jpegSize, &width, &height) == -1) + /* decode the image */ + code = bmp_decode(&bmp); + + if (code != BMP_OK) { - free(jpegBuf); - tjDestroy(handle); + bmp_finalise(&bmp); return NULL; } - outBuf = malloc(width*height*sizeof(u32)); + u8 *texture; + texture = (u8 *)bmp.bitmap; - if (tjDecompress2(handle, jpegBuf, jpegSize, (unsigned char*)outBuf, width, 0, height, TJPF_ABGR, TJFLAG_ACCURATEDCT) == -1) + for (u32 x = 0; x < bmp.width; x++) { - free(outBuf); - free(jpegBuf); - tjDestroy(handle); - return NULL; + for (u32 y = 0; y < bmp.height; y++) + { + u32 pos = (y * bmp.width + x) * 4; + u8 c1 = texture[pos + 0]; + u8 c2 = texture[pos + 1]; + u8 c3 = texture[pos + 2]; + u8 c4 = texture[pos + 3]; + + texture[pos + 0] = c4; + texture[pos + 1] = c3; + texture[pos + 2] = c2; + texture[pos + 3] = c1; + } } - free(jpegBuf); - tjDestroy(handle); + size = (bmp.width * bmp.height * 4); + outBuf = (u32 *)malloc(size); + memcpy(outBuf, texture, size); + bmp_finalise(&bmp); + format = GPU_RGBA8; } else return NULL; if (outBuf) { - C2D_Image* image = malloc(sizeof(C2D_Image)); + C2D_Image *image = malloc(sizeof(C2D_Image)); if (image == NULL) { free(outBuf); return NULL; } - C3D_Tex* tex = malloc(sizeof(C3D_Tex)); - if (tex == NULL) - { - free(image); - free(outBuf); - return NULL; - } - image->tex = tex; + image->tex = malloc(sizeof(C3D_Tex)); u32 w_pow2 = Gallery_GetNextPowerOf2((u32)width); u32 h_pow2 = Gallery_GetNextPowerOf2((u32)height); - Tex3DS_SubTexture* subt3x = malloc(sizeof(Tex3DS_SubTexture)); + Tex3DS_SubTexture *subt3x = malloc(sizeof(Tex3DS_SubTexture)); if (subt3x == NULL) { - free(tex); free(image); free(outBuf); return NULL; @@ -222,25 +254,30 @@ static C2D_Image *Gallery_LoadImage(const char *path) subt3x->left = 0.0f; subt3x->top = 1.0f; subt3x->right = width/(float)w_pow2; - subt3x->bottom = 1.0-(height/(float)h_pow2); + subt3x->bottom = 1.0 - (height/(float)h_pow2); image->subtex = subt3x; - C3D_TexInit(image->tex, w_pow2, h_pow2, GPU_RGBA8); + C3D_TexInit(image->tex, (u16)w_pow2, (u16)h_pow2, format); + C3D_TexSetFilter(image->tex, GPU_LINEAR, GPU_LINEAR); memset(image->tex->data, 0, image->tex->size); - for(int j = 0; j < height; j++) + u32 pixelSize = size / width / height; + + for (u32 x = 0; x < width; x++) { - for(int i = 0; i < width; i++) + for (u32 y = 0; y < height; y++) { - u32 dst = ((((j >> 3) * (w_pow2 >> 3) + (i >> 3)) << 6) + ((i & 1) | ((j & 1) << 1) | ((i & 2) << 1) | ((j & 2) << 2) | ((i & 4) << 2) | ((j & 4) << 3))) * 4; - memcpy(((u8*)(image->tex->data)) + dst, &outBuf[j*width +i], sizeof(u32)); + u32 dstPos = ((((y >> 3) * (w_pow2 >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3))) * pixelSize; + u32 srcPos = (y * width + x) * pixelSize; + + memcpy(&((u8 *)image->tex->data)[dstPos], &((u8 *) outBuf)[srcPos], pixelSize); } } + + C3D_TexFlush(image->tex); free(outBuf); - - C3D_TexSetFilter(image->tex, GPU_LINEAR, GPU_LINEAR); image->tex->border = 0xFFFFFFFF; C3D_TexSetWrap(image->tex, GPU_CLAMP_TO_BORDER, GPU_CLAMP_TO_BORDER); @@ -262,6 +299,18 @@ static void Gallery_FreeImage(C2D_Image *image) } } +static bool Gallery_DrawImage(C2D_Image image, float x, float y, float start, float end) +{ + C2D_DrawParams params = + { + { x, y, 1.0f*image.subtex->width, 1.0f*image.subtex->height }, + { start, end }, + 0.5f, 0.0f + }; + + return C2D_DrawImage(image, ¶ms, NULL); +} + static Result Gallery_GetImageList(void) { Handle dir; @@ -340,6 +389,8 @@ void Gallery_DisplayImage(char *path) Gallery_GetImageList(); selection = Gallery_GetCurrentIndex(path); + int dimensions = 0; + while(aptMainLoop()) { C3D_FrameBegin(C3D_FRAME_SYNCDRAW); @@ -347,7 +398,39 @@ void Gallery_DisplayImage(char *path) C2D_TargetClear(RENDER_BOTTOM, C2D_Color32(33, 39, 43, 255)); C2D_SceneBegin(RENDER_TOP); - Draw_Image(*image, ((400 - (image->subtex->width)) / 2), (240 - (image->subtex->height)) / 2); + if ((image->subtex->width <= 400) && (image->subtex->height <= 240)) + DIMENSION_DEFAULT; + else if ((image->subtex->width == 432) && (image->subtex->height == 528)) // Nintnedo's screenshot (both screens) dimensions. + dimensions = DIMENSION_NINTENDO_SCREENSHOT; + else if ((image->subtex->width == 640) && (image->subtex->height == 480)) // Nintnedo's CAM dimensions. + dimensions = DIMENSION_NINTENDO_PICTURE; + else if ((image->subtex->width == 400) && ((image->subtex->height == 480) || (image->subtex->height == 482))) + dimensions = DIMENSION_3DSHELL_SCREENSHOT; + else if ((image->subtex->width > 400) && (image->subtex->height > 240)) + dimensions = DIMENSION_OTHER; + + switch (dimensions) + { + case DIMENSION_DEFAULT: + Draw_Image(*image, ((400.0f - image->subtex->width) / 2.0f), ((240.0f - image->subtex->height) / 2.0f)); + break; + + case DIMENSION_NINTENDO_SCREENSHOT: + Gallery_DrawImage(*image, 0, 0, 16, 16); + break; + + case DIMENSION_NINTENDO_PICTURE: + Draw_ImageScale(*image, 40, 0, 0.5, 0.5); + break; + + case DIMENSION_3DSHELL_SCREENSHOT: + Gallery_DrawImage(*image, 0, 0, 0, 0); + break; + + case DIMENSION_OTHER: + Draw_ImageScale(*image, 0, 0, 400.0f / image->subtex->width, 240.0f / image->subtex->height); + break; + } hidScanInput(); u32 kDown = hidKeysDown(); @@ -362,22 +445,20 @@ void Gallery_DisplayImage(char *path) wait(1); Gallery_HandleNext(true); } - - /*if (touchInfo.state == TouchEnded && touchInfo.tapType != TapNone) - { - if (tapped_inside(touchInfo, 0, 0, 120, 240)) - { - wait(1); - Gallery_HandleNext(false); - } - else if (tapped_inside(touchInfo, 1160, 0, 400, 240)) - { - wait(1); - Gallery_HandleNext(true); - } - }*/ C2D_SceneBegin(RENDER_BOTTOM); + + switch (dimensions) + { + case DIMENSION_NINTENDO_SCREENSHOT: + Gallery_DrawImage(*image, 0, 0, 56, 272); + break; + + case DIMENSION_3DSHELL_SCREENSHOT: + Gallery_DrawImage(*image, 0, 0, 40, 240); + break; + } + Draw_EndFrame(); if (kDown & KEY_B) From 2924a8b359e06735c486e37fb5a54ca58fc267f5 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Sun, 15 Jul 2018 20:43:05 -0500 Subject: [PATCH 17/33] Add bmp support + add CIA support --- Makefile | 52 +- include/graphics/libnsbmp.h | 250 ------ include/graphics/loadbmp.h | 278 +++++++ res/app.rsf | 195 +++++ res/banner.png | Bin 0 -> 10753 bytes res/banner.wav | Bin 0 -> 362020 bytes res/ic_launcher_filemanager.png | Bin 0 -> 18807 bytes res/logo.lz11 | Bin 0 -> 8192 bytes source/dirbrowse.c | 4 +- source/graphics/libnsbmp.c | 1371 ------------------------------- source/menus/menu_gallery.c | 291 +++---- 11 files changed, 616 insertions(+), 1825 deletions(-) delete mode 100644 include/graphics/libnsbmp.h create mode 100644 include/graphics/loadbmp.h create mode 100644 res/app.rsf create mode 100644 res/banner.png create mode 100644 res/banner.wav create mode 100644 res/ic_launcher_filemanager.png create mode 100644 res/logo.lz11 delete mode 100644 source/graphics/libnsbmp.c diff --git a/Makefile b/Makefile index 139ddbf..124faf8 100644 --- a/Makefile +++ b/Makefile @@ -40,12 +40,23 @@ GRAPHICS := res/drawable ROMFS := romfs GFXBUILD := $(ROMFS)/res/drawable -APP_TITLE := 3DShell -VERSION_MAJOR := 3 -VERSION_MINOR := 0 -VERSION_MICRO := 0 - -GITVERSION := $(shell git log -1 --pretty='%h') +APP_TITLE := 3DShell +APP_DESCRIPTION := Multi-purpose file manager +APP_AUTHOR := Joel16 +VERSION_MAJOR := 4 +VERSION_MINOR := 0 +VERSION_MICRO := 0 +GITVERSION := $(shell git log -1 --pretty='%h') +ICON := res/ic_launcher_filemanager.png + +# CIA +BANNER_AUDIO := res/banner.wav +BANNER_IMAGE := res/banner.png +RSF_PATH := res/app.rsf +LOGO := res/logo.lz11 +UNIQUE_ID := 0x16200 +PRODUCT_CODE := CTR-3D-SHEL +ICON_FLAGS := nosavebackups,visible #--------------------------------------------------------------------------------- # options for code generation @@ -169,9 +180,38 @@ endif .PHONY: all clean +#--------------------------------------------------------------------------------- +MAKEROM ?= makerom +MAKEROM_ARGS := -elf "$(OUTPUT).elf" -rsf "$(RSF_PATH)" -banner "$(BUILD)/banner.bnr" -icon "$(BUILD)/icon.icn" -DAPP_TITLE="$(APP_TITLE)" -DAPP_PRODUCT_CODE="$(PRODUCT_CODE)" -DAPP_UNIQUE_ID="$(UNIQUE_ID)" +MAKEROM_ARGS += -major $(VERSION_MAJOR) -minor $(VERSION_MINOR) -micro $(VERSION_MICRO) + +ifneq ($(strip $(LOGO)),) + MAKEROM_ARGS += -logo "$(LOGO)" +endif +ifneq ($(strip $(ROMFS)),) + MAKEROM_ARGS += -DAPP_ROMFS="$(ROMFS)" +endif + +BANNERTOOL ?= bannertool + +ifeq ($(suffix $(BANNER_IMAGE)),.cgfx) + BANNER_IMAGE_ARG := -ci +else + BANNER_IMAGE_ARG := -i +endif + +ifeq ($(suffix $(BANNER_AUDIO)),.cwav) + BANNER_AUDIO_ARG := -ca +else + BANNER_AUDIO_ARG := -a +endif + #--------------------------------------------------------------------------------- all: $(BUILD) $(GFXBUILD) $(DEPSDIR) $(ROMFS_T3XFILES) $(T3XHFILES) @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + @$(BANNERTOOL) makebanner $(BANNER_IMAGE_ARG) "$(BANNER_IMAGE)" $(BANNER_AUDIO_ARG) "$(BANNER_AUDIO)" -o "$(BUILD)/banner.bnr" + @$(BANNERTOOL) makesmdh -s "$(APP_TITLE)" -l "$(APP_DESCRIPTION)" -p "$(APP_AUTHOR)" -i "$(APP_ICON)" -f "$(ICON_FLAGS)" -o "$(BUILD)/icon.icn" + $(MAKEROM) -f cia -o "$(OUTPUT).cia" -target t -exefslogo $(MAKEROM_ARGS) @echo "${VERSION_MAJOR}${VERSION_MINOR}${VERSION_MICRO}" > UPDATE_MILESTONE.txt # For maintainer builds @echo "${GITVERSION}" > UPDATE_NIGHTLY.txt # For maintainer builds diff --git a/include/graphics/libnsbmp.h b/include/graphics/libnsbmp.h deleted file mode 100644 index 7e90b4a..0000000 --- a/include/graphics/libnsbmp.h +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright 2006 Richard Wilson - * Copyright 2008 Sean Fox - * - * This file is part of NetSurf's libnsbmp, http://www.netsurf-browser.org/ - * Licenced under the MIT License, - * http://www.opensource.org/licenses/mit-license.php - */ - -/** - * \file - * Bitmap file decoding interface. - */ - -#ifndef libnsbmp_h_ -#define libnsbmp_h_ - -#include -#include -#include - -/* bmp flags */ -#define BMP_NEW 0 -/** image is opaque (as opposed to having an alpha mask) */ -#define BMP_OPAQUE (1 << 0) -/** memory should be wiped */ -#define BMP_CLEAR_MEMORY (1 << 1) - -/** - * error return values - */ -typedef enum { - BMP_OK = 0, - BMP_INSUFFICIENT_MEMORY = 1, - BMP_INSUFFICIENT_DATA = 2, - BMP_DATA_ERROR = 3 -} bmp_result; - -/** - * encoding types - */ -typedef enum { - BMP_ENCODING_RGB = 0, - BMP_ENCODING_RLE8 = 1, - BMP_ENCODING_RLE4 = 2, - BMP_ENCODING_BITFIELDS = 3 -} bmp_encoding; - -/* API for Bitmap callbacks */ -typedef void* (*bmp_bitmap_cb_create)(int width, int height, unsigned int state); -typedef void (*bmp_bitmap_cb_destroy)(void *bitmap); -typedef unsigned char* (*bmp_bitmap_cb_get_buffer)(void *bitmap); -typedef size_t (*bmp_bitmap_cb_get_bpp)(void *bitmap); - -/** - * The Bitmap callbacks function table - */ -typedef struct bmp_bitmap_callback_vt_s { - /** Callback to allocate bitmap storage. */ - bmp_bitmap_cb_create bitmap_create; - /** Called to free bitmap storage. */ - bmp_bitmap_cb_destroy bitmap_destroy; - /** Return a pointer to the pixel data in a bitmap. */ - bmp_bitmap_cb_get_buffer bitmap_get_buffer; - /** Find the width of a pixel row in bytes. */ - bmp_bitmap_cb_get_bpp bitmap_get_bpp; -} bmp_bitmap_callback_vt; - -/** - * bitmap image - */ -typedef struct bmp_image { - /** callbacks for bitmap functions */ - bmp_bitmap_callback_vt bitmap_callbacks; - /** pointer to BMP data */ - uint8_t *bmp_data; - /** width of BMP (valid after _analyse) */ - uint32_t width; - /** heigth of BMP (valid after _analyse) */ - uint32_t height; - /** whether the image has been decoded */ - bool decoded; - /** decoded image */ - void *bitmap; - - /* Internal members are listed below */ - /** total number of bytes of BMP data available */ - uint32_t buffer_size; - /** pixel encoding type */ - bmp_encoding encoding; - /** offset of bitmap data */ - uint32_t bitmap_offset; - /** bits per pixel */ - uint16_t bpp; - /** number of colours */ - uint32_t colours; - /** colour table */ - uint32_t *colour_table; - /** whether to use bmp's limited transparency */ - bool limited_trans; - /** colour to display for "transparent" pixels when using limited - * transparency - */ - uint32_t trans_colour; - /** scanlines are top to bottom */ - bool reversed; - /** image is part of an ICO, mask follows */ - bool ico; - /** true if the bitmap does not contain an alpha channel */ - bool opaque; - /** four bitwise mask */ - uint32_t mask[4]; - /** four bitwise shifts */ - int32_t shift[4]; - /** colour representing "transparency" in the bitmap */ - uint32_t transparent_index; -} bmp_image; - -typedef struct ico_image { - bmp_image bmp; - struct ico_image *next; -} ico_image; - -/** - * icon image collection - */ -typedef struct ico_collection { - /** callbacks for bitmap functions */ - bmp_bitmap_callback_vt bitmap_callbacks; - /** width of largest BMP */ - uint16_t width; - /** heigth of largest BMP */ - uint16_t height; - - /* Internal members are listed below */ - /** pointer to ICO data */ - uint8_t *ico_data; - /** total number of bytes of ICO data available */ - uint32_t buffer_size; - /** root of linked list of images */ - ico_image *first; -} ico_collection; - -/** - * Initialises bitmap ready for analysing the bitmap. - * - * \param bmp The Bitmap to initialise - * \param callbacks The callbacks the library will call on operations. - * \return BMP_OK on success or appropriate error code. - */ -bmp_result bmp_create(bmp_image *bmp, bmp_bitmap_callback_vt *callbacks); - -/** - * Initialises icon ready for analysing the icon - * - * \param bmp The Bitmap to initialise - * \param callbacks The callbacks the library will call on operations. - * \return BMP_OK on success or appropriate error code. - */ -bmp_result ico_collection_create(ico_collection *ico, - bmp_bitmap_callback_vt *callbacks); - -/** - * Analyse a BMP prior to decoding. - * - * This will scan the data provided and perform checks to ensure the data is a - * valid BMP and prepare the bitmap image structure ready for decode. - * - * This function must be called and resturn BMP_OK before bmp_decode() as it - * prepares the bmp internal state for the decode process. - * - * \param bmp the BMP image to analyse. - * \param size The size of data in cdata. - * \param data The bitmap source data. - * \return BMP_OK on success or error code on faliure. - */ -bmp_result bmp_analyse(bmp_image *bmp, size_t size, uint8_t *data); - -/** - * Analyse an ICO prior to decoding. - * - * This function will scan the data provided and perform checks to ensure the - * data is a valid ICO. - * - * This function must be called before ico_find(). - * - * \param ico the ICO image to analyse - * \param size The size of data in cdata. - * \param data The bitmap source data. - * \return BMP_OK on success - */ -bmp_result ico_analyse(ico_collection *ico, size_t size, uint8_t *data); - -/** - * Decode a BMP - * - * This function decodes the BMP data such that bmp->bitmap is a valid - * image. The state of bmp->decoded is set to TRUE on exit such that it - * can easily be identified which BMPs are in a fully decoded state. - * - * \param bmp the BMP image to decode - * \return BMP_OK on success - */ -bmp_result bmp_decode(bmp_image *bmp); - -/** - * Decode a BMP using "limited transparency" - * - * Bitmaps do not have native transparency support. However, there is a - * "trick" that is used in some instances in which the first pixel of the - * bitmap becomes the "transparency index". The decoding application can - * replace this index with whatever background colour it chooses to - * create the illusion of transparency. - * - * When to use transparency is at the discretion of the decoding - * application. - * - * \param bmp the BMP image to decode - * \param colour the colour to use as "transparent" - * \return BMP_OK on success - */ -bmp_result bmp_decode_trans(bmp_image *bmp, uint32_t transparent_colour); - -/** - * Finds the closest BMP within an ICO collection - * - * This function finds the BMP with dimensions as close to a specified set - * as possible from the images in the collection. - * - * \param ico the ICO collection to examine - * \param width the preferred width (0 to use ICO header width) - * \param height the preferred height (0 to use ICO header height) - */ -bmp_image *ico_find(ico_collection *ico, uint16_t width, uint16_t height); - -/** - * Finalise a BMP prior to destruction. - * - * \param bmp the BMP image to finalise. - */ -void bmp_finalise(bmp_image *bmp); - -/** - * Finalise an ICO prior to destruction. - * - * \param ico the ICO image to finalise, - */ -void ico_finalise(ico_collection *ico); - -#endif diff --git a/include/graphics/loadbmp.h b/include/graphics/loadbmp.h new file mode 100644 index 0000000..c35318a --- /dev/null +++ b/include/graphics/loadbmp.h @@ -0,0 +1,278 @@ + +// Author: Christian Vallentin +// Website: http://vallentinsource.com +// Repository: https://github.com/MrVallentin/LoadBMP +// +// Date Created: January 03, 2014 +// Last Modified: August 13, 2016 +// +// Version: 1.1.0 + +// Copyright (c) 2014-2016 Christian Vallentin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. + +// Include loadbmp.h as following +// to create the implementation file. +// +// #define LOADBMP_IMPLEMENTATION +// #include "loadbmp.h" + +#ifndef LOADBMP_H +#define LOADBMP_H + +#ifdef __cplusplus +extern "C" { +#endif + + +// Errors +#define LOADBMP_NO_ERROR 0 + +#define LOADBMP_OUT_OF_MEMORY 1 + +#define LOADBMP_FILE_NOT_FOUND 2 +#define LOADBMP_FILE_OPERATION 3 + +#define LOADBMP_INVALID_FILE_FORMAT 4 + +#define LOADBMP_INVALID_SIGNATURE 5 +#define LOADBMP_INVALID_BITS_PER_PIXEL 6 + + +// Components +#define LOADBMP_RGB 3 +#define LOADBMP_RGBA 4 + + +#ifdef LOADBMP_IMPLEMENTATION +# define LOADBMP_API +#else +# define LOADBMP_API extern +#endif + + +// LoadBMP uses raw buffers and support RGB and RGBA formats. +// The order is RGBRGBRGB... or RGBARGBARGBA..., from top left +// to bottom right, without any padding. + + +LOADBMP_API unsigned int loadbmp_decode_file( + const char *filename, unsigned char **imageData, unsigned int *width, unsigned int *height, unsigned int components); + +LOADBMP_API unsigned int loadbmp_encode_file( + const char *filename, const unsigned char *imageData, unsigned int width, unsigned int height, unsigned int components); + + +#ifdef LOADBMP_IMPLEMENTATION + +// Disable Microsoft Visual C++ compiler security warnings for fopen, strcpy, etc being unsafe +#if defined(_MSC_VER) && (_MSC_VER >= 1310) +# pragma warning(disable: 4996) +#endif + +#include /* malloc(), free() */ +#include /* memset(), memcpy() */ +#include /* fopen(), fwrite(), fread(), fclose() */ + + +LOADBMP_API unsigned int loadbmp_decode_file( + const char *filename, unsigned char **imageData, unsigned int *width, unsigned int *height, unsigned int components) +{ + FILE *f = fopen(filename, "rb"); + + if (!f) + return LOADBMP_FILE_NOT_FOUND; + + unsigned char bmp_file_header[14]; + unsigned char bmp_info_header[40]; + unsigned char bmp_pad[3]; + + unsigned int w, h; + unsigned char *data = NULL; + + unsigned int x, y, i, padding; + + memset(bmp_file_header, 0, sizeof(bmp_file_header)); + memset(bmp_info_header, 0, sizeof(bmp_info_header)); + + if (fread(bmp_file_header, sizeof(bmp_file_header), 1, f) == 0) + { + fclose(f); + return LOADBMP_INVALID_FILE_FORMAT; + } + + if (fread(bmp_info_header, sizeof(bmp_info_header), 1, f) == 0) + { + fclose(f); + return LOADBMP_INVALID_FILE_FORMAT; + } + + if ((bmp_file_header[0] != 'B') || (bmp_file_header[1] != 'M')) + { + fclose(f); + return LOADBMP_INVALID_SIGNATURE; + } + + if ((bmp_info_header[14] != 24) && (bmp_info_header[14] != 32)) + { + fclose(f); + return LOADBMP_INVALID_BITS_PER_PIXEL; + } + + w = (bmp_info_header[4] + (bmp_info_header[5] << 8) + (bmp_info_header[6] << 16) + (bmp_info_header[7] << 24)); + h = (bmp_info_header[8] + (bmp_info_header[9] << 8) + (bmp_info_header[10] << 16) + (bmp_info_header[11] << 24)); + + if ((w > 0) && (h > 0)) + { + data = (unsigned char*)malloc(w * h * components); + + if (!data) + { + fclose(f); + return LOADBMP_OUT_OF_MEMORY; + } + + for (y = (h - 1); y != -1; y--) + { + for (x = 0; x < w; x++) + { + i = (x + y * w) * components; + + if (fread(data + i, 3, 1, f) == 0) + { + free(data); + + fclose(f); + return LOADBMP_INVALID_FILE_FORMAT; + } + + data[i] ^= data[i + 2] ^= data[i] ^= data[i + 2]; // BGR -> RGB + + if (components == LOADBMP_RGBA) + data[i + 3] = 255; + } + + padding = ((4 - (w * 3) % 4) % 4); + + if (fread(bmp_pad, 1, padding, f) != padding) + { + free(data); + + fclose(f); + return LOADBMP_INVALID_FILE_FORMAT; + } + } + } + + (*width) = w; + (*height) = h; + (*imageData) = data; + + fclose(f); + + return LOADBMP_NO_ERROR; +} + + +LOADBMP_API unsigned int loadbmp_encode_file( + const char *filename, const unsigned char *imageData, unsigned int width, unsigned int height, unsigned int components) +{ + FILE *f = fopen(filename, "wb"); + + if (!f) + return LOADBMP_FILE_OPERATION; + + unsigned char bmp_file_header[14] = { 'B', 'M', 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0 }; + unsigned char bmp_info_header[40] = { 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 24, 0 }; + const unsigned char bmp_pad[3] = { 0, 0, 0 }; + + const unsigned int size = 54 + width * height * 3; // 3 as the BMP format uses 3 channels (red, green, blue and NO alpha) + + unsigned int x, y, i, padding; + + unsigned char pixel[3]; + + bmp_file_header[2] = (unsigned char)(size); + bmp_file_header[3] = (unsigned char)(size >> 8); + bmp_file_header[4] = (unsigned char)(size >> 16); + bmp_file_header[5] = (unsigned char)(size >> 24); + + bmp_info_header[4] = (unsigned char)(width); + bmp_info_header[5] = (unsigned char)(width >> 8); + bmp_info_header[6] = (unsigned char)(width >> 16); + bmp_info_header[7] = (unsigned char)(width >> 24); + + bmp_info_header[8] = (unsigned char)(height); + bmp_info_header[9] = (unsigned char)(height >> 8); + bmp_info_header[10] = (unsigned char)(height >> 16); + bmp_info_header[11] = (unsigned char)(height >> 24); + + if (fwrite(bmp_file_header, 14, 1, f) == 0) + { + fclose(f); + return LOADBMP_FILE_OPERATION; + } + + if (fwrite(bmp_info_header, 40, 1, f) == 0) + { + fclose(f); + return LOADBMP_FILE_OPERATION; + } + + for (y = (height - 1); y != -1; y--) + { + for (x = 0; x < width; x++) + { + i = (x + y * width) * components; + + memcpy(pixel, imageData + i, sizeof(pixel)); + + pixel[0] ^= pixel[2] ^= pixel[0] ^= pixel[2]; // RGB -> BGR + + if (fwrite(pixel, sizeof(pixel), 1, f) == 0) + { + fclose(f); + return LOADBMP_FILE_OPERATION; + } + } + + padding = ((4 - (width * 3) % 4) % 4); + + if (fwrite(bmp_pad, 1, padding, f) != padding) + { + fclose(f); + return LOADBMP_FILE_OPERATION; + } + } + + fclose(f); + + return LOADBMP_NO_ERROR; +} + + +#endif + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/res/app.rsf b/res/app.rsf new file mode 100644 index 0000000..47112b1 --- /dev/null +++ b/res/app.rsf @@ -0,0 +1,195 @@ +BasicInfo: + Title : $(APP_TITLE) + CompanyCode : "00" + ProductCode : $(APP_PRODUCT_CODE) + ContentType : Application + Logo : Homebrew # Nintendo / Licensed / Distributed / iQue / iQueForSystem + +TitleInfo: + UniqueId : $(APP_UNIQUE_ID) + + Category : Application + +CardInfo: + MediaSize : 128MB # 128MB / 256MB / 512MB / 1GB / 2GB / 4GB + MediaType : Card1 # Card1 / Card2 + CardDevice : NorFlash # NorFlash(Pick this if you use savedata) / None + + +Option: + UseOnSD : true # true if App is to be installed to SD + FreeProductCode : true # Removes limitations on ProductCode + MediaFootPadding : false # If true CCI files are created with padding + EnableCrypt : false # Enables encryption for NCCH and CIA + EnableCompress : true # Compresses exefs code + +AccessControlInfo: + #UseExtSaveData : true + #ExtSaveDataId: 0xff3ff + #UseExtendedSaveDataAccessControl: true + #AccessibleSaveDataIds: [0x101, 0x202, 0x303, 0x404, 0x505, 0x606] + +SystemControlInfo: + SaveDataSize: 128KB + RemasterVersion: 7 + StackSize: 0x40000 + +RomFs: + RootPath : $(APP_ROMFS) + +# DO NOT EDIT BELOW HERE OR PROGRAMS WILL NOT LAUNCH (most likely) + +AccessControlInfo: + FileSystemAccess: + - Debug + - DirectSdmc + - DirectSdmcWrite + + IdealProcessor : 0 + AffinityMask : 1 + + Priority : 16 + + MaxCpu : 0x9E # Default + DisableDebug : false + EnableForceDebug : false + CanWriteSharedPage : false + CanUsePrivilegedPriority : false + CanUseNonAlphabetAndNumber : false + PermitMainFunctionArgument : false + CanShareDeviceMemory : false + RunnableOnSleep : false + SpecialMemoryArrange : false + CoreVersion : 2 + DescVersion : 2 + + ReleaseKernelMajor : "02" + ReleaseKernelMinor : "33" + MemoryType : Application + HandleTableSize: 512 + IORegisterMapping: + - 1ff50000-1ff57fff + - 1ff70000-1ff77fff + MemoryMapping: + - 1f000000-1f5fffff:r + SystemCallAccess: + ArbitrateAddress: 34 + Break: 60 + CancelTimer: 28 + ClearEvent: 25 + ClearTimer: 29 + CloseHandle: 35 + ConnectToPort: 45 + ControlMemory: 1 + CreateAddressArbiter: 33 + CreateEvent: 23 + CreateMemoryBlock: 30 + CreateMutex: 19 + CreateSemaphore: 21 + CreateThread: 8 + CreateTimer: 26 + DuplicateHandle: 39 + ExitProcess: 3 + ExitThread: 9 + GetCurrentProcessorNumber: 17 + GetHandleInfo: 41 + GetProcessId: 53 + GetProcessIdOfThread: 54 + GetProcessIdealProcessor: 6 + GetProcessInfo: 43 + GetResourceLimit: 56 + GetResourceLimitCurrentValues: 58 + GetResourceLimitLimitValues: 57 + GetSystemInfo: 42 + GetSystemTick: 40 + GetThreadContext: 59 + GetThreadId: 55 + GetThreadIdealProcessor: 15 + GetThreadInfo: 44 + GetThreadPriority: 11 + MapMemoryBlock: 31 + OutputDebugString: 61 + QueryMemory: 2 + ReleaseMutex: 20 + ReleaseSemaphore: 22 + SendSyncRequest1: 46 + SendSyncRequest2: 47 + SendSyncRequest3: 48 + SendSyncRequest4: 49 + SendSyncRequest: 50 + SetThreadPriority: 12 + SetTimer: 27 + SignalEvent: 24 + SleepThread: 10 + UnmapMemoryBlock: 32 + WaitSynchronization1: 36 + WaitSynchronizationN: 37 + InterruptNumbers: + ServiceAccessControl: + - APT:U + - $hioFIO + - $hostio0 + - $hostio1 + - ac:u + - boss:U + - cam:u + - cecd:u + - cfg:u + - dlp:FKCL + - dlp:SRVR + - dsp::DSP + - frd:u + - fs:USER + - gsp::Gpu + - hid:USER + - http:C + - mic:u + - ndm:u + - news:s + - nwm::UDS + - ptm:u + - pxi:dev + - soc:U + - gsp::Lcd + - y2r:u + - ldr:ro + - ir:USER + - ir:u + - csnd:SND + - am:u + - ns:s + - ptm:sysm + - mcu::HWC + +SystemControlInfo: + Dependency: + ac: 0x0004013000002402L + am: 0x0004013000001502L + boss: 0x0004013000003402L + camera: 0x0004013000001602L + cecd: 0x0004013000002602L + cfg: 0x0004013000001702L + codec: 0x0004013000001802L + csnd: 0x0004013000002702L + dlp: 0x0004013000002802L + dsp: 0x0004013000001a02L + friends: 0x0004013000003202L + gpio: 0x0004013000001b02L + gsp: 0x0004013000001c02L + hid: 0x0004013000001d02L + http: 0x0004013000002902L + i2c: 0x0004013000001e02L + ir: 0x0004013000003302L + mcu: 0x0004013000001f02L + mic: 0x0004013000002002L + ndm: 0x0004013000002b02L + news: 0x0004013000003502L + nim: 0x0004013000002c02L + nwm: 0x0004013000002d02L + pdn: 0x0004013000002102L + ps: 0x0004013000003102L + ptm: 0x0004013000002202L + ro: 0x0004013000003702L + socket: 0x0004013000002e02L + spi: 0x0004013000002302L + ssl: 0x0004013000002f02L diff --git a/res/banner.png b/res/banner.png new file mode 100644 index 0000000000000000000000000000000000000000..19ea9a63880b335d53e88f06cdbee14fa692d21f GIT binary patch literal 10753 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%0DcTgLlxLMSYPtGd{p)(qX3yq@&?HXUj@X2xAktmN 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

lq@au|9 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 literal 0 HcmV?d00001 diff --git a/res/logo.lz11 b/res/logo.lz11 new file mode 100644 index 0000000000000000000000000000000000000000..5764b298a807007a1740fe82101964482d8533fa GIT binary patch literal 8192 zcmeHL|9cZ5?+K)pBa7Hzb$y>68ZRIFG^rcn7} zzswZY?P`I%J$9HPnbpGNy?(uVFBp9Fq!?ebDzzf}MCG&M^VOs# z`);}MACE0iA!aIEz48$<@gpstF)bXln?YVzvnX#_`JDVE^X$v75{{7k`6cBWHgA|x z{Cv$y`=f5}N~;&+*m`;0#=Mf%_A0h;tqJfG`SpdOz44*-53kG1G=7a9TK|~OSCe?i zlxSPCN^Dy-MQtlwDHKj=6b@KZUx`2Q_*VUa;s?T;b}p>R%m4J>&q6T!IT;n!?AVky z_Zj2f{{eV*{U%?9&kO5)H8sAf-1VmNO&hDA%04FDtoYFbW3V+m_bw1k)7+1x9K~yY zw_O!HXi@3r2Q#cp3Mn7;tlPXH!KA#AaC>^?bzAeM{3Td|)Dn2y4Y3k#ZZ5ZKm0Gf~ zJWu^GHc#LVc)XLbcEF>~>fgMQan+$t736grqydk)VoRQ!_u~)OINV$0-=7dFwmfRa z_Qjpo_b=|f?u}n>!ru9i!!MZ)0q60E$9eEXcf}S@H=I=9n*K4ouiI*T^7=#fT}XXQ z_w98>;X3!)JaVojhrsFP?3a(X*y)!& z*%wav?ZkF+m(4(oLU0IgRmgW((O4kF;ALUtd=MNY@%lW{{|j#x*T#t!Oig=eI&@A% zyA7U62qr@}8HB#y9V3k+L9>KD;v3%(IJzpvPb668ebO1u*E@Sn3RyUwkm7cPBmFOC zMPd^6>nZwQf? z(a}*NA_O|TXM?M)T$^eJ2^rzrKyL+Ij3}742yz5^y>k*4J&07Wj=s4}uJ--$ zA^pdFDg=aCI&qh)tF~LyU18$VU451#+J8xh?SI{LyK z(hIe7$j3WolmCMqb4cG_J2|Fu?BD8bmkhl!R}2kp@`Dux`1vA=vTx6igP0$1+bNG} zx~d~w2#0SO{|u&8pgf%{lWW%=yap;cefl)D_I9n464THP9zG>j)TvVqv9u?k9kY-)8*hDn>>OcJsu*D7$eX)n@ zz(}G-zoS`Y2xuW$lD~~Vf3OtD@fT*1tEZxW70sip!i^`?# zm`VQeU)dyO@SvSs!r^^3j{G$%zh~UAD^_&v8<$YS5f6)~8kBw^q65QmDm5{bO~XuVfpC?(p_!;GoNogwpAZsZ zi0Qa9HZa5lgoJCp@Woz+>!PSK8a=a5wqWJGu*`BTxHX0d4!gSLjIuu2E!#b%cz_tF zj#2@t{lMl+rThH#r39|D-3|k=Roya0of0Q$X?0=tn24S%VZ;b%IvAr$gmBwM)ZoXp zSdod;B0I(c?Q*DY^st4KO6zS>$w-irPZKU-0kWEjy|`dP4Fi>2!yFV2_JTEBoqlP8 zBJT-D+10&kGh|+6n@4D{dC_8%kf%UqbuR(MzE9yww8$fV!+tCT@79v~W?Z7~uWG0E zDa#Cg!UO^s9VwcP`aOTtLo(G!aCeGzdg+pTW~(CR)Ep-D@Mq*Oy%g z+-tuHIKnrrS4>2qQEZY(6HRPnEno>`R?t$kCmRKnX6i~^TxM1CbGl)MSX;nMKtzX& zf%?15f7l;vt&4*k9x;-oUK3sht;Hl|{M`o97-6uETXCD*5k*T!C8YIG>I5lF=0lwf zqa0`pSU}4lOfXIDO|Ze?WoFqdqrLoyoW93JE6qzm^9#Pi{zRPA`ysRWT$t8h0ROUx zY~IvOw6NR;MLI}nd)V&->;BL=Chv#kLP{cy(ez(oR7q2w5YawR1S*BLeX|nj{ZL`b zh6$`@YsabN7~PC4IE$!9%-ikMu}%it=&7k?BOL91o8A#da(N2qeD^QkCMHH%UJJ57 zJQ+{5L2m8ay|R1HU_%GjN*ECZdq89%;r18RS7;UnqcNioxIsoNVhsI7>OP@0Hpvn* zJssrjzZzyPC;+`iygzvkP56b3W(SbzGl6E`*s%4jRi~(FkY`O_z>bqFTq5FCUT3BbZ9I z#x^m$1A=9u7dCg?IJT9R>t)>#wZ%f0n(D?$ZX_wd>Oe4qYNpH^0s+CH+rcOc7s0_0 z;%JYrGqP9tB}U85d9GYnay^g=0#{4m7?l&r2lYy04}3i;hsQu>K`Sr>dHhAG4Rj~m zZX$FsBlm(lg4;nu)!nq3M+K6^`pH6I?ZgR&m#OYrQAr7SREg?fwaXpNdQUeqpHH?Z zv?sFlI$*i)b}39$GWAVylcY1MYj?5~G*m!+F-)}4P>#cU?I3s2t*+WY6LzIg%->hW zSNc~pfi|AoA$RP`?WFzDi0q~y1Pca(l!MUb@Y%;eD-(4%)~2=&QlnY~M0-3z<)RS+ zrL=lE=uNqUP@EywLPN%ggAmY@aawSMy3|!4zDU!P{%|ojO-1tuiE5rdObeU^tY)KQ z!X51*vUhJ6rz} zf}cbP{P~DeNyK}Fy%M^faBI`;L-qOh+Fn$5%M zit-B;7DWt84E28?{5p)J=w}NYLV+9N<6v;CiHc(v(M|^55tm&U8TM4-- z!4jp2GLco?TqW&1d+7<~G%dq0OPxqViu)CRj2#CxEDMQbr8Y%-8{mS|KsPX-urN`5 zVZLTjGH5htPSb578XcvQTDR66MElB;`AhRkt9>|KHt+1G`e}rTrsE>DEK#~w0Vy5H zX;j}S>0P^M+)YNSN$#R){5`qI3l$&6YyGW}rG98Gauzdc2kEf~y})HKhx%Y@`7N^1 z52|^bS(Xo4;p7EdVQMcU>uQo~q`@s`&<2XmS9^W%x&l|*sR$862TUo@3r1nJb1D6X zOwmr-Pz%e-X#QI0=uq7+x5)*Bitlj0RwlDYoiOaBB_`2D;9&uR8O+_G!@YIk!^5}2 z^l_Rr4CaB>!@~lTRft5JiRQ2-DD_loQ>av3ZT^6YHE1D9ytpwyxi^dX%!yP7x1Ca4 z)(O}8&z)t;EvlVnfEHr3LDWK_GsV8AK66-rIlDn8YtnrL1YSpSw8*!Yqg`slY=>jkd2uGEI%^F;BehAQ#X`?9d)LxtxPoySC}Nb5W1S~>`cLCz;Q7A1Tf3wRZ_f} z=s<2*O{$7vBapDrMrxN+<~O%Q1t!-=%gOj;S`rj0*o4kncWI;+@dWM(O}T!4+l+<7 zu@hAfrhhbiVdqSYb{~SL(oUbKnC@^0X_F&P5Qxj^%1>S=B1KkSAtoPTcw**>!am(C zRLbcOIwdEzV`orknaxK;FK?L^CQL#qEn*7k4b~-d6HJ>EiH< zA@WS=w(@OWjsBWk>vs$BP232Fl!9c}MjNfsWLndKflkbtUYX8#2=Bhl(Q4MvC9$bi z5)7Z`j@(up*ut@N_Dt|G;W>Jn>IN4Qky0wLbkd9^#etZuNxu5sjl$;sRF%ZSv_k7N zx29v1$`Nl-vg4;CZp;)FtW&L|Hn(HP%&{|QMrO@Wu{z9kFPF(w6=)0+#YMoG2lAJG-|MOUL2~pFo zT!g~aKY5t`^|p&ACeu7%I0j6xv1(m~l_ye=V^WI=10OLQ{KpeVpPRDoxd&j^vCNil zD~sNNS@7}r-5-)Text, "wav", 3) == 0) || (strncasecmp(file->ext, "fla", 3) == 0)) Draw_Image(icon_audio, 30, 56 + (38 * printed)); else if ((strncasecmp(file->ext, "png", 3) == 0) || (strncasecmp(file->ext, "jpg", 3) == 0) - || (strncasecmp(file->ext, "bmp", 3) == 0) || (strncasecmp(file->ext, "gif", 3) == 0)) + || (strncasecmp(file->ext, "bmp", 3) == 0)) Draw_Image(icon_image, 30, 56 + (38 * printed)); else if ((strncasecmp(file->ext, "txt", 3) == 0) || (strncasecmp(file->ext, "lua", 3) == 0) || (strncasecmp(file->ext, "cfg", 3) == 0)) @@ -297,7 +297,7 @@ void Dirbrowse_OpenFile(void) } } else if ((strncasecmp(file->ext, "png", 3) == 0) || (strncasecmp(file->ext, "jpg", 3) == 0) || - (strncasecmp(file->ext, "bmp", 3) == 0) || (strncasecmp(file->ext, "gif", 3) == 0)) + (strncasecmp(file->ext, "bmp", 3) == 0)) Gallery_DisplayImage(path); else if (strncasecmp(file->ext, "zip", 3) == 0) { diff --git a/source/graphics/libnsbmp.c b/source/graphics/libnsbmp.c deleted file mode 100644 index 173581c..0000000 --- a/source/graphics/libnsbmp.c +++ /dev/null @@ -1,1371 +0,0 @@ -/* - * Copyright 2006 Richard Wilson - * Copyright 2008 Sean Fox - * - * This file is part of NetSurf's libnsbmp, http://www.netsurf-browser.org/ - * Licenced under the MIT License, - * http://www.opensource.org/licenses/mit-license.php - */ - -/** - * \file - * BMP decoding implementation - * - * This library decode windows bitmaps and icons from their disc images. - * - * The image format is described in several documents: - * https://msdn.microsoft.com/en-us/library/dd183391(v=vs.85).aspx - * http://www.fileformat.info/format/bmp/egff.htm - * https://en.wikipedia.org/wiki/BMP_file_format - * - * Despite the format being clearly defined many bitmaps found on the web are - * not compliant and this implementation attempts to cope with as many issues - * as possible rather than simply failing. - */ - -#include -#include -#include -#include -#include -#include - -#include - -/* squashes unused variable compiler warnings */ -#define UNUSED(x) ((x)=(x)) - -/* BMP entry sizes */ -#define BMP_FILE_HEADER_SIZE 14 -#define ICO_FILE_HEADER_SIZE 6 -#define ICO_DIR_ENTRY_SIZE 16 - -/* the bitmap information header types (encoded as lengths) */ -#define BITMAPCOREHEADER 12 - -#ifdef WE_NEED_INT8_READING_NOW -static inline int8_t read_int8(uint8_t *data, unsigned int o) { - return (int8_t) data[o]; -} -#endif - -static inline uint8_t read_uint8(uint8_t *data, unsigned int o) { - return (uint8_t) data[o]; -} - -static inline int16_t read_int16(uint8_t *data, unsigned int o) { - return (int16_t) (data[o] | (data[o+1] << 8)); -} - -static inline uint16_t read_uint16(uint8_t *data, unsigned int o) { - return (uint16_t) (data[o] | (data[o+1] << 8)); -} - -static inline int32_t read_int32(uint8_t *data, unsigned int o) { - return (int32_t) (data[o] | (data[o+1] << 8) | (data[o+2] << 16) | (data[o+3] << 24)); -} - -static inline uint32_t read_uint32(uint8_t *data, unsigned int o) { - return (uint32_t) (data[o] | (data[o+1] << 8) | (data[o+2] << 16) | (data[o+3] << 24)); -} - - -/** - * Parse the bitmap info header - */ -static bmp_result bmp_info_header_parse(bmp_image *bmp, uint8_t *data) -{ - uint32_t header_size; - uint32_t i; - uint8_t j; - int32_t width, height; - uint8_t palette_size; - unsigned int flags = 0; - - /* must be at least enough data for a core header */ - if (bmp->buffer_size < (BMP_FILE_HEADER_SIZE + BITMAPCOREHEADER)) { - return BMP_INSUFFICIENT_DATA; - } - - header_size = read_uint32(data, 0); - - /* ensure there is enough data for the declared header size*/ - if ((bmp->buffer_size - BMP_FILE_HEADER_SIZE) < header_size) { - return BMP_INSUFFICIENT_DATA; - } - - /* a variety of different bitmap headers can follow, depending - * on the BMP variant. The header length field determines the type. - */ - if (header_size == BITMAPCOREHEADER) { - /* the following header is for os/2 and windows 2.x and consists of: - * - * +0 UINT32 size of this header (in bytes) - * +4 INT16 image width (in pixels) - * +6 INT16 image height (in pixels) - * +8 UINT16 number of colour planes (always 1) - * +10 UINT16 number of bits per pixel - */ - width = read_int16(data, 4); - height = read_int16(data, 6); - if ((width <= 0) || (height == 0)) - return BMP_DATA_ERROR; - if (height < 0) { - bmp->reversed = true; - height = -height; - } - /* ICOs only support 256*256 resolutions - * In the case of the ICO header, the height is actually the added - * height of XOR-Bitmap and AND-Bitmap (double the visible height) - * Technically we could remove this check and ICOs with bitmaps - * of any size could be processed; this is to conform to the spec. - */ - if (bmp->ico) { - if ((width > 256) || (height > 512)) { - return BMP_DATA_ERROR; - } else { - bmp->width = width; - bmp->height = height / 2; - } - } else { - bmp->width = width; - bmp->height = height; - } - if (read_uint16(data, 8) != 1) - return BMP_DATA_ERROR; - bmp->bpp = read_uint16(data, 10); - /** - * The bpp value should be in the range 1-32, but the only - * values considered legal are: - * RGB ENCODING: 1, 4, 8, 16, 24 and 32 - */ - if ((bmp->bpp != 1) && (bmp->bpp != 4) && - (bmp->bpp != 8) && - (bmp->bpp != 16) && - (bmp->bpp != 24) && - (bmp->bpp != 32)) - return BMP_DATA_ERROR; - bmp->colours = (1 << bmp->bpp); - palette_size = 3; - } else if (header_size < 40) { - return BMP_DATA_ERROR; - } else { - /* the following header is for windows 3.x and onwards. it is a - * minimum of 40 bytes and (as of Windows 95) a maximum of 108 bytes. - * - * +0 UINT32 size of this header (in bytes) - * +4 INT32 image width (in pixels) - * +8 INT32 image height (in pixels) - * +12 UINT16 number of colour planes (always 1) - * +14 UINT16 number of bits per pixel - * +16 UINT32 compression methods used - * +20 UINT32 size of bitmap (in bytes) - * +24 UINT32 horizontal resolution (in pixels per meter) - * +28 UINT32 vertical resolution (in pixels per meter) - * +32 UINT32 number of colours in the image - * +36 UINT32 number of important colours - * +40 UINT32 mask identifying bits of red component - * +44 UINT32 mask identifying bits of green component - * +48 UINT32 mask identifying bits of blue component - * +52 UINT32 mask identifying bits of alpha component - * +56 UINT32 color space type - * +60 UINT32 x coordinate of red endpoint - * +64 UINT32 y coordinate of red endpoint - * +68 UINT32 z coordinate of red endpoint - * +72 UINT32 x coordinate of green endpoint - * +76 UINT32 y coordinate of green endpoint - * +80 UINT32 z coordinate of green endpoint - * +84 UINT32 x coordinate of blue endpoint - * +88 UINT32 y coordinate of blue endpoint - * +92 UINT32 z coordinate of blue endpoint - * +96 UINT32 gamma red coordinate scale value - * +100 UINT32 gamma green coordinate scale value - * +104 UINT32 gamma blue coordinate scale value - */ - width = read_int32(data, 4); - height = read_int32(data, 8); - if ((width <= 0) || (height == 0)) - return BMP_DATA_ERROR; - if (height < 0) { - bmp->reversed = true; - if (height <= -INT32_MAX) { - height = INT32_MAX; - } else { - height = -height; - } - } - /* ICOs only support 256*256 resolutions - * In the case of the ICO header, the height is actually the added - * height of XOR-Bitmap and AND-Bitmap (double the visible height) - * Technically we could remove this check and ICOs with bitmaps - * of any size could be processed; this is to conform to the spec. - */ - if (bmp->ico) { - if ((width > 256) || (height > 512)) { - return BMP_DATA_ERROR; - } else { - bmp->width = width; - bmp->height = height / 2; - } - } else { - bmp->width = width; - bmp->height = height; - } - if (read_uint16(data, 12) != 1) - return BMP_DATA_ERROR; - bmp->bpp = read_uint16(data, 14); - if (bmp->bpp == 0) - bmp->bpp = 8; - bmp->encoding = read_uint32(data, 16); - /** - * The bpp value should be in the range 1-32, but the only - * values considered legal are: - * RGB ENCODING: 1, 4, 8, 16, 24 and 32 - * RLE4 ENCODING: 4 - * RLE8 ENCODING: 8 - * BITFIELD ENCODING: 16 and 32 - */ - switch (bmp->encoding) { - case BMP_ENCODING_RGB: - if ((bmp->bpp != 1) && (bmp->bpp != 4) && - (bmp->bpp != 8) && - (bmp->bpp != 16) && - (bmp->bpp != 24) && - (bmp->bpp != 32)) - return BMP_DATA_ERROR; - break; - case BMP_ENCODING_RLE8: - if (bmp->bpp != 8) - return BMP_DATA_ERROR; - break; - case BMP_ENCODING_RLE4: - if (bmp->bpp != 4) - return BMP_DATA_ERROR; - break; - case BMP_ENCODING_BITFIELDS: - if ((bmp->bpp != 16) && (bmp->bpp != 32)) - return BMP_DATA_ERROR; - break; - /* invalid encoding */ - default: - return BMP_DATA_ERROR; - break; - } - /* Bitfield encoding means we have red, green, blue, and alpha masks. - * Here we acquire the masks and determine the required bit shift to - * align them in our 24-bit color 8-bit alpha format. - */ - if (bmp->encoding == BMP_ENCODING_BITFIELDS) { - if (header_size == 40) { - header_size += 12; - if (bmp->buffer_size < (14 + header_size)) - return BMP_INSUFFICIENT_DATA; - for (i = 0; i < 3; i++) - bmp->mask[i] = read_uint32(data, 40 + (i << 2)); - } else { - for (i = 0; i < 4; i++) - bmp->mask[i] = read_uint32(data, 40 + (i << 2)); - } - for (i = 0; i < 4; i++) { - if (bmp->mask[i] == 0) - break; - for (j = 31; j > 0; j--) - if (bmp->mask[i] & (1 << j)) { - if ((j - 7) > 0) - bmp->mask[i] &= 0xff << (j - 7); - else - bmp->mask[i] &= 0xff >> (-(j - 7)); - bmp->shift[i] = (i << 3) - (j - 7); - break; - } - } - } - bmp->colours = read_uint32(data, 32); - if (bmp->colours == 0) - bmp->colours = (1 << bmp->bpp); - palette_size = 4; - } - data += header_size; - - /* if there's no alpha mask, flag the bmp opaque */ - if ((!bmp->ico) && (bmp->mask[3] == 0)) { - flags |= BMP_OPAQUE; - bmp->opaque = true; - } - - /* we only have a palette for <16bpp */ - if (bmp->bpp < 16) { - /* we now have a series of palette entries of the format: - * - * +0 BYTE blue - * +1 BYTE green - * +2 BYTE red - * - * if the palette is from an OS/2 or Win2.x file then the entries - * are padded with an extra byte. - */ - - /* boundary checking */ - if (bmp->buffer_size < (14 + header_size + ((uint64_t)4 * bmp->colours))) - return BMP_INSUFFICIENT_DATA; - - /* create the colour table */ - bmp->colour_table = (uint32_t *)malloc(bmp->colours * 4); - if (!bmp->colour_table) - return BMP_INSUFFICIENT_MEMORY; - for (i = 0; i < bmp->colours; i++) { - bmp->colour_table[i] = data[2] | (data[1] << 8) | (data[0] << 16); - if (bmp->opaque) - bmp->colour_table[i] |= (0xff << 24); - data += palette_size; - bmp->colour_table[i] = read_uint32((uint8_t *)&bmp->colour_table[i],0); - } - - /* some bitmaps have a bad offset if there is a pallete, work - * round this by fixing up the data offset to after the palette - * but only if there is data following the palette as some - * bitmaps encode data in the palette! - */ - if ((bmp->bitmap_offset < (uint32_t)(data - bmp->bmp_data)) && - ((bmp->buffer_size - (data - bmp->bmp_data)) > 0)) { - bmp->bitmap_offset = data - bmp->bmp_data; - } - } - - /* create our bitmap */ - flags |= BMP_NEW | BMP_CLEAR_MEMORY; - bmp->bitmap = bmp->bitmap_callbacks.bitmap_create(bmp->width, bmp->height, flags); - if (!bmp->bitmap) { - if (bmp->colour_table) - free(bmp->colour_table); - bmp->colour_table = NULL; - return BMP_INSUFFICIENT_MEMORY; - } - /* BMPs within ICOs don't have BMP file headers, so the image data should - * always be right after the colour table. - */ - if (bmp->ico) - bmp->bitmap_offset = (intptr_t)data - (intptr_t)bmp->bmp_data; - return BMP_OK; -} - - -/** - * Parse the bitmap file header - * - * \param bmp The bitmap. - * \param data The data for the file header - * \return BMP_OK on success or error code on faliure - */ -static bmp_result bmp_file_header_parse(bmp_image *bmp, uint8_t *data) -{ - /* standard 14-byte BMP file header is: - * - * +0 UINT16 File Type ('BM') - * +2 UINT32 Size of File (in bytes) - * +6 INT16 Reserved Field (1) - * +8 INT16 Reserved Field (2) - * +10 UINT32 Starting Position of Image Data (offset in bytes) - */ - if (bmp->buffer_size < BMP_FILE_HEADER_SIZE) - return BMP_INSUFFICIENT_DATA; - - if ((data[0] != (uint8_t)'B') || (data[1] != (uint8_t)'M')) - return BMP_DATA_ERROR; - - bmp->bitmap_offset = read_uint32(data, 10); - - /* check the offset to data lies within the file */ - if (bmp->bitmap_offset >= bmp->buffer_size) { - return BMP_INSUFFICIENT_DATA; - } - - return BMP_OK; -} - - -/** - * Allocates memory for the next BMP in an ICO collection - * - * Sets proper structure values - * - * \param ico the ICO collection to add the image to - * \param image a pointer to the ICO image to be initialised - */ -static bmp_result next_ico_image(ico_collection *ico, ico_image *image) { - bmp_create(&image->bmp, &ico->bitmap_callbacks); - image->next = ico->first; - ico->first = image; - return BMP_OK; -} - - -/** - * Parse the icon file header - * - * \param ico The icon collection. - * \param data The header data to parse. - * \return BMP_OK on successful parse else error code - */ -static bmp_result ico_header_parse(ico_collection *ico, uint8_t *data) -{ - uint16_t count, i; - bmp_result result; - int area, max_area = 0; - - /* 6-byte ICO file header is: - * - * +0 INT16 Reserved (should be 0) - * +2 UINT16 Type (1 for ICO, 2 for CUR) - * +4 UINT16 Number of BMPs to follow - */ - if (ico->buffer_size < ICO_FILE_HEADER_SIZE) - return BMP_INSUFFICIENT_DATA; - // if (read_int16(data, 2) != 0x0000) - // return BMP_DATA_ERROR; - if (read_uint16(data, 2) != 0x0001) - return BMP_DATA_ERROR; - count = read_uint16(data, 4); - if (count == 0) - return BMP_DATA_ERROR; - data += ICO_FILE_HEADER_SIZE; - - /* check if we have enough data for the directory */ - if (ico->buffer_size < (uint32_t)(ICO_FILE_HEADER_SIZE + (ICO_DIR_ENTRY_SIZE * count))) - return BMP_INSUFFICIENT_DATA; - - /* Decode the BMP files. - * - * 16-byte ICO directory entry is: - * - * +0 UINT8 Width (0 for 256 pixels) - * +1 UINT8 Height (0 for 256 pixels) - * +2 UINT8 Colour count (0 if more than 256 colours) - * +3 INT8 Reserved (should be 0, but may not be) - * +4 UINT16 Colour Planes (should be 0 or 1) - * +6 UINT16 Bits Per Pixel - * +8 UINT32 Size of BMP info header + bitmap data in bytes - * +12 UINT32 Offset (points to the BMP info header, not the bitmap data) - */ - for (i = 0; i < count; i++) { - ico_image *image; - image = calloc(1, sizeof(ico_image)); - if (!image) - return BMP_INSUFFICIENT_MEMORY; - result = next_ico_image(ico, image); - if (result != BMP_OK) - return result; - image->bmp.width = read_uint8(data, 0); - if (image->bmp.width == 0) - image->bmp.width = 256; - image->bmp.height = read_uint8(data, 1); - if (image->bmp.height == 0) - image->bmp.height = 256; - image->bmp.buffer_size = read_uint32(data, 8); - image->bmp.bmp_data = ico->ico_data + read_uint32(data, 12); - image->bmp.ico = true; - data += ICO_DIR_ENTRY_SIZE; - - /* Ensure that the bitmap data resides in the buffer */ - if (image->bmp.bmp_data - ico->ico_data >= 0 && - (uint32_t)(image->bmp.bmp_data - - ico->ico_data) >= ico->buffer_size) - return BMP_DATA_ERROR; - - /* Ensure that we have sufficient data to read the bitmap */ - if (image->bmp.buffer_size - ICO_DIR_ENTRY_SIZE >= - ico->buffer_size - (ico->ico_data - data)) - return BMP_INSUFFICIENT_DATA; - - result = bmp_info_header_parse(&image->bmp, - image->bmp.bmp_data); - if (result != BMP_OK) - return result; - - /* adjust the size based on the images available */ - area = image->bmp.width * image->bmp.height; - if (area > max_area) { - ico->width = image->bmp.width; - ico->height = image->bmp.height; - max_area = area; - } - } - return BMP_OK; -} - - -/** - * Decode BMP data stored in 32bpp colour. - * - * \param bmp the BMP image to decode - * \param start the data to decode, updated to last byte read on success - * \param bytes the number of bytes of data available - * \return BMP_OK on success - * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; - * in this case, the image may be partially viewable - */ -static bmp_result bmp_decode_rgb32(bmp_image *bmp, uint8_t **start, int bytes) -{ - uint8_t *top, *bottom, *end, *data; - uint32_t *scanline; - uint32_t x, y; - uint32_t swidth; - uint8_t i; - uint32_t word; - - assert(bmp->bpp == 32); - - data = *start; - swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; - top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); - if (!top) - return BMP_INSUFFICIENT_MEMORY; - bottom = top + (uint64_t)swidth * (bmp->height - 1); - end = data + bytes; - bmp->decoded = true; - - /* Determine transparent index */ - if (bmp->limited_trans) { - if ((data + 4) > end) - return BMP_INSUFFICIENT_DATA; - if (bmp->encoding == BMP_ENCODING_BITFIELDS) - bmp->transparent_index = read_uint32(data, 0); - else - bmp->transparent_index = data[2] | (data[1] << 8) | (data[0] << 16); - } - - for (y = 0; y < bmp->height; y++) { - if ((data + (4 * bmp->width)) > end) - return BMP_INSUFFICIENT_DATA; - if (bmp->reversed) - scanline = (void *)(top + (y * swidth)); - else - scanline = (void *)(bottom - (y * swidth)); - if (bmp->encoding == BMP_ENCODING_BITFIELDS) { - for (x = 0; x < bmp->width; x++) { - word = read_uint32(data, 0); - for (i = 0; i < 4; i++) - if (bmp->shift[i] > 0) - scanline[x] |= ((word & bmp->mask[i]) << bmp->shift[i]); - else - scanline[x] |= ((word & bmp->mask[i]) >> (-bmp->shift[i])); - /* 32-bit BMPs have alpha masks, but sometimes they're not utilized */ - if (bmp->opaque) - scanline[x] |= (0xff << 24); - data += 4; - scanline[x] = read_uint32((uint8_t *)&scanline[x],0); - } - } else { - for (x = 0; x < bmp->width; x++) { - scanline[x] = data[2] | (data[1] << 8) | (data[0] << 16); - if ((bmp->limited_trans) && (scanline[x] == bmp->transparent_index)) { - scanline[x] = bmp->trans_colour; - } - if (bmp->opaque) { - scanline[x] |= (0xff << 24); - } else { - scanline[x] |= data[3] << 24; - } - data += 4; - scanline[x] = read_uint32((uint8_t *)&scanline[x],0); - } - } - } - *start = data; - return BMP_OK; -} - - -/** - * Decode BMP data stored in 24bpp colour. - * - * \param bmp the BMP image to decode - * \param start the data to decode, updated to last byte read on success - * \param bytes the number of bytes of data available - * \return BMP_OK on success - * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; - * in this case, the image may be partially viewable - */ -static bmp_result bmp_decode_rgb24(bmp_image *bmp, uint8_t **start, int bytes) -{ - uint8_t *top, *bottom, *end, *data; - uint32_t *scanline; - uint32_t x, y; - uint32_t swidth; - intptr_t addr; - - assert(bmp->encoding == BMP_ENCODING_RGB); - assert(bmp->bpp == 24); - - data = *start; - swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; - top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); - if (!top) { - return BMP_INSUFFICIENT_MEMORY; - } - - bottom = top + (uint64_t)swidth * (bmp->height - 1); - end = data + bytes; - addr = ((intptr_t)data) & 3; - bmp->decoded = true; - - /* Determine transparent index */ - if (bmp->limited_trans) { - if ((data + 3) > end) { - return BMP_INSUFFICIENT_DATA; - } - - bmp->transparent_index = data[2] | (data[1] << 8) | (data[0] << 16); - } - - for (y = 0; y < bmp->height; y++) { - if ((data + (3 * bmp->width)) > end) { - return BMP_INSUFFICIENT_DATA; - } - - if (bmp->reversed) { - scanline = (void *)(top + (y * swidth)); - } else { - scanline = (void *)(bottom - (y * swidth)); - } - - for (x = 0; x < bmp->width; x++) { - scanline[x] = data[2] | (data[1] << 8) | (data[0] << 16); - if ((bmp->limited_trans) && (scanline[x] == bmp->transparent_index)) { - scanline[x] = bmp->trans_colour; - } else { - scanline[x] |= (0xff << 24); - } - data += 3; - scanline[x] = read_uint32((uint8_t *)&scanline[x],0); - } - - while (addr != (((intptr_t)data) & 3)) { - data++; - } - } - *start = data; - return BMP_OK; -} - - -/** - * Decode BMP data stored in 16bpp colour. - * - * \param bmp the BMP image to decode - * \param start the data to decode, updated to last byte read on success - * \param bytes the number of bytes of data available - * \return BMP_OK on success - * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; - * in this case, the image may be partially viewable - */ -static bmp_result bmp_decode_rgb16(bmp_image *bmp, uint8_t **start, int bytes) -{ - uint8_t *top, *bottom, *end, *data; - uint32_t *scanline; - uint32_t x, y, swidth; - intptr_t addr; - uint8_t i; - uint16_t word; - - data = *start; - swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; - top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); - if (!top) - return BMP_INSUFFICIENT_MEMORY; - bottom = top + (uint64_t)swidth * (bmp->height - 1); - end = data + bytes; - addr = ((intptr_t)data) & 3; - bmp->decoded = true; - - /* Determine transparent index */ - if (bmp->limited_trans) { - if ((data + 2) > end) - return BMP_INSUFFICIENT_DATA; - bmp->transparent_index = read_uint16(data, 0); - } - - for (y = 0; y < bmp->height; y++) { - if ((data + (2 * bmp->width)) > end) - return BMP_INSUFFICIENT_DATA; - if (bmp->reversed) - scanline = (void *)(top + (y * swidth)); - else - scanline = (void *)(bottom - (y * swidth)); - if (bmp->encoding == BMP_ENCODING_BITFIELDS) { - for (x = 0; x < bmp->width; x++) { - word = read_uint16(data, 0); - if ((bmp->limited_trans) && (word == bmp->transparent_index)) - scanline[x] = bmp->trans_colour; - else { - scanline[x] = 0; - for (i = 0; i < 4; i++) - if (bmp->shift[i] > 0) - scanline[x] |= ((word & bmp->mask[i]) << bmp->shift[i]); - else - scanline[x] |= ((word & bmp->mask[i]) >> (-bmp->shift[i])); - if (bmp->opaque) - scanline[x] |= (0xff << 24); - } - data += 2; - scanline[x] = read_uint32((uint8_t *)&scanline[x],0); - } - } else { - for (x = 0; x < bmp->width; x++) { - word = read_uint16(data, 0); - if ((bmp->limited_trans) && (word == bmp->transparent_index)) - scanline[x] = bmp->trans_colour; - else { - /* 16-bit RGB defaults to RGB555 */ - scanline[x] = ((word & (31 << 0)) << 19) | - ((word & (31 << 5)) << 6) | - ((word & (31 << 10)) >> 7); - } - if (bmp->opaque) - scanline[x] |= (0xff << 24); - data += 2; - scanline[x] = read_uint32((uint8_t *)&scanline[x],0); - } - } - while (addr != (((intptr_t)data) & 3)) - data += 2; - } - *start = data; - return BMP_OK; -} - - -/** - * Decode BMP data stored with a palette and in 8bpp colour or less. - * - * \param bmp the BMP image to decode - * \param start the data to decode, updated to last byte read on success - * \param bytes the number of bytes of data available - * \return BMP_OK on success - * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; - * in this case, the image may be partially viewable - */ -static bmp_result bmp_decode_rgb(bmp_image *bmp, uint8_t **start, int bytes) -{ - uint8_t *top, *bottom, *end, *data; - uint32_t *scanline; - intptr_t addr; - uint32_t x, y, swidth; - uint8_t bit_shifts[8]; - uint8_t ppb = 8 / bmp->bpp; - uint8_t bit_mask = (1 << bmp->bpp) - 1; - uint8_t cur_byte = 0, bit, i; - - for (i = 0; i < ppb; i++) - bit_shifts[i] = 8 - ((i + 1) * bmp->bpp); - - data = *start; - swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; - top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); - if (!top) - return BMP_INSUFFICIENT_MEMORY; - bottom = top + (uint64_t)swidth * (bmp->height - 1); - end = data + bytes; - addr = ((intptr_t)data) & 3; - bmp->decoded = true; - - /* Determine transparent index */ - if (bmp->limited_trans) { - uint32_t idx = (*data >> bit_shifts[0]) & bit_mask; - if (idx >= bmp->colours) - return BMP_DATA_ERROR; - bmp->transparent_index = bmp->colour_table[idx]; - } - - for (y = 0; y < bmp->height; y++) { - bit = 8; - if ((data + (bmp->width / ppb)) > end) - return BMP_INSUFFICIENT_DATA; - if (bmp->reversed) - scanline = (void *)(top + (y * swidth)); - else - scanline = (void *)(bottom - (y * swidth)); - for (x = 0; x < bmp->width; x++) { - uint32_t idx; - if (bit >= ppb) { - bit = 0; - cur_byte = *data++; - } - idx = (cur_byte >> bit_shifts[bit++]) & bit_mask; - if (idx < bmp->colours) { - /* ensure colour table index is in bounds */ - scanline[x] = bmp->colour_table[idx]; - if ((bmp->limited_trans) && - (scanline[x] == bmp->transparent_index)) { - scanline[x] = bmp->trans_colour; - } - } - } - while (addr != (((intptr_t)data) & 3)) - data++; - } - *start = data; - return BMP_OK; -} - - -/** - * Decode a 1bpp mask for an ICO - * - * \param bmp the BMP image to decode - * \param data the data to decode - * \param bytes the number of bytes of data available - * \return BMP_OK on success - */ -static bmp_result bmp_decode_mask(bmp_image *bmp, uint8_t *data, int bytes) -{ - uint8_t *top, *bottom, *end; - uint32_t *scanline; - intptr_t addr; - uint32_t x, y, swidth; - uint32_t cur_byte = 0; - - swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; - top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); - if (!top) - return BMP_INSUFFICIENT_MEMORY; - bottom = top + (uint64_t)swidth * (bmp->height - 1); - end = data + bytes; - - addr = ((intptr_t)data) & 3; - - for (y = 0; y < bmp->height; y++) { - if ((data + (bmp->width >> 3)) > end) - return BMP_INSUFFICIENT_DATA; - scanline = (void *)(bottom - (y * swidth)); - for (x = 0; x < bmp->width; x++) { - if ((x & 7) == 0) - cur_byte = *data++; - scanline[x] = read_uint32((uint8_t *)&scanline[x], 0); - if ((cur_byte & 128) == 0) { - scanline[x] |= (0xff << 24); - } else { - scanline[x] &= 0xffffff; - } - scanline[x] = read_uint32((uint8_t *)&scanline[x], 0); - cur_byte = cur_byte << 1; - } - while (addr != (((intptr_t)data) & 3)) - data++; - } - return BMP_OK; -} - - -/** - * Decode BMP data stored encoded in RLE8. - * - * \param bmp the BMP image to decode - * \param data the data to decode - * \param bytes the number of bytes of data available - * \return BMP_OK on success - * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; - * in this case, the image may be partially viewable - */ -static bmp_result -bmp_decode_rle8(bmp_image *bmp, uint8_t *data, int bytes) -{ - uint8_t *top, *bottom, *end; - uint32_t *scanline; - uint32_t swidth; - uint32_t i, length, pixels_left; - uint32_t x = 0, y = 0, last_y = 0; - uint32_t pixel = 0; - - if (bmp->ico) - return BMP_DATA_ERROR; - - swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; - top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); - if (!top) - return BMP_INSUFFICIENT_MEMORY; - bottom = top + (uint64_t)swidth * (bmp->height - 1); - end = data + bytes; - bmp->decoded = true; - - do { - if (data + 2 > end) - return BMP_INSUFFICIENT_DATA; - length = *data++; - if (length == 0) { - length = *data++; - switch (length) { - case 0: - /* 00 - 00 means end of scanline */ - x = 0; - if (last_y == y) { - y++; - if (y >= bmp->height) - return BMP_DATA_ERROR; - } - last_y = y; - break; - - case 1: - /* 00 - 01 means end of RLE data */ - return BMP_OK; - - case 2: - /* 00 - 02 - XX - YY means move cursor */ - if (data + 2 > end) - return BMP_INSUFFICIENT_DATA; - x += *data++; - if (x >= bmp->width) - return BMP_DATA_ERROR; - y += *data++; - if (y >= bmp->height) - return BMP_DATA_ERROR; - break; - - default: - /* 00 - NN means escape NN pixels */ - if (bmp->reversed) { - pixels_left = (bmp->height - y) * bmp->width - x; - scanline = (void *)(top + (y * swidth)); - } else { - pixels_left = (y + 1) * bmp->width - x; - scanline = (void *)(bottom - (y * swidth)); - } - if (length > pixels_left) - length = pixels_left; - if (data + length > end) - return BMP_INSUFFICIENT_DATA; - - /* the following code could be easily optimised - * by simply checking the bounds on entry and - * using some simple copying routines if so - */ - for (i = 0; i < length; i++) { - uint32_t idx = (uint32_t) *data++; - if (x >= bmp->width) { - x = 0; - y++; - if (y >= bmp->height) - return BMP_DATA_ERROR; - if (bmp->reversed) { - scanline += bmp->width; - } else { - scanline -= bmp->width; - } - } - if (idx >= bmp->colours) - return BMP_DATA_ERROR; - scanline[x++] = bmp->colour_table[idx]; - } - - if ((length & 1) && (*data++ != 0x00)) - return BMP_DATA_ERROR; - - break; - } - } else { - uint32_t idx; - - /* NN means perform RLE for NN pixels */ - if (bmp->reversed) { - pixels_left = (bmp->height - y) * bmp->width - x; - scanline = (void *)(top + (y * swidth)); - } else { - pixels_left = (y + 1) * bmp->width - x; - scanline = (void *)(bottom - (y * swidth)); - } - if (length > pixels_left) - length = pixels_left; - - /* boundary checking */ - if (data + 1 > end) - return BMP_INSUFFICIENT_DATA; - - /* the following code could be easily optimised by - * simply checking the bounds on entry and using some - * simply copying routines if so - */ - idx = (uint32_t) *data++; - if (idx >= bmp->colours) - return BMP_DATA_ERROR; - - pixel = bmp->colour_table[idx]; - for (i = 0; i < length; i++) { - if (x >= bmp->width) { - x = 0; - y++; - if (y >= bmp->height) - return BMP_DATA_ERROR; - if (bmp->reversed) { - scanline += bmp->width; - } else { - scanline -= bmp->width; - } - } - scanline[x++] = pixel; - } - } - } while (data < end); - - return BMP_OK; -} - - -/** - * Decode BMP data stored encoded in RLE4. - * - * \param bmp the BMP image to decode - * \param data the data to decode - * \param bytes the number of bytes of data available - * \return BMP_OK on success - * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; - * in this case, the image may be partially viewable - */ -static bmp_result -bmp_decode_rle4(bmp_image *bmp, uint8_t *data, int bytes) -{ - uint8_t *top, *bottom, *end; - uint32_t *scanline; - uint32_t swidth; - uint32_t i, length, pixels_left; - uint32_t x = 0, y = 0, last_y = 0; - uint32_t pixel = 0, pixel2; - - if (bmp->ico) - return BMP_DATA_ERROR; - - swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; - top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); - if (!top) - return BMP_INSUFFICIENT_MEMORY; - bottom = top + (uint64_t)swidth * (bmp->height - 1); - end = data + bytes; - bmp->decoded = true; - - do { - if (data + 2 > end) - return BMP_INSUFFICIENT_DATA; - length = *data++; - if (length == 0) { - length = *data++; - switch (length) { - case 0: - /* 00 - 00 means end of scanline */ - x = 0; - if (last_y == y) { - y++; - if (y >= bmp->height) - return BMP_DATA_ERROR; - } - last_y = y; - break; - - case 1: - /* 00 - 01 means end of RLE data */ - return BMP_OK; - - case 2: - /* 00 - 02 - XX - YY means move cursor */ - if (data + 2 > end) - return BMP_INSUFFICIENT_DATA; - x += *data++; - if (x >= bmp->width) - return BMP_DATA_ERROR; - y += *data++; - if (y >= bmp->height) - return BMP_DATA_ERROR; - break; - - default: - /* 00 - NN means escape NN pixels */ - if (bmp->reversed) { - pixels_left = (bmp->height - y) * bmp->width - x; - scanline = (void *)(top + (y * swidth)); - } else { - pixels_left = (y + 1) * bmp->width - x; - scanline = (void *)(bottom - (y * swidth)); - } - if (length > pixels_left) - length = pixels_left; - if (data + ((length + 1) / 2) > end) - return BMP_INSUFFICIENT_DATA; - - /* the following code could be easily optimised - * by simply checking the bounds on entry and - * using some simple copying routines - */ - - for (i = 0; i < length; i++) { - if (x >= bmp->width) { - x = 0; - y++; - if (y >= bmp->height) - return BMP_DATA_ERROR; - if (bmp->reversed) { - scanline += bmp->width; - } else { - scanline -= bmp->width; - } - - } - if ((i & 1) == 0) { - pixel = *data++; - if ((pixel >> 4) >= bmp->colours) - return BMP_DATA_ERROR; - scanline[x++] = bmp->colour_table - [pixel >> 4]; - } else { - if ((pixel & 0xf) >= bmp->colours) - return BMP_DATA_ERROR; - scanline[x++] = bmp->colour_table - [pixel & 0xf]; - } - } - length = (length + 1) >> 1; - - if ((length & 1) && (*data++ != 0x00)) - return BMP_DATA_ERROR; - - break; - } - } else { - /* NN means perform RLE for NN pixels */ - if (bmp->reversed) { - pixels_left = (bmp->height - y) * bmp->width - x; - scanline = (void *)(top + (y * swidth)); - } else { - pixels_left = (y + 1) * bmp->width - x; - scanline = (void *)(bottom - (y * swidth)); - } - if (length > pixels_left) - length = pixels_left; - - /* boundary checking */ - if (data + 1 > end) - return BMP_INSUFFICIENT_DATA; - - /* the following code could be easily optimised by - * simply checking the bounds on entry and using some - * simple copying routines - */ - - pixel2 = *data++; - if ((pixel2 >> 4) >= bmp->colours || - (pixel2 & 0xf) >= bmp->colours) - return BMP_DATA_ERROR; - pixel = bmp->colour_table[pixel2 >> 4]; - pixel2 = bmp->colour_table[pixel2 & 0xf]; - for (i = 0; i < length; i++) { - if (x >= bmp->width) { - x = 0; - y++; - if (y >= bmp->height) - return BMP_DATA_ERROR; - if (bmp->reversed) { - scanline += bmp->width; - } else { - scanline -= bmp->width; - } - } - if ((i & 1) == 0) - scanline[x++] = pixel; - else - scanline[x++] = pixel2; - } - - } - } while (data < end); - - return BMP_OK; -} - - -/* exported interface documented in libnsbmp.h */ -bmp_result -bmp_create(bmp_image *bmp, - bmp_bitmap_callback_vt *bitmap_callbacks) -{ - memset(bmp, 0, sizeof(bmp_image)); - bmp->bitmap_callbacks = *bitmap_callbacks; - - return BMP_OK; -} - - -/* exported interface documented in libnsbmp.h */ -bmp_result -ico_collection_create(ico_collection *ico, - bmp_bitmap_callback_vt *bitmap_callbacks) -{ - - memset(ico, 0, sizeof(ico_collection)); - ico->bitmap_callbacks = *bitmap_callbacks; - - return BMP_OK; -} - - -/* exported interface documented in libnsbmp.h */ -bmp_result bmp_analyse(bmp_image *bmp, size_t size, uint8_t *data) -{ - bmp_result res; - - /* ensure we aren't already initialised */ - if (bmp->bitmap) { - return BMP_OK; - } - - /* initialize source data values */ - bmp->buffer_size = size; - bmp->bmp_data = data; - - res = bmp_file_header_parse(bmp, data); - if (res == BMP_OK) { - res = bmp_info_header_parse(bmp, data + BMP_FILE_HEADER_SIZE); - } - return res; -} - - -/* exported interface documented in libnsbmp.h */ -bmp_result ico_analyse(ico_collection *ico, size_t size, uint8_t *data) -{ - /* ensure we aren't already initialised */ - if (ico->first) - return BMP_OK; - - /* initialize values */ - ico->buffer_size = size; - ico->ico_data = data; - - return ico_header_parse(ico, data); -} - - -/* exported interface documented in libnsbmp.h */ -bmp_result bmp_decode(bmp_image *bmp) -{ - uint8_t *data; - uint32_t bytes; - bmp_result result = BMP_OK; - - assert(bmp->bitmap); - - data = bmp->bmp_data + bmp->bitmap_offset; - bytes = bmp->buffer_size - bmp->bitmap_offset; - - switch (bmp->encoding) { - case BMP_ENCODING_RGB: - switch (bmp->bpp) { - case 32: - result = bmp_decode_rgb32(bmp, &data, bytes); - break; - - case 24: - result = bmp_decode_rgb24(bmp, &data, bytes); - break; - - case 16: - result = bmp_decode_rgb16(bmp, &data, bytes); - break; - - default: - result = bmp_decode_rgb(bmp, &data, bytes); - break; - } - break; - - case BMP_ENCODING_RLE8: - result = bmp_decode_rle8(bmp, data, bytes); - break; - - case BMP_ENCODING_RLE4: - result = bmp_decode_rle4(bmp, data, bytes); - break; - - case BMP_ENCODING_BITFIELDS: - switch (bmp->bpp) { - case 32: - result = bmp_decode_rgb32(bmp, &data, bytes); - break; - - case 16: - result = bmp_decode_rgb16(bmp, &data, bytes); - break; - - default: - result = BMP_DATA_ERROR; - break; - } - break; - } - - /* icons with less than 32bpp have a 1bpp alpha mask */ - if ((result == BMP_OK) && (bmp->ico) && (bmp->bpp != 32)) { - bytes = (uintptr_t)bmp->bmp_data + bmp->buffer_size - (uintptr_t)data; - result = bmp_decode_mask(bmp, data, bytes); - } - return result; -} - - -/* exported interface documented in libnsbmp.h */ -bmp_result bmp_decode_trans(bmp_image *bmp, uint32_t colour) -{ - bmp->limited_trans = true; - bmp->trans_colour = colour; - return bmp_decode(bmp); -} - - -/* exported interface documented in libnsbmp.h */ -bmp_image *ico_find(ico_collection *ico, uint16_t width, uint16_t height) -{ - bmp_image *bmp = NULL; - ico_image *image; - int x, y, cur, distance = (1 << 24); - - if (width == 0) - width = ico->width; - if (height == 0) - height = ico->height; - for (image = ico->first; image; image = image->next) { - if ((image->bmp.width == width) && (image->bmp.height == height)) - return &image->bmp; - x = image->bmp.width - width; - y = image->bmp.height - height; - cur = (x * x) + (y * y); - if (cur < distance) { - distance = cur; - bmp = &image->bmp; - } - } - return bmp; -} - - -/* exported interface documented in libnsbmp.h */ -void bmp_finalise(bmp_image *bmp) -{ - if (bmp->bitmap) - bmp->bitmap_callbacks.bitmap_destroy(bmp->bitmap); - bmp->bitmap = NULL; - if (bmp->colour_table) - free(bmp->colour_table); - bmp->colour_table = NULL; -} - - -/* exported interface documented in libnsbmp.h */ -void ico_finalise(ico_collection *ico) -{ - ico_image *image; - - for (image = ico->first; image; image = image->next) - bmp_finalise(&image->bmp); - while (ico->first) { - image = ico->first; - ico->first = image->next; - free(image); - } -} diff --git a/source/menus/menu_gallery.c b/source/menus/menu_gallery.c index cb6f06b..1af82f3 100644 --- a/source/menus/menu_gallery.c +++ b/source/menus/menu_gallery.c @@ -7,7 +7,9 @@ #include "fs.h" #include "menu_gallery.h" -#include "libnsbmp.h" +#define LOADBMP_IMPLEMENTATION +#include "loadbmp.h" +#undef LOADBMP_IMPLEMENTATION #include "lodepng.h" #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" @@ -25,8 +27,8 @@ #define DIMENSION_OTHER 4 static char album[512][512]; -static int count = 0, selection = 0; -C2D_Image *image; +static int count = 0, selection = 0, dimensions = 0; +C2D_Image image; // Thanks to LiquidFenrir static u32 Gallery_GetNextPowerOf2(u32 v) // from pp2d @@ -41,66 +43,15 @@ static u32 Gallery_GetNextPowerOf2(u32 v) // from pp2d return v >= 64 ? v : 64; } -static void *Gallery_CreateBitmap(int width, int height, unsigned int state) -{ - (void) state; /* unused */ - return calloc(width * height, 4); -} - -static unsigned char *Gallery_GetBitmapBuf(void *bitmap) -{ - return (unsigned char *)bitmap; -} - -static size_t Gallery_GetBitmapBPP(void *bitmap) -{ - (void) bitmap; /* unused */ - return 4; -} - -void Gallery_FreeBitmap(void *bitmap) -{ - free(bitmap); -} - -static void *Gallery_BitmapToBuf(const char *path, u32 *size) -{ - FILE *fd = fopen(path, "rb"); - - if (fd == NULL) - return NULL; - - u8 *buffer; - long long_size; - fseek(fd, 0, SEEK_END); - long_size = ftell(fd); - rewind(fd); - - buffer = (u8 *)malloc(long_size); - - if (size) - *size = long_size; - - if (!buffer) - { - fclose(fd); - return NULL; - } - - fread(buffer, 1, long_size, fd); - fclose(fd); - return buffer; -} - // Thanks to LiquidFenrir -static C2D_Image *Gallery_LoadImage(const char *path) +static C2D_Image Gallery_LoadImage(const char *path) { u32* outBuf = NULL; u32 size = 0; int width = 0, height = 0; GPU_TEXCOLOR format; - char extension[EXTENSION_SIZE+1] = {0}; + char extension[EXTENSION_SIZE + 1] = {0}; strncpy(extension, &path[strlen(path)-EXTENSION_SIZE], EXTENSION_SIZE); if (!strncasecmp(extension, ".png", EXTENSION_SIZE)) @@ -136,9 +87,6 @@ static C2D_Image *Gallery_LoadImage(const char *path) int channel = 0; stbi_uc *texture = stbi_load(path, &width, &height, &channel, STBI_rgb); - if ((texture == NULL) || (channel != STBI_rgb)) - return NULL; - for (u32 x = 0; x < width; x++) { for (u32 y = 0; y < height; y++) @@ -164,103 +112,53 @@ static C2D_Image *Gallery_LoadImage(const char *path) } else if (!strncasecmp(extension, ".bmp", EXTENSION_SIZE)) { - u32 size; - u8 *buf = (u8 *)Gallery_BitmapToBuf(path, &size); - - bmp_bitmap_callback_vt bitmap_callbacks = - { - Gallery_CreateBitmap, - Gallery_FreeBitmap, - Gallery_GetBitmapBuf, - Gallery_GetBitmapBPP - }; - - bmp_result code; - bmp_image bmp; - - /* create our bmp image */ - bmp_create(&bmp, &bitmap_callbacks); - - /* analyse the BMP */ - code = bmp_analyse(&bmp, size, buf); - - if (code != BMP_OK) - { - bmp_finalise(&bmp); - return NULL; - } - - /* decode the image */ - code = bmp_decode(&bmp); - - if (code != BMP_OK) - { - bmp_finalise(&bmp); - return NULL; - } - - u8 *texture; - texture = (u8 *)bmp.bitmap; - - for (u32 x = 0; x < bmp.width; x++) + u8* texture = NULL; + loadbmp_decode_file(path, &texture, &width, &height, LOADBMP_RGBA); + + for (u32 i = 0; i < width; i++) { - for (u32 y = 0; y < bmp.height; y++) + for (u32 j = 0; j < height; j++) { - u32 pos = (y * bmp.width + x) * 4; - u8 c1 = texture[pos + 0]; - u8 c2 = texture[pos + 1]; - u8 c3 = texture[pos + 2]; - u8 c4 = texture[pos + 3]; + u32 p = (i + j*width) * 4; + + u8 r = *(u8*)(texture + p); + u8 g = *(u8*)(texture + p + 1); + u8 b = *(u8*)(texture + p + 2); + u8 a = *(u8*)(texture + p + 3); - texture[pos + 0] = c4; - texture[pos + 1] = c3; - texture[pos + 2] = c2; - texture[pos + 3] = c1; + *(texture + p) = a; + *(texture + p + 1) = b; + *(texture + p + 2) = g; + *(texture + p + 3) = r; } } - - size = (bmp.width * bmp.height * 4); + + size = (u32)(width * height * 4); outBuf = (u32 *)malloc(size); memcpy(outBuf, texture, size); - bmp_finalise(&bmp); format = GPU_RGBA8; } - else - return NULL; if (outBuf) { - C2D_Image *image = malloc(sizeof(C2D_Image)); - if (image == NULL) - { - free(outBuf); - return NULL; - } - image->tex = malloc(sizeof(C3D_Tex)); + C3D_Tex *tex = malloc(sizeof(C3D_Tex)); + Tex3DS_SubTexture *subtex = malloc(sizeof(Tex3DS_SubTexture)); u32 w_pow2 = Gallery_GetNextPowerOf2((u32)width); u32 h_pow2 = Gallery_GetNextPowerOf2((u32)height); - Tex3DS_SubTexture *subt3x = malloc(sizeof(Tex3DS_SubTexture)); - if (subt3x == NULL) - { - free(image); - free(outBuf); - return NULL; - } - subt3x->width = width; - subt3x->height = height; - subt3x->left = 0.0f; - subt3x->top = 1.0f; - subt3x->right = width/(float)w_pow2; - subt3x->bottom = 1.0 - (height/(float)h_pow2); - image->subtex = subt3x; + subtex->width = width; + subtex->height = height; + subtex->left = 0.0f; + subtex->top = 1.0f; + subtex->right = width/(float)w_pow2; + subtex->bottom = 1.0 - (height/(float)h_pow2); - C3D_TexInit(image->tex, (u16)w_pow2, (u16)h_pow2, format); - C3D_TexSetFilter(image->tex, GPU_LINEAR, GPU_LINEAR); + C3D_TexInit(tex, (u16)w_pow2, (u16)h_pow2, format); + C3D_TexSetFilter(tex, GPU_LINEAR, GPU_LINEAR); - memset(image->tex->data, 0, image->tex->size); + memset(tex->data, 0, tex->size); u32 pixelSize = size / width / height; @@ -271,32 +169,43 @@ static C2D_Image *Gallery_LoadImage(const char *path) u32 dstPos = ((((y >> 3) * (w_pow2 >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3))) * pixelSize; u32 srcPos = (y * width + x) * pixelSize; - memcpy(&((u8 *)image->tex->data)[dstPos], &((u8 *) outBuf)[srcPos], pixelSize); + memcpy(&((u8 *)tex->data)[dstPos], &((u8 *) outBuf)[srcPos], pixelSize); } } - C3D_TexFlush(image->tex); + C3D_TexFlush(tex); free(outBuf); - image->tex->border = 0xFFFFFFFF; - C3D_TexSetWrap(image->tex, GPU_CLAMP_TO_BORDER, GPU_CLAMP_TO_BORDER); + tex->border = 0xFFFFFFFF; + C3D_TexSetWrap(tex, GPU_CLAMP_TO_BORDER, GPU_CLAMP_TO_BORDER); - return image; - } + if ((subtex->width <= 400) && (subtex->height <= 240)) + DIMENSION_DEFAULT; + else if ((subtex->width == 432) && (subtex->height == 528)) // Nintnedo's screenshot (both screens) dimensions. + dimensions = DIMENSION_NINTENDO_SCREENSHOT; + else if ((subtex->width == 640) && (subtex->height == 480)) // Nintnedo's CAM dimensions. + dimensions = DIMENSION_NINTENDO_PICTURE; + else if ((subtex->width == 400) && ((subtex->height == 480) || (subtex->height == 482))) + dimensions = DIMENSION_3DSHELL_SCREENSHOT; + else if ((subtex->width > 400) && (subtex->height > 240)) + dimensions = DIMENSION_OTHER; + + C2D_Image img; + img.tex = tex; + img.subtex = subtex; - return NULL; + return img; + } } // Thanks to LiquidFenrir static void Gallery_FreeImage(C2D_Image *image) { - if (image) - { - C3D_TexDelete(image->tex); - free(image->tex); - free((Tex3DS_SubTexture *)image->subtex); - free(image); - } + dimensions = 0; + C3D_TexDelete(image->tex); + free(image->tex); + free((Tex3DS_SubTexture *)image->subtex); + //free(image); } static bool Gallery_DrawImage(C2D_Image image, float x, float y, float start, float end) @@ -332,7 +241,7 @@ static Result Gallery_GetImageList(void) int length = strlen(name); if ((strncasecmp(entries[i].shortExt, "png", 3) == 0) || (strncasecmp(entries[i].shortExt, "jpg", 3) == 0) - || (strncasecmp(entries[i].shortExt, "bmp", 3) == 0) || (strncasecmp(entries[i].shortExt, "gif", 3) == 0)) + || (strncasecmp(entries[i].shortExt, "bmp", 3) == 0)) { strcpy(album[count], cwd); strcpy(album[count] + strlen(album[count]), name); @@ -364,6 +273,14 @@ static int Gallery_GetCurrentIndex(char *path) } } +static void Gallery_LoadTexture(char *path) +{ + Gallery_GetImageList(); + selection = Gallery_GetCurrentIndex(path); + + image = Gallery_LoadImage(path); +} + static void Gallery_HandleNext(bool forward) { if (forward) @@ -374,22 +291,15 @@ static void Gallery_HandleNext(bool forward) Utils_SetMax(&selection, 0, (count - 1)); Utils_SetMin(&selection, (count - 1), 0); - Gallery_FreeImage(image); - - Gallery_GetImageList(); - selection = Gallery_GetCurrentIndex(album[selection]); + Gallery_FreeImage(&image); - image = Gallery_LoadImage(album[selection]); + wait(1); + Gallery_LoadTexture(album[selection]); } void Gallery_DisplayImage(char *path) { - image = Gallery_LoadImage(path); - - Gallery_GetImageList(); - selection = Gallery_GetCurrentIndex(path); - - int dimensions = 0; + Gallery_LoadTexture(path); while(aptMainLoop()) { @@ -398,74 +308,63 @@ void Gallery_DisplayImage(char *path) C2D_TargetClear(RENDER_BOTTOM, C2D_Color32(33, 39, 43, 255)); C2D_SceneBegin(RENDER_TOP); - if ((image->subtex->width <= 400) && (image->subtex->height <= 240)) - DIMENSION_DEFAULT; - else if ((image->subtex->width == 432) && (image->subtex->height == 528)) // Nintnedo's screenshot (both screens) dimensions. - dimensions = DIMENSION_NINTENDO_SCREENSHOT; - else if ((image->subtex->width == 640) && (image->subtex->height == 480)) // Nintnedo's CAM dimensions. - dimensions = DIMENSION_NINTENDO_PICTURE; - else if ((image->subtex->width == 400) && ((image->subtex->height == 480) || (image->subtex->height == 482))) - dimensions = DIMENSION_3DSHELL_SCREENSHOT; - else if ((image->subtex->width > 400) && (image->subtex->height > 240)) - dimensions = DIMENSION_OTHER; - switch (dimensions) { case DIMENSION_DEFAULT: - Draw_Image(*image, ((400.0f - image->subtex->width) / 2.0f), ((240.0f - image->subtex->height) / 2.0f)); + Draw_Image(image, ((400.0f - image.subtex->width) / 2.0f), ((240.0f - image.subtex->height) / 2.0f)); break; case DIMENSION_NINTENDO_SCREENSHOT: - Gallery_DrawImage(*image, 0, 0, 16, 16); + Gallery_DrawImage(image, 0, 0, 16, 16); break; case DIMENSION_NINTENDO_PICTURE: - Draw_ImageScale(*image, 40, 0, 0.5, 0.5); + Draw_ImageScale(image, 40, 0, 0.5, 0.5); break; case DIMENSION_3DSHELL_SCREENSHOT: - Gallery_DrawImage(*image, 0, 0, 0, 0); + Gallery_DrawImage(image, 0, 0, 0, 0); break; case DIMENSION_OTHER: - Draw_ImageScale(*image, 0, 0, 400.0f / image->subtex->width, 240.0f / image->subtex->height); + Draw_ImageScale(image, 0, 0, 400.0f / image.subtex->width, 240.0f / image.subtex->height); break; } - hidScanInput(); - u32 kDown = hidKeysDown(); - - if ((kDown & KEY_LEFT) || (kDown & KEY_L)) - { - wait(1); - Gallery_HandleNext(false); - } - else if ((kDown & KEY_RIGHT) || (kDown & KEY_R)) - { - wait(1); - Gallery_HandleNext(true); - } - C2D_SceneBegin(RENDER_BOTTOM); switch (dimensions) { case DIMENSION_NINTENDO_SCREENSHOT: - Gallery_DrawImage(*image, 0, 0, 56, 272); + Gallery_DrawImage(image, 0, 0, 56, 272); break; case DIMENSION_3DSHELL_SCREENSHOT: - Gallery_DrawImage(*image, 0, 0, 40, 240); + Gallery_DrawImage(image, 0, 0, 40, 240); break; } Draw_EndFrame(); + + hidScanInput(); + u32 kDown = hidKeysDown(); + + if ((kDown & KEY_LEFT) || (kDown & KEY_L)) + { + wait(1); + Gallery_HandleNext(false); + } + else if ((kDown & KEY_RIGHT) || (kDown & KEY_R)) + { + wait(1); + Gallery_HandleNext(true); + } if (kDown & KEY_B) break; } - Gallery_FreeImage(image); + Gallery_FreeImage(&image); memset(album, 0, sizeof(album[0][0]) * 512 * 512); count = 0; MENU_STATE = MENU_STATE_HOME; From 4632b0a25c80be6b6ba4debe3765309f6cda41a4 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Tue, 17 Jul 2018 18:22:04 -0500 Subject: [PATCH 18/33] Attempt to fix travis builds again --- .travis.yml | 34 +++++++++++++++++++++++++--------- .travis/build.sh | 2 +- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index bf5b733..8305d65 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,21 +1,37 @@ language: c -sudo: required +sudo: true -services: docker +#Cache devkitARM and doxygen +cache: + directories: + - /home/travis/devkitPro before_install: -- cd include/dr_libs/ -- git clone https://github.com/mackron/dr_libs.git -- cd ../.. + - cd include/dr_libs/ + - git clone https://github.com/mackron/dr_libs.git + - cd ../.. + - wget https://github.com/devkitPro/pacman/releases/download/v1.0.0/devkitpro-pacman.deb + - sudo dpkg -i devkitpro-pacman.deb install: -- docker pull devkitpro/devkitarm + - sudo dkp-pacman -Syu 3ds-dev --noconfirm --needed + - sudo dkp-pacman -S 3ds-libogg --noconfirm + - sudo dkp-pacman -S 3ds-libvorbisidec --noconfirm + - sudo dkp-pacman -S 3ds-mbedtls --noconfirm + - sudo dkp-pacman -S 3ds-mpg123 --noconfirm + - source /etc/profile.d/devkit-env.sh + - git clone --recursive https://github.com/Steveice10/bannertool && cd bannertool/ + - make && cp output/linux-x86_64/bannertool /opt/devkitpro/devkitARM/bin/bannertool + - cd ../ && rm -rf bannertool/ + - git clone https://github.com/profi200/Project_CTR.git && cd Project_CTR/makerom + - make && cp makerom /opt/devkitpro/devkitARM/bin/makerom + - cd ../.. && rm -rf Project_CTR script: -- docker run -e ENABLE_COMPATIBILITY_REPORTING -v $TRAVIS_BUILD_DIR:/3DShell - devkitpro/devkitarm /bin/bash -ex /3DShell/.travis/build.sh - + - cd $TRAVIS_BUILD_DIR + - sh .travis/.build.sh + deploy: provider: pages skip_cleanup: true diff --git a/.travis/build.sh b/.travis/build.sh index bd6e436..9a16e15 100644 --- a/.travis/build.sh +++ b/.travis/build.sh @@ -3,5 +3,5 @@ source /etc/profile.d/devkit-env.sh cd /3DShell make clean && make -rm .gitignore .gitmodules .travis.yml 3DShell.elf 3DShell.smdh LICENSE Makefile README.md +rm .gitignore .gitmodules .travis.yml 3DShell.elf 3DShell.smdh devkitpro-pacman.deb LICENSE Makefile README.md rm -rf .travis build include res romfs source \ No newline at end of file From 5a0cdcc3b9ed040861d4c62f9aafaf519c74425c Mon Sep 17 00:00:00 2001 From: Joel16 Date: Tue, 17 Jul 2018 18:24:19 -0500 Subject: [PATCH 19/33] Remove mbedtls --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8305d65..aec3163 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,6 @@ install: - sudo dkp-pacman -Syu 3ds-dev --noconfirm --needed - sudo dkp-pacman -S 3ds-libogg --noconfirm - sudo dkp-pacman -S 3ds-libvorbisidec --noconfirm - - sudo dkp-pacman -S 3ds-mbedtls --noconfirm - sudo dkp-pacman -S 3ds-mpg123 --noconfirm - source /etc/profile.d/devkit-env.sh - git clone --recursive https://github.com/Steveice10/bannertool && cd bannertool/ From d8ffeac1fca21541879ac8a066cb65961f1d0be2 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Tue, 17 Jul 2018 18:26:35 -0500 Subject: [PATCH 20/33] Give makerom and bannertool root permissions --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index aec3163..977d705 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,10 +21,10 @@ install: - sudo dkp-pacman -S 3ds-mpg123 --noconfirm - source /etc/profile.d/devkit-env.sh - git clone --recursive https://github.com/Steveice10/bannertool && cd bannertool/ - - make && cp output/linux-x86_64/bannertool /opt/devkitpro/devkitARM/bin/bannertool + - make && sudo cp output/linux-x86_64/bannertool /opt/devkitpro/devkitARM/bin/bannertool - cd ../ && rm -rf bannertool/ - git clone https://github.com/profi200/Project_CTR.git && cd Project_CTR/makerom - - make && cp makerom /opt/devkitpro/devkitARM/bin/makerom + - make && sudo cp makerom /opt/devkitpro/devkitARM/bin/makerom - cd ../.. && rm -rf Project_CTR script: From f50fb4ba39722ec9dd0da7fc41a0bfcd1e04d335 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Tue, 17 Jul 2018 18:30:47 -0500 Subject: [PATCH 21/33] Get rid of the . in front of build.sh --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 977d705..9fdb981 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,7 @@ install: script: - cd $TRAVIS_BUILD_DIR - - sh .travis/.build.sh + - sh .travis/build.sh deploy: provider: pages From 40be7e6d168013ac1de289504e2f219464512bc0 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Tue, 17 Jul 2018 18:34:29 -0500 Subject: [PATCH 22/33] Export CC and CXX --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 9fdb981..4a25821 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,8 @@ install: - sudo dkp-pacman -S 3ds-libvorbisidec --noconfirm - sudo dkp-pacman -S 3ds-mpg123 --noconfirm - source /etc/profile.d/devkit-env.sh + - export CC=arm-none-eabi-gcc + - export CXX=arm-none-eabi-g++ - git clone --recursive https://github.com/Steveice10/bannertool && cd bannertool/ - make && sudo cp output/linux-x86_64/bannertool /opt/devkitpro/devkitARM/bin/bannertool - cd ../ && rm -rf bannertool/ From 97d051e895686ec88f918d4e14d73447759e2643 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Tue, 17 Jul 2018 18:39:12 -0500 Subject: [PATCH 23/33] Upgrade first before installing --- .travis.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4a25821..ec37501 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,12 +2,8 @@ language: c sudo: true -#Cache devkitARM and doxygen -cache: - directories: - - /home/travis/devkitPro - before_install: + - sudo apt-get dist-upgrade - cd include/dr_libs/ - git clone https://github.com/mackron/dr_libs.git - cd ../.. From e999a2af1fd6caf82140092d5449a2dda3cf6cae Mon Sep 17 00:00:00 2001 From: Joel16 Date: Tue, 17 Jul 2018 18:41:24 -0500 Subject: [PATCH 24/33] Properly upgrade before installing --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index ec37501..93b6a22 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,9 @@ language: c sudo: true before_install: + - sudo add-apt-repository ppa:ubuntu-toolchain-r/test + - sudo apt-get update + - sudo apt-get upgrade - sudo apt-get dist-upgrade - cd include/dr_libs/ - git clone https://github.com/mackron/dr_libs.git From 47759549a6c58e397a01b874bd0bebf486e5bac0 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Tue, 17 Jul 2018 18:46:45 -0500 Subject: [PATCH 25/33] Use ubuntu-toolchain-r-test --- .travis.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 93b6a22..a9551b0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,16 +3,19 @@ language: c sudo: true before_install: - - sudo add-apt-repository ppa:ubuntu-toolchain-r/test - - sudo apt-get update - - sudo apt-get upgrade - - sudo apt-get dist-upgrade - cd include/dr_libs/ - git clone https://github.com/mackron/dr_libs.git - cd ../.. - wget https://github.com/devkitPro/pacman/releases/download/v1.0.0/devkitpro-pacman.deb - sudo dpkg -i devkitpro-pacman.deb +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - libstdc++-4.9-dev + install: - sudo dkp-pacman -Syu 3ds-dev --noconfirm --needed - sudo dkp-pacman -S 3ds-libogg --noconfirm From ddaca0cf0f65e24155fe2a4995633531a12e6abe Mon Sep 17 00:00:00 2001 From: Joel16 Date: Tue, 17 Jul 2018 18:55:12 -0500 Subject: [PATCH 26/33] Bring back mbedtls (needed for curl) --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a9551b0..2ec240e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,11 +15,12 @@ addons: - ubuntu-toolchain-r-test packages: - libstdc++-4.9-dev - + install: - sudo dkp-pacman -Syu 3ds-dev --noconfirm --needed - sudo dkp-pacman -S 3ds-libogg --noconfirm - sudo dkp-pacman -S 3ds-libvorbisidec --noconfirm + - sudo dkp-pacman -S 3ds-mbedtls --noconfirm - sudo dkp-pacman -S 3ds-mpg123 --noconfirm - source /etc/profile.d/devkit-env.sh - export CC=arm-none-eabi-gcc From 302c15784abd402abb0c0401b852c1700517ef8a Mon Sep 17 00:00:00 2001 From: Joel16 Date: Tue, 17 Jul 2018 19:00:09 -0500 Subject: [PATCH 27/33] Use the -Syu command for curl --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2ec240e..ac9e888 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ install: - sudo dkp-pacman -Syu 3ds-dev --noconfirm --needed - sudo dkp-pacman -S 3ds-libogg --noconfirm - sudo dkp-pacman -S 3ds-libvorbisidec --noconfirm - - sudo dkp-pacman -S 3ds-mbedtls --noconfirm + - sudo dkp-pacman -Syu 3ds-mbedtls --noconfirm - sudo dkp-pacman -S 3ds-mpg123 --noconfirm - source /etc/profile.d/devkit-env.sh - export CC=arm-none-eabi-gcc From 5aa01a219e554b228f3cdf89b09307e9904c6f62 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Tue, 17 Jul 2018 19:03:45 -0500 Subject: [PATCH 28/33] Force upgrade mbedtls ffs --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ac9e888..8eee9d1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ install: - sudo dkp-pacman -Syu 3ds-dev --noconfirm --needed - sudo dkp-pacman -S 3ds-libogg --noconfirm - sudo dkp-pacman -S 3ds-libvorbisidec --noconfirm - - sudo dkp-pacman -Syu 3ds-mbedtls --noconfirm + - sudo dkp-pacman -Syu 3ds-mbedtls --noconfirm --force - sudo dkp-pacman -S 3ds-mpg123 --noconfirm - source /etc/profile.d/devkit-env.sh - export CC=arm-none-eabi-gcc From 929cc25b96ec0ca3ab744e71d30118802e9d4a62 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Tue, 17 Jul 2018 19:07:06 -0500 Subject: [PATCH 29/33] Only install curl --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8eee9d1..96a0318 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,9 +18,9 @@ addons: install: - sudo dkp-pacman -Syu 3ds-dev --noconfirm --needed + - sudo dkp-pacman -S 3ds-curl --noconfirm - sudo dkp-pacman -S 3ds-libogg --noconfirm - sudo dkp-pacman -S 3ds-libvorbisidec --noconfirm - - sudo dkp-pacman -Syu 3ds-mbedtls --noconfirm --force - sudo dkp-pacman -S 3ds-mpg123 --noconfirm - source /etc/profile.d/devkit-env.sh - export CC=arm-none-eabi-gcc From 82f037dd660c8d77a7b916bdcbdb908fd02249c5 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Tue, 17 Jul 2018 19:10:58 -0500 Subject: [PATCH 30/33] Force install curl --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 96a0318..dd09c5a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ addons: install: - sudo dkp-pacman -Syu 3ds-dev --noconfirm --needed - - sudo dkp-pacman -S 3ds-curl --noconfirm + - sudo dkp-pacman -Syu 3ds-curl --noconfirm --force - sudo dkp-pacman -S 3ds-libogg --noconfirm - sudo dkp-pacman -S 3ds-libvorbisidec --noconfirm - sudo dkp-pacman -S 3ds-mpg123 --noconfirm From bdaef55f9e8efe04951a522dd4a8b07b69461fdf Mon Sep 17 00:00:00 2001 From: Joel16 Date: Tue, 17 Jul 2018 21:26:37 -0500 Subject: [PATCH 31/33] Display file timestamp and fix text out of bounds in properties --- include/fs.h | 1 + source/fs.c | 41 ++++++++++++++++++++++++++++++++- source/menus/menu_fileoptions.c | 7 +++--- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/include/fs.h b/include/fs.h index f630992..9b45715 100644 --- a/include/fs.h +++ b/include/fs.h @@ -24,5 +24,6 @@ Result FS_RenameDir(FS_Archive archive, const char *old_filename, const char *ne Result FS_Open(Handle *handle, FS_Archive archive, const char *path, u32 flags); Result FS_Read(FS_Archive archive, const char *path, u64 size, char *buf); Result FS_Write(FS_Archive archive, const char *path, const char *buf); +char *FS_GetFileTimestamp(FS_Archive archive, char *path); #endif \ No newline at end of file diff --git a/source/fs.c b/source/fs.c index 31cd198..b41ff07 100644 --- a/source/fs.c +++ b/source/fs.c @@ -279,4 +279,43 @@ Result FS_Write(FS_Archive archive, const char *path, const char *buf) return ret; return 0; -} \ No newline at end of file +} + +/*static Result FS_GetFileTimestampRaw(FS_Archive archive, const char *path, u64 *mtime) +{ + Result ret = 0; + u16 path_utf16[256] = {0}; + + utf8_to_utf16(path_utf16, path, 256); + + if (R_FAILED(ret = FSUSER_ControlArchive(archive, ARCHIVE_ACTION_GET_TIMESTAMP, path_utf16, sizeof(path_utf16), mtime, sizeof(*mtime)))) + return ret; + + *mtime /= 1000; + *mtime += 946684800; + + return 0; +}*/ + +char *FS_GetFileTimestamp(FS_Archive archive, char *path) +{ + static char timeStr[20]; + u64 mtime = 0; + + if (R_SUCCEEDED(sdmc_getmtime(path, &mtime))) + { + time_t mt = mtime; + struct tm *timeStruct = gmtime(&mt); + + int hours = timeStruct->tm_hour; + int minutes = timeStruct->tm_min; + + int day = timeStruct->tm_mday; + int month = timeStruct->tm_mon + 1; // January being 0 + int year = timeStruct->tm_year + 1900; + + snprintf(timeStr, sizeof(timeStr), "%d/%d/%d %2i:%02i", year, month, day, hours, minutes); + } + + return timeStr; +} diff --git a/source/menus/menu_fileoptions.c b/source/menus/menu_fileoptions.c index 3016c2a..5ce5a37 100644 --- a/source/menus/menu_fileoptions.c +++ b/source/menus/menu_fileoptions.c @@ -492,9 +492,10 @@ void Menu_DisplayProperties(void) Utils_GetSizeString(utils_size, size); } - Draw_Textf(66, 65, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Name: %s", file->name); - Draw_Textf(66, 80, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Parent: %s", cwd); - Draw_Textf(66, 95, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Size: %s", utils_size); + Draw_Textf(66, 60, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Name: %.22s", file->name); + Draw_Textf(66, 76, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Parent: %.20s", cwd); + Draw_Textf(66, 92, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Size: %s", utils_size); + Draw_Textf(66, 108, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Modified time: %s", FS_GetFileTimestamp(archive, path)); Draw_GetTextSize(0.45f, &properties_ok_width, &properties_ok_height, "OK"); Draw_Rect((253 - properties_ok_width) - 5, (218 - properties_ok_height) - 5, properties_ok_width + 10, properties_ok_height + 10, config_dark_theme? SELECTOR_COLOUR_DARK : SELECTOR_COLOUR_LIGHT); From 04a8f8c4f1f0c5d974562eaada971eb45c5c10fc Mon Sep 17 00:00:00 2001 From: Joel16 Date: Wed, 18 Jul 2018 00:48:45 -0500 Subject: [PATCH 32/33] Fix sleep mode in music player, dark menu icons, more - File size only displayed for dirs - Fixed rosalina crash --- include/textures.h | 7 +- .../ic_material_light_filesystem_dark.png | Bin 180 -> 180 bytes .../ic_material_light_filesystem_overlay.png | Bin 0 -> 180 bytes res/drawable/ic_material_light_home_dark.png | Bin 289 -> 299 bytes .../ic_material_light_home_overlay.png | Bin 0 -> 289 bytes .../ic_material_light_remote_dark.png | Bin 429 -> 446 bytes .../ic_material_light_remote_overlay.png | Bin 0 -> 429 bytes .../ic_material_light_sdcard_dark.png | Bin 240 -> 240 bytes .../ic_material_light_sdcard_overlay.png | Bin 0 -> 240 bytes .../ic_material_light_secure_dark.png | Bin 491 -> 531 bytes .../ic_material_light_secure_overlay.png | Bin 0 -> 491 bytes .../ic_material_light_settings_dark.png | Bin 669 -> 746 bytes .../ic_material_light_settings_overlay.png | Bin 0 -> 669 bytes res/drawable/ic_material_light_tab_dark.png | Bin 385 -> 385 bytes .../ic_material_light_tab_overlay.png | Bin 0 -> 385 bytes res/drawable/sprites.t3s | 19 +++-- source/main.c | 13 ++-- source/menus/menu_fileoptions.c | 10 ++- source/menus/menu_ftp.c | 7 +- source/menus/menu_main.c | 26 +++++-- source/menus/menu_music.c | 2 + source/menus/menu_update.c | 69 +++++++++++------- source/textures.c | 7 ++ 23 files changed, 105 insertions(+), 55 deletions(-) create mode 100644 res/drawable/ic_material_light_filesystem_overlay.png create mode 100644 res/drawable/ic_material_light_home_overlay.png create mode 100644 res/drawable/ic_material_light_remote_overlay.png create mode 100644 res/drawable/ic_material_light_sdcard_overlay.png create mode 100644 res/drawable/ic_material_light_secure_overlay.png create mode 100644 res/drawable/ic_material_light_settings_overlay.png create mode 100644 res/drawable/ic_material_light_tab_overlay.png diff --git a/include/textures.h b/include/textures.h index 0e00b8d..ecf1a9a 100644 --- a/include/textures.h +++ b/include/textures.h @@ -7,9 +7,10 @@ C2D_Image icon_app, icon_archive, icon_audio, icon_dir, icon_file, icon_image, i icon_doc, icon_check, icon_uncheck, icon_check_dark, icon_uncheck_dark, icon_radio_off, icon_radio_on, \ icon_radio_dark_off, icon_radio_dark_on, icon_toggle_on, icon_toggle_dark_on, icon_toggle_off, \ dialog, options_dialog, properties_dialog, dialog_dark, options_dialog_dark, properties_dialog_dark, \ - icon_home, icon_home_dark, icon_options, icon_options_dark, icon_settings, icon_settings_dark, \ - icon_ftp, icon_ftp_dark, icon_sd, icon_sd_dark, icon_secure, icon_secure_dark, icon_search, \ - icon_updates, icon_updates_dark, \ + icon_home, icon_home_dark, icon_home_overlay, icon_options, icon_options_dark, icon_options_overlay, \ + icon_settings, icon_settings_dark, icon_settings_overlay, icon_ftp, icon_ftp_dark, icon_ftp_overlay, \ + icon_sd, icon_sd_dark, icon_sd_overlay, icon_secure, icon_secure_dark, icon_secure_overlay, icon_search, \ + icon_updates, icon_updates_dark, icon_updates_overlay, \ default_artwork, default_artwork_blur, ic_music_bg_bottom, btn_play, btn_pause, btn_rewind, btn_forward, \ btn_repeat, btn_shuffle, btn_repeat_overlay, btn_shuffle_overlay, \ icon_nav_drawer, icon_actions, icon_back; diff --git a/res/drawable/ic_material_light_filesystem_dark.png b/res/drawable/ic_material_light_filesystem_dark.png index acbc0f11858b8930785c29f2b2388509726d72dc..5c8cded48b3a4c35570aea479ea9e40e17c61294 100644 GIT binary patch delta 44 YcmdnOxP@_oj#55e5d1aFcA{Sz0Fh1-YXATM delta 44 YcmdnOxP@_oj?#a;;Jo7NpA-Gk01;s%L;wH) diff --git a/res/drawable/ic_material_light_filesystem_overlay.png b/res/drawable/ic_material_light_filesystem_overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..acbc0f11858b8930785c29f2b2388509726d72dc GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAd3?%E9GuQzs#Q>iWS0Mc#FF3FG`X^8kcS(?6 zFoXQ8n7&3HJGKs?V+xhtKw)!F7srr_ImrnM!UkH_nv9JM9V;bRk~d9lc4V4xQjvp= wSAq4_o@PhU2Q>yKqDADUHx3vIVCg!0KpqY_5c6? literal 0 HcmV?d00001 diff --git a/res/drawable/ic_material_light_home_dark.png b/res/drawable/ic_material_light_home_dark.png index 6d44947e86736b2a613e44cc897ffdc64619376d..c8e572a82f6dcf3077cb42f09cd2fead0ddff600 100644 GIT binary patch literal 299 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~%3?x6Bmj(i<@Bp6>S0J6Amj^_7xw-jZ0AxZK zgh{YUxOzAPs8Kj^_D-PDvL!)&!3^BmOi`UoGAXk}n6)dXW$HG}m@ZMrV$jNA?pyNz z;LgiHg=wBHjv*Cu&YsKVcTf;%d#F+3;_>Cs|NnE1j$Qnf_4?nXZ!xzcTb_gjie)ZX zlVq~-IxjoJy=#UY>!v$CxzOox%=6$)o8I^fr&$@YE@s5{wwh;ObDwj6Le9O1#U6{~ i?_7Srsr{b%cRs;2Y?X_gyY>TZWbkzLb6Mw<&;$T7L3Ygm literal 289 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~%3?x6Bmj(i5?G7 zU_a-wO_|?yw@+9zwOPfziNP&s> z$2Qmb3=6(YdUMRGqUhS4O}{31e8{fSoA`$P`j_1~8=tS-D=RI;x{pC5(hq1KgQu&X J%Q~loCIDrCo7?~Z diff --git a/res/drawable/ic_material_light_home_overlay.png b/res/drawable/ic_material_light_home_overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..6d44947e86736b2a613e44cc897ffdc64619376d GIT binary patch literal 289 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~%3?x6Bmj(i5?G7 zU_a-wO_|?yw@+9zwOPfziNP&s> z$2Qmb3=6(YdUMRGqUhS4O}{31e8{fSoA`$P`j_1~8=tS-D=RI;x{pC5(hq1KgQu&X J%Q~loCIDrCo7?~Z literal 0 HcmV?d00001 diff --git a/res/drawable/ic_material_light_remote_dark.png b/res/drawable/ic_material_light_remote_dark.png index e89f6b6939ef1a76a1c8b57fd26a31bbb42e609f..fa71938f44a3c71e9788e9af12b915fd04225e8b 100644 GIT binary patch literal 446 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQa8v}eoT!D09K|w)&K9B&B`T0OD z1c3z*Tx9WOOP2y=5h6&sa&vOv=D-h*(a}yh%EYOwKi`42~08>l-I`$T82zj85n@ zV+@(5DPPGi9p=s}@zp5=XiTH0i(^Q|oaBTBk_jnEX^!H_smbZ-&mXu5K7Pd3wej*eUjRc#MR}c(6_0pYn4>T!gY)y zSDr8!ZrxH^Qu6gc*4M@+wl=pnCk~vDNLkjoQf!k%yW|bEVs1{02V1uYB`uIQj*df#gkK$)6<_na1ng`h^=em=MSGga&vGw z9&K^G@z5cJM@vJkPhiS~DNPzvp1E;sI(>qR`Dn|NCyp|#uCA)70?w|Y8Y_-5CaucO z$jZuWzWR!Rb!o}h8xtGn*`4rVb}mgZ OVeoYIb6Mw<&;$Ss(fYao diff --git a/res/drawable/ic_material_light_remote_overlay.png b/res/drawable/ic_material_light_remote_overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..e89f6b6939ef1a76a1c8b57fd26a31bbb42e609f GIT binary patch literal 429 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQaO9OmDT!HleK@WEPeH;pOsCh|{ zUoZo!qL!FR%gjVUnZD^+-r+V}3``NRoOzRU7@ccW9VfKbH%{)5W1f*2ozQ6(GEGyy zl3zN^op-K;vl-Bc22U5qkcv6U2@50>Qj*df#gkK$)6<_na1ng`h^=em=MSGga&vGw z9&K^G@z5cJM@vJkPhiS~DNPzvp1E;sI(>qR`Dn|NCyp|#uCA)70?w|Y8Y_-5CaucO z$jZuWzWR!Rb!o}h8xtGn*`4rVb}mgZ OVeoYIb6Mw<&;$Ss(fYao literal 0 HcmV?d00001 diff --git a/res/drawable/ic_material_light_sdcard_dark.png b/res/drawable/ic_material_light_sdcard_dark.png index c441b89d020e4153fd10ae97462db25139f7b352..f15c9da65aa7ef5921c0e355babe4c7e35b64965 100644 GIT binary patch delta 74 ocmeys_S#UO31dR(-2B-4!+O48_C+4&O0HuH*fB*mh delta 74 Ycmeys_CTnfb;HOSrc4?>gcK&GG%*pwFtaj+ z2M=7t9zS}P_#pLBa+15)bP0l+XkKbD(l? literal 0 HcmV?d00001 diff --git a/res/drawable/ic_material_light_secure_dark.png b/res/drawable/ic_material_light_secure_dark.png index 6c949e806f98cc822a3db0d3c6c93fa6a566d73a..c0a57c324662de9ba66e2feb02f1c7cb3402ef35 100644 GIT binary patch literal 531 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQaw*!1aT!C}}7!(#101*%r7Zv5_ zBh(|AnV+A(Z0Sy^D)!plAdCH$MMw~ ztIq#VY)s&25G#vPk?LM8HNnHm!1c1^B4(4>lTR#o;_1jE;w{8GMLR7tVXfXq$B#Vu z+f=q$>$^<(j}v&Bg_V h55K0}i~Uh7x8oJ>I%O_d4WLUIJYD@<);T3K0RUFI&lLaw literal 491 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQa2LgOTT!Hlep$wQ8Kl1_l!J#C` zFPMSfEZkOrQK(}2%qj(*qITyxLCvWFOomo2%$8vs4qe`E^}+0_GhE{)G4Q7MP3~t^ zv#*h|?rl@|jSP$uagx8)^#^3uEKe85kcv5LFGLC-G!S8ZFoDbO?W(EMg0wPL*53d3 zfB&S1iVOYI<{Zy|H^=(B(5Lp4O;e;Sza{>35^RXPv^HktH6Gy=<|a8&y0a}Bu5J@8 z3YcE8X~E^t7mKg+vkA2?JT$R2{K$<^3BP+Cn&DDMH}4eayux}tOgB9H{!I^G8(-yP zC(5?7^BrHG{CKjA-SWBu#&w!|?l!w~)t?jI`jY$o;faUYfUaclboFyt=akR{0IqB< ACjbBd diff --git a/res/drawable/ic_material_light_secure_overlay.png b/res/drawable/ic_material_light_secure_overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..6c949e806f98cc822a3db0d3c6c93fa6a566d73a GIT binary patch literal 491 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQa2LgOTT!Hlep$wQ8Kl1_l!J#C` zFPMSfEZkOrQK(}2%qj(*qITyxLCvWFOomo2%$8vs4qe`E^}+0_GhE{)G4Q7MP3~t^ zv#*h|?rl@|jSP$uagx8)^#^3uEKe85kcv5LFGLC-G!S8ZFoDbO?W(EMg0wPL*53d3 zfB&S1iVOYI<{Zy|H^=(B(5Lp4O;e;Sza{>35^RXPv^HktH6Gy=<|a8&y0a}Bu5J@8 z3YcE8X~E^t7mKg+vkA2?JT$R2{K$<^3BP+Cn&DDMH}4eayux}tOgB9H{!I^G8(-yP zC(5?7^BrHG{CKjA-SWBu#&w!|?l!w~)t?jI`jY$o;faUYfUaclboFyt=akR{0IqB< ACjbBd literal 0 HcmV?d00001 diff --git a/res/drawable/ic_material_light_settings_dark.png b/res/drawable/ic_material_light_settings_dark.png index d71b1f9a11d77db7c6f0f34ef2a7867ee4ee2c9c..e39dfea968cea3d5474fc24f0a049ab5be68c9a2 100644 GIT binary patch literal 746 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqMyzV6+bK32_B-3-a>|3JMCr2+n|T zfn&nZ`1w| zA#AvIpuKQ5P%)g1?4D&ymm(XB%tq6WCY6?&iYx+T1D$%p=k!xx@MV?+`2{m@SvZ<9 z>-EoU7h`62oz|zz!o(byY+%$pyL?$F_U-)YcsQ_Pg=VQ~3jpOd$tgTe~DWM4fxh5Ol literal 669 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqMyzVB`q!32_B-|Br(I|0g`ZejgYX z5hX!>!3cE8yGcDFIP8DcV<+Z*dv`2#~|2b%gXLj%2DCq zGowI8OO{W?q>Ym&y49M0YPg(2Y)Z}zXX_1VKOwuEv zNkNy45*#Bf73MSZG)&pU6tPe#gh6ZJ5+0F|Kvu)2SFf0wJay2~&0VmdXAR$kmE2o= zV*8t&?;i+|%vm8AFhTIh112sh=bY(+Y+SP%BDiE^Y?dY5W<7JJv8hRnhi6)tL)%tf z`+y7Aq*C_G-P^oa_(om&g#A-uEdm;ZBKEJAZr{!B(r{P!RRx=*e!jDOeC>i6^EcKz zC>)v1bIM^lE2FHO3{&Fa#zYP4>KQCi6!3cE8yGcDFIP8DcV<+Z*dv`2#~|2b%gXLj%2DCq zGowI8OO{W?q>Ym&y49M0YPg(2Y)Z}zXX_1VKOwuEv zNkNy45*#Bf73MSZG)&pU6tPe#gh6ZJ5+0F|Kvu)2SFf0wJay2~&0VmdXAR$kmE2o= zV*8t&?;i+|%vm8AFhTIh112sh=bY(+Y+SP%BDiE^Y?dY5W<7JJv8hRnhi6)tL)%tf z`+y7Aq*C_G-P^oa_(om&g#A-uEdm;ZBKEJAZr{!B(r{P!RRx=*e!jDOeC>i6^EcKz zC>)v1bIM^lE2FHO3{&Fa#zYP4>KQCi6(BJu0Z<#-~ivuGkSrp(JKk^ z3ua(TDi2}gsGFI~EmWV+!>ZHPK5cTN982;nU*6_C)9DjsnfZLMKe@p55NuJiWhP-ZQ`_AWrZIM&RC>+EWLe3_$LeZwd+HR+me7FQ@YK}M;OhZNsiCeySFT*T vbm31MJZ=f!FBname); Draw_Textf(66, 76, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Parent: %.20s", cwd); - Draw_Textf(66, 92, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Size: %s", utils_size); - Draw_Textf(66, 108, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Modified time: %s", FS_GetFileTimestamp(archive, path)); + + if (!file->isDir) + { + Draw_Textf(66, 92, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Size: %s", utils_size); + Draw_Textf(66, 108, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Modified time: %s", FS_GetFileTimestamp(archive, path)); + } + else + Draw_Textf(66, 92, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Modified time: %s", FS_GetFileTimestamp(archive, path)); Draw_GetTextSize(0.45f, &properties_ok_width, &properties_ok_height, "OK"); Draw_Rect((253 - properties_ok_width) - 5, (218 - properties_ok_height) - 5, properties_ok_width + 10, properties_ok_height + 10, config_dark_theme? SELECTOR_COLOUR_DARK : SELECTOR_COLOUR_LIGHT); diff --git a/source/menus/menu_ftp.c b/source/menus/menu_ftp.c index e041765..616fcd3 100644 --- a/source/menus/menu_ftp.c +++ b/source/menus/menu_ftp.c @@ -11,6 +11,7 @@ #include "menu_main.h" #include "status_bar.h" #include "textures.h" +#include "touch.h" #include "utils.h" void Menu_DisplayFTP(void) @@ -27,7 +28,7 @@ void Menu_DisplayFTP(void) while(MENU_STATE == MENU_STATE_FTP) { ftp_loop(); - + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); C2D_TargetClear(RENDER_BOTTOM, config_dark_theme? BLACK_BG : WHITE); C2D_SceneBegin(RENDER_BOTTOM); @@ -38,7 +39,7 @@ void Menu_DisplayFTP(void) Menu_DrawMenuBar(); - if (!(wifiStatus)) + if (wifiStatus == 0) { Draw_Text(((320 - Draw_GetTextWidth(0.48f, "Failed to initialize FTP.")) / 2), 40, 0.48f, WHITE, "Failed to initialize FTP."); sprintf(buf, "WiFi not enabled."); @@ -82,7 +83,7 @@ void Menu_DisplayFTP(void) hidScanInput(); u32 kDown = hidKeysDown(); - if (kDown & KEY_SELECT) + if (((kDown & KEY_TOUCH) && (TouchInRect(73, 0, 97, 20))) || (kDown & KEY_SELECT)) break; } diff --git a/source/menus/menu_main.c b/source/menus/menu_main.c index e3c223d..9a54895 100644 --- a/source/menus/menu_main.c +++ b/source/menus/menu_main.c @@ -60,6 +60,17 @@ static void Menu_ControlHome(u32 input) else if (input & KEY_DDOWN) position++; + if (hidKeysHeld() & KEY_CPAD_UP) + { + wait(5); + position--; + } + else if (hidKeysHeld() & KEY_CPAD_DOWN) + { + wait(5); + position++; + } + Utils_SetMax(&position, 0, (fileCount - 1)); Utils_SetMin(&position, (fileCount - 1), 0); @@ -96,15 +107,16 @@ static void Menu_ControlHome(u32 input) void Menu_DrawMenuBar(void) { - Draw_Image(MENU_STATE == MENU_STATE_HOME? icon_home_dark : icon_home, 0, -2); + Draw_Image(MENU_STATE == MENU_STATE_HOME? icon_home_overlay : (config_dark_theme? icon_home_dark : icon_home), 0, -2); Draw_Image((MENU_STATE == MENU_STATE_FILEOPTIONS) || (MENU_STATE == MENU_STATE_PROPERTIES) || (MENU_STATE == MENU_STATE_DELETE)? - icon_options_dark : icon_options, 25, 0); + icon_options_overlay : (config_dark_theme? icon_options_dark : icon_options), 25, 0); Draw_Image((MENU_STATE == MENU_STATE_SETTINGS) || (MENU_STATE == MENU_STATE_SORT) || (MENU_STATE == MENU_STATE_ABOUT)? - icon_settings_dark : icon_settings, 50, 0); - Draw_Image(MENU_STATE == MENU_STATE_FTP? icon_ftp_dark : icon_ftp, 75, 0); - Draw_Image((MENU_STATE == MENU_STATE_UPDATE) || (MENU_STATE == MENU_STATE_UPDATE_2)? icon_updates_dark : icon_updates, 100, 1); - Draw_Image(BROWSE_STATE == BROWSE_STATE_SD? icon_sd_dark : icon_sd, 250, 0); - Draw_Image(BROWSE_STATE == BROWSE_STATE_NAND? icon_secure_dark : icon_secure, 275, 0); + icon_settings_overlay : (config_dark_theme? icon_settings_dark : icon_settings), 50, 0); + Draw_Image(MENU_STATE == MENU_STATE_FTP? icon_ftp_overlay : (config_dark_theme? icon_ftp_dark : icon_ftp), 75, 0); + Draw_Image((MENU_STATE == MENU_STATE_UPDATE) || (MENU_STATE == MENU_STATE_UPDATE_2)? icon_updates_overlay : + (config_dark_theme? icon_updates_dark : icon_updates), 100, 1); + Draw_Image(BROWSE_STATE == BROWSE_STATE_SD? icon_sd_overlay : (config_dark_theme? icon_sd_dark : icon_sd), 250, 0); + Draw_Image(BROWSE_STATE == BROWSE_STATE_NAND? icon_secure_overlay : (config_dark_theme? icon_secure_dark : icon_secure), 275, 0); Draw_Image(icon_search, 300, 0); } diff --git a/source/menus/menu_music.c b/source/menus/menu_music.c index d6a5357..685e8e0 100644 --- a/source/menus/menu_music.c +++ b/source/menus/menu_music.c @@ -145,6 +145,7 @@ static void Music_HandleNext(bool forward, int state) void Menu_PlayMusic(char *path) { + aptSetSleepAllowed(false); Music_Play(path); while (aptMainLoop()) @@ -275,5 +276,6 @@ void Menu_PlayMusic(char *path) memset(title, 0, sizeof(title)); memset(playlist, 0, sizeof(playlist[0][0]) * 512 * 512); count = 0; + aptSetSleepAllowed(true); return; } \ No newline at end of file diff --git a/source/menus/menu_update.c b/source/menus/menu_update.c index ebd7fd0..e3e3511 100644 --- a/source/menus/menu_update.c +++ b/source/menus/menu_update.c @@ -14,7 +14,8 @@ static int update_dialog_selection = 0; static bool err = false; -char ver[10]; +static u32 wifiStatus = 0; +static char ver[10]; static bool Menu_ValidateUpdate(bool nighlty) { @@ -39,7 +40,7 @@ static bool Menu_ValidateUpdate(bool nighlty) sscanf(buf, "%s", ver); free(buf); - if (strcmp(ver, GITVERSION) != 0) + if (strcasecmp(ver, GITVERSION) != 0) return true; return false; @@ -59,7 +60,11 @@ void Menu_DisplayUpdate(void) Draw_Rect(0, 20, 400, 35, config_dark_theme? MENU_BAR_DARK : STATUS_BAR_LIGHT); // Menu bar Draw_Text(10, 30, 0.48f, WHITE, "Update Center"); - if (err) + ACU_GetWifiStatus(&wifiStatus); + + if (wifiStatus == 0) + Draw_Text(((320 - Draw_GetTextWidth(0.48f, "WiFi not enabled.")) / 2), 60, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "WiFi not enabled."); + else if (err) Draw_Text(err_width, 60, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "No updates available."); Draw_Text(122, 85, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Nightly builds"); @@ -92,7 +97,7 @@ void Menu_DisplayUpdate2(void) Draw_Image(config_dark_theme? dialog_dark : dialog, ((320 - (dialog.subtex->width)) / 2), ((240 - (dialog.subtex->height)) / 2)); - Draw_Text(((320 - (dialog.subtex->width)) / 2) + 6, ((240 - (dialog.subtex->height)) / 2) + 6, 0.45f, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR, "Confirm update"); + Draw_Textf(((320 - (dialog.subtex->width)) / 2) + 6, ((240 - (dialog.subtex->height)) / 2) + 6, 0.45f, config_dark_theme? TITLE_COLOUR_DARK : TITLE_COLOUR, "New update: %s", ver); Draw_Text(((320 - (text_width)) / 2), ((240 - (dialog.subtex->height)) / 2) + 40, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "This action cannot be undone."); Draw_Text(((320 - (text2_width)) / 2), ((240 - (dialog.subtex->height)) / 2) + 55, 0.45f, config_dark_theme? TEXT_MIN_COLOUR_DARK : TEXT_MIN_COLOUR_LIGHT, "Do you wish to update?"); @@ -110,23 +115,29 @@ void Menu_ControlUpdate(u32 input) { if ((input & KEY_TOUCH) && (TouchInRect(106, 117, (106) + (Draw_GetTextWidth(0.45, "Check for updates") + 10), 137))) { - wait(1); - Net_DownloadFile("https://github.com/joel16/3DShell/raw/gh-pages/UPDATE_NIGHTLY.txt", "/3ds/3DShell/UPDATE_NIGHTLY.txt"); + if (wifiStatus != 0) + { + wait(1); + Net_DownloadFile("https://github.com/joel16/3DShell/raw/gh-pages/UPDATE_NIGHTLY.txt", "/3ds/3DShell/UPDATE_NIGHTLY.txt"); - if (Menu_ValidateUpdate(true)) - MENU_STATE = MENU_STATE_UPDATE_2; - else - err = true; + if (Menu_ValidateUpdate(true)) + MENU_STATE = MENU_STATE_UPDATE_2; + else + err = true; + } } else if ((input & KEY_TOUCH) && (TouchInRect(106, 187, (106) + (Draw_GetTextWidth(0.45, "Check for updates") + 10), 207))) { - wait(1); - Net_DownloadFile("https://github.com/joel16/3DShell/raw/gh-pages/UPDATE_MILESTONE.txt", "/3ds/3DShell/UPDATE_MILESTONE.txt"); - if (Menu_ValidateUpdate(false)) - MENU_STATE = MENU_STATE_UPDATE_2; - else - err = true; + if (wifiStatus != 0) + { + wait(1); + Net_DownloadFile("https://github.com/joel16/3DShell/raw/gh-pages/UPDATE_MILESTONE.txt", "/3ds/3DShell/UPDATE_MILESTONE.txt"); + if (Menu_ValidateUpdate(false)) + MENU_STATE = MENU_STATE_UPDATE_2; + else + err = true; + } } } @@ -151,18 +162,22 @@ void Menu_ControlUpdate2(u32 input) { if (update_dialog_selection == 1) { - if (envIsHomebrew()) + if (wifiStatus != 0) { - if (FS_FileExists(archive, "/3ds/3DShell/3DShell.3dsx")) - FS_Remove(archive, "/3ds/3DShell/3DShell.3dsx"); - Net_DownloadFile("https://github.com/joel16/3DShell/raw/gh-pages/3DShell.3dsx", "/3ds/3DShell/3DShell.3dsx"); - longjmp(exitJmp, 1); - } - else - { - Net_DownloadFile("https://github.com/joel16/3DShell/raw/gh-pages/3DShell.cia", "/3ds/3DShell/3DShell.cia"); - CIA_InstallTitle("/3ds/3DShell/3DShell.cia", MEDIATYPE_SD, true); - } + if (envIsHomebrew()) + { + if (FS_FileExists(archive, "/3ds/3DShell/3DShell.3dsx")) + FS_Remove(archive, "/3ds/3DShell/3DShell.3dsx"); + + Net_DownloadFile("https://github.com/joel16/3DShell/raw/gh-pages/3DShell.3dsx", "/3ds/3DShell/3DShell.3dsx"); + longjmp(exitJmp, 1); + } + else + { + Net_DownloadFile("https://github.com/joel16/3DShell/raw/gh-pages/3DShell.cia", "/3ds/3DShell/3DShell.cia"); + CIA_InstallTitle("/3ds/3DShell/3DShell.cia", MEDIATYPE_SD, true); + } + } } else { diff --git a/source/textures.c b/source/textures.c index 4a50644..7a34294 100644 --- a/source/textures.c +++ b/source/textures.c @@ -37,19 +37,26 @@ void Textures_Load(void) icon_toggle_off = C2D_SpriteSheetGetImage(spritesheet, sprites_btn_material_light_toggle_off_normal_idx); icon_home = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_home_idx); icon_home_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_home_dark_idx); + icon_home_overlay = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_home_overlay_idx); icon_options = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_filesystem_idx); icon_options_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_filesystem_dark_idx); + icon_options_overlay = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_filesystem_overlay_idx); icon_settings = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_settings_idx); icon_settings_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_settings_dark_idx); + icon_settings_overlay = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_settings_overlay_idx); icon_ftp = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_remote_idx); icon_ftp_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_remote_dark_idx); + icon_ftp_overlay = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_remote_overlay_idx); icon_sd = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_sdcard_idx); icon_sd_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_sdcard_dark_idx); + icon_sd_overlay = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_sdcard_overlay_idx); icon_secure = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_secure_idx); icon_secure_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_secure_dark_idx); + icon_secure_overlay = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_secure_overlay_idx); icon_search = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_search_idx); icon_updates = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_tab_idx); icon_updates_dark = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_tab_dark_idx); + icon_updates_overlay = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_material_light_tab_overlay_idx); default_artwork_blur = C2D_SpriteSheetGetImage(spritesheet, sprites_default_artwork_blur_idx); default_artwork = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_album_lg_idx); ic_music_bg_bottom = C2D_SpriteSheetGetImage(spritesheet, sprites_ic_music_bg_bottom_idx); From f1421b866f8427cb3cabd95fb32fefb88aa521b5 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Wed, 18 Jul 2018 00:49:14 -0500 Subject: [PATCH 33/33] Only build on master --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index dd09c5a..4e349aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,4 +41,4 @@ deploy: skip_cleanup: true github_token: $GITHUB_TOKEN # Set in travis-ci.org dashboard on: - branch: C2D \ No newline at end of file + branch: master \ No newline at end of file

>8 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 VAuDcgXes~z002ovPDHLkV1iTUX&e9m literal 0 HcmV?d00001 diff --git a/res/drawable/ic_music_bg_top.png b/res/drawable/ic_music_bg_top.png new file mode 100644 index 0000000000000000000000000000000000000000..d822ee339e0d7e2dae547ac0b9bec270dee69fe1 GIT binary patch literal 46256 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 q8V2LAj(mnnSJuNXc^#1^*R=PoST0zwS literal 0 HcmV?d00001 diff --git a/res/drawable/sprites.t3s b/res/drawable/sprites.t3s new file mode 100644 index 0000000..c17ba0e --- /dev/null +++ b/res/drawable/sprites.t3s @@ -0,0 +1,51 @@ +--atlas -f rgba -z auto +btn_material_light_check_off_normal_dark.png +btn_material_light_check_off_normal.png +btn_material_light_check_on_normal_dark.png +btn_material_light_check_on_normal.png +btn_material_light_radio_off_normal_dark.png +btn_material_light_radio_off_normal.png +btn_material_light_radio_on_normal_dark.png +btn_material_light_radio_on_normal.png +btn_material_light_toggle_off_normal.png +btn_material_light_toggle_on_normal_dark.png +btn_material_light_toggle_on_normal.png +ic_material_light_filesystem_dark.png +ic_material_light_filesystem.png +ic_material_light_home_dark.png +ic_material_light_home.png +ic_material_light_remote_dark.png +ic_material_light_remote.png +ic_material_light_sdcard_dark.png +ic_material_light_sdcard.png +ic_material_light_search.png +ic_material_light_secure_dark.png +ic_material_light_secure.png +ic_material_light_settings_dark.png +ic_material_light_settings.png +btn_playback_forward.png +btn_playback_pause.png +btn_playback_play.png +btn_playback_repeat_overlay.png +btn_playback_repeat.png +btn_playback_rewind.png +btn_playback_shuffle_overlay.png +btn_playback_shuffle.png +ic_arrow_back_normal.png +ic_fso_default.png +ic_fso_folder_dark.png +ic_fso_folder.png +ic_fso_type_audio.png +ic_fso_type_compress.png +ic_fso_type_document.png +ic_fso_type_executable.png +ic_fso_type_image.png +ic_fso_type_text.png +ic_material_dialog_dark.png +ic_material_dialog.png +ic_material_light_contextual_action.png +ic_material_light_navigation_drawer.png +ic_material_options_dialog_dark.png +ic_material_options_dialog.png +ic_material_properties_dialog_dark.png +ic_material_properties_dialog.png \ No newline at end of file diff --git a/resources/.build.sh b/resources/.build.sh deleted file mode 100644 index 75f407e..0000000 --- a/resources/.build.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -set -e -set -x - -make clean && make -mv out/3DShell.cia . -mv out/3DShell.3dsx . -rm .gitignore .gitmodules .travis.yml LICENSE Makefile README.md -rm -rf build include out romfs resources source \ No newline at end of file diff --git a/resources/.travis-deps.sh b/resources/.travis-deps.sh deleted file mode 100644 index 0b61659..0000000 --- a/resources/.travis-deps.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/bin/sh -set -e -set -x - -# Build and install devkitARM + ctrulib -mkdir -p $DEVKITPRO -cd $DEVKITPRO -wget -N https://raw.githubusercontent.com/devkitPro/installer/master/perl/devkitARMupdate.pl -chmod +x ./devkitARMupdate.pl -./devkitARMupdate.pl - -# Get latest ctrulib and overwrite bundled one -git clone https://github.com/smealum/ctrulib.git -cd ctrulib/libctru -make && make install -cd ../../ -rm -rf ctrulib - -# Get latest version of Citro3D -git clone --recursive https://github.com/fincs/citro3d.git -cd citro3d -make && make install -cd ../ -rm -rf citro3d - -# Install latest version of picasso -git clone https://github.com/fincs/picasso.git -cd picasso -./autogen.sh -./configure -make -cp picasso ${DEVKITPRO}/devkitARM/bin/picasso -cd ../ -rm -rf picasso - -export CC=arm-none-eabi-gcc -export CXX=arm-none-eabi-g++ - -# Build and install bannertool -git clone --recursive https://github.com/Steveice10/bannertool -cd bannertool/ -make -cp output/linux-x86_64/bannertool ${DEVKITPRO}/devkitARM/bin/bannertool -cd ../ -rm -rf bannertool/ - -# Build and install makerom -git clone https://github.com/profi200/Project_CTR.git -cd Project_CTR/makerom -make -cp makerom ${DEVKITPRO}/devkitARM/bin/makerom -cd ../.. -rm -rf Project_CTR - -# Build and install portlibs using pacman -mkdir ${DEVKITPRO}/portlibs && mkdir ${PORTLIBS}/ -wget https://github.com/devkitPro/pacman/releases/download/v1.0.0/devkitpro-pacman.deb -sudo dpkg -i devkitpro-pacman.deb - -# Remove mbedtls if it's included - causes issues. -sudo rm -rf ${DEVKITPRO}/portlibs/3ds/include/mbedtls/ -sudo rm ${DEVKITPRO}/portlibs/3ds/lib/libmbedcrypto.a -sudo rm ${DEVKITPRO}/portlibs/3ds/lib/libmbedtls.a -sudo rm ${DEVKITPRO}/portlibs/3ds/lib/libmbedx509.a - -# Install required libs -sudo dkp-pacman -S 3ds-zlib --noconfirm -sudo dkp-pacman -S 3ds-curl --noconfirm -sudo dkp-pacman -S 3ds-mpg123 --noconfirm -sudo dkp-pacman -S 3ds-libogg --noconfirm -sudo dkp-pacman -S 3ds-libvorbisidec --noconfirm - -# Remove unnecessary files -rm *.bz2 -rm devkitARMupdate.pl -rm devkitpro-pacman.deb \ No newline at end of file diff --git a/resources/banner.png b/resources/banner.png deleted file mode 100644 index 19ea9a63880b335d53e88f06cdbee14fa692d21f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10753 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