├── .idea
├── .name
├── .gitignore
├── compiler.xml
├── render.experimental.xml
├── vcs.xml
├── AndroidProjectSystem.xml
├── deploymentTargetDropDown.xml
├── migrations.xml
├── gradle.xml
├── runConfigurations.xml
├── appInsightsSettings.xml
└── deploymentTargetSelector.xml
├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── chessboard.jpg
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── drawable
│ │ │ │ ├── bracket_vert_open.9.png
│ │ │ │ ├── bracket_horiz_close.9.png
│ │ │ │ ├── bracket_horiz_open.9.png
│ │ │ │ ├── bracket_vert_close.9.png
│ │ │ │ ├── chessboard.xml
│ │ │ │ ├── ic_void.xml
│ │ │ │ ├── layer_visible.xml
│ │ │ │ ├── ic_add.xml
│ │ │ │ ├── ic_done.xml
│ │ │ │ ├── ic_expand_more.xml
│ │ │ │ ├── ic_format_italic.xml
│ │ │ │ ├── ic_arrow_upward.xml
│ │ │ │ ├── ic_format_strikethrough.xml
│ │ │ │ ├── brush_tip_shape.xml
│ │ │ │ ├── ic_arrow_downward.xml
│ │ │ │ ├── ic_delete.xml
│ │ │ │ ├── ic_crop.xml
│ │ │ │ ├── ic_arrow_back.xml
│ │ │ │ ├── ic_clear.xml
│ │ │ │ ├── ic_close.xml
│ │ │ │ ├── ic_info.xml
│ │ │ │ ├── ic_format_align_center.xml
│ │ │ │ ├── ic_layers.xml
│ │ │ │ ├── ic_transform.xml
│ │ │ │ ├── ic_movie.xml
│ │ │ │ ├── ic_contrast.xml
│ │ │ │ ├── ic_image.xml
│ │ │ │ ├── ic_photo_size_select_actual.xml
│ │ │ │ ├── ic_filter_b_and_w.xml
│ │ │ │ ├── ic_folder_open.xml
│ │ │ │ ├── ic_format_align_right.xml
│ │ │ │ ├── ic_format_underlined.xml
│ │ │ │ ├── ic_format_align_left.xml
│ │ │ │ ├── ic_redo.xml
│ │ │ │ ├── ic_undo.xml
│ │ │ │ ├── ic_edit.xml
│ │ │ │ ├── ic_content_copy.xml
│ │ │ │ ├── ic_insert_drive_file.xml
│ │ │ │ ├── ic_save.xml
│ │ │ │ ├── ic_create_new_folder.xml
│ │ │ │ ├── ic_brightness_5.xml
│ │ │ │ ├── ic_more_horiz.xml
│ │ │ │ ├── ic_more_vert.xml
│ │ │ │ ├── ic_fit_screen.xml
│ │ │ │ ├── ic_drive_file_rename_outline.xml
│ │ │ │ ├── ic_filter.xml
│ │ │ │ ├── ic_push_pin.xml
│ │ │ │ ├── ic_flip.xml
│ │ │ │ ├── ic_opacity.xml
│ │ │ │ ├── ic_visibility.xml
│ │ │ │ ├── ic_content_paste.xml
│ │ │ │ ├── ic_grid_on.xml
│ │ │ │ ├── ic_history.xml
│ │ │ │ ├── ic_format_bold.xml
│ │ │ │ ├── ic_rotate_90_degrees_cw.xml
│ │ │ │ ├── ic_access_time.xml
│ │ │ │ ├── ic_save_as.xml
│ │ │ │ ├── ic_photo_size_select_large.xml
│ │ │ │ ├── ic_select_all.xml
│ │ │ │ ├── ic_balance.xml
│ │ │ │ ├── ic_deselect.xml
│ │ │ │ ├── ic_content_cut.xml
│ │ │ │ ├── ic_palette.xml
│ │ │ │ ├── ic_visibility_off.xml
│ │ │ │ ├── ic_settings.xml
│ │ │ │ └── ic_language.xml
│ │ │ ├── xml
│ │ │ │ └── provider_paths.xml
│ │ │ ├── menu
│ │ │ │ ├── color_picker.xml
│ │ │ │ ├── action_mode_color.xml
│ │ │ │ ├── palette.xml
│ │ │ │ ├── proj_tab.xml
│ │ │ │ ├── frame.xml
│ │ │ │ ├── action_mode_transformer.xml
│ │ │ │ └── tools.xml
│ │ │ ├── layout
│ │ │ │ ├── bracket.xml
│ │ │ │ ├── settings_activity.xml
│ │ │ │ ├── progress_indicator.xml
│ │ │ │ ├── item_color.xml
│ │ │ │ ├── slider.xml
│ │ │ │ ├── color_picker_hsv.xml
│ │ │ │ ├── frame_list.xml
│ │ │ │ ├── top_app_bar.xml
│ │ │ │ ├── options_bucket_fill.xml
│ │ │ │ ├── edit_text.xml
│ │ │ │ ├── animation_trimmer.xml
│ │ │ │ ├── options_gradient.xml
│ │ │ │ ├── options_eyedropper.xml
│ │ │ │ ├── fill_with_clip.xml
│ │ │ │ ├── item_frame.xml
│ │ │ │ ├── merge_as_hidden.xml
│ │ │ │ ├── lighting.xml
│ │ │ │ ├── file_name.xml
│ │ │ │ ├── options_patcher.xml
│ │ │ │ ├── color_picker_tabs.xml
│ │ │ │ ├── options_gradient_line.xml
│ │ │ │ ├── options_brush.xml
│ │ │ │ ├── options_eraser.xml
│ │ │ │ ├── options_pencil.xml
│ │ │ │ ├── options_clone_stamp.xml
│ │ │ │ ├── options_shape.xml
│ │ │ │ ├── options_text.xml
│ │ │ │ ├── options_magic_paint.xml
│ │ │ │ ├── options_transformer.xml
│ │ │ │ ├── color_picker.xml
│ │ │ │ ├── new_image.xml
│ │ │ │ ├── options_magic_eraser.xml
│ │ │ │ ├── noise_generator.xml
│ │ │ │ ├── new_guide.xml
│ │ │ │ └── layer_list.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── color
│ │ │ │ └── item_checked.xml
│ │ │ ├── values
│ │ │ │ ├── colors.xml
│ │ │ │ └── themes.xml
│ │ │ ├── values-land
│ │ │ │ └── styles.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── layout-land
│ │ │ │ └── color_picker.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── misterchan
│ │ │ │ └── iconeditor
│ │ │ │ ├── Guide.java
│ │ │ │ ├── util
│ │ │ │ ├── RunnableRunnable.java
│ │ │ │ ├── CanvasUtils.java
│ │ │ │ └── LightingToLevels.java
│ │ │ │ ├── tool
│ │ │ │ ├── Shape.java
│ │ │ │ ├── Ruler.java
│ │ │ │ ├── Gradient.java
│ │ │ │ ├── TextTool.java
│ │ │ │ ├── CloneStamp.java
│ │ │ │ ├── MagicPaint.java
│ │ │ │ ├── BrushTool.java
│ │ │ │ └── MagicEraser.java
│ │ │ │ ├── listener
│ │ │ │ ├── OnAdapterViewItemSelectedListener.java
│ │ │ │ ├── OnItemSelectedListener.java
│ │ │ │ ├── OnCBCheckedListener.java
│ │ │ │ ├── OnSliderValueChangeListener.java
│ │ │ │ ├── OnButtonCheckedListener.java
│ │ │ │ ├── AfterTextChangedListener.java
│ │ │ │ ├── OnRSChangeListener.java
│ │ │ │ ├── OnSliderChangeListener.java
│ │ │ │ └── OnCircularRSChangeListener.java
│ │ │ │ ├── ui
│ │ │ │ ├── CoordinateConversions.java
│ │ │ │ ├── ViewModel.java
│ │ │ │ └── ColorAdapter.java
│ │ │ │ ├── FloatingLayer.java
│ │ │ │ ├── Reference.java
│ │ │ │ ├── ColorRange.java
│ │ │ │ ├── DrawingPrimitivePreview.java
│ │ │ │ ├── colorpicker
│ │ │ │ ├── HslColorPicker.java
│ │ │ │ ├── XyzColorPicker.java
│ │ │ │ ├── LabColorPicker.java
│ │ │ │ ├── CmykColorPicker.java
│ │ │ │ ├── XyYColorPicker.java
│ │ │ │ └── YuvColorPicker.java
│ │ │ │ ├── CellGrid.java
│ │ │ │ ├── dialog
│ │ │ │ ├── FilterDialog.java
│ │ │ │ ├── NewImageDialog.java
│ │ │ │ ├── FillWithClipDialog.java
│ │ │ │ ├── BitmapConfigModifier.java
│ │ │ │ ├── EditNumberDialog.java
│ │ │ │ ├── SliderDialog.java
│ │ │ │ └── MatrixManager.java
│ │ │ │ ├── Project.java
│ │ │ │ └── ImageStateAccumulator.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── misterchan
│ │ │ └── iconeditor
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── misterchan
│ │ └── iconeditor
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── .github
└── ISSUE_TEMPLATE
│ ├── config.yml
│ └── new-issue.md
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── README.md
├── .gitignore
├── settings.gradle
├── gradle.properties
└── gradlew.bat
/.idea/.name:
--------------------------------------------------------------------------------
1 | IconEditor
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MISTER-CHAN/icon-editor/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MISTER-CHAN/icon-editor/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MISTER-CHAN/icon-editor/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MISTER-CHAN/icon-editor/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MISTER-CHAN/icon-editor/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/chessboard.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MISTER-CHAN/icon-editor/HEAD/app/src/main/res/mipmap-xxxhdpi/chessboard.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MISTER-CHAN/icon-editor/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bracket_vert_open.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MISTER-CHAN/icon-editor/HEAD/app/src/main/res/drawable/bracket_vert_open.9.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Icon Editor #
2 | **Requirement:** Android 12 or above
3 | **Third Party Library:** [Android NDK GIF](https://github.com/waynejo/android-ndk-gif)
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bracket_horiz_close.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MISTER-CHAN/icon-editor/HEAD/app/src/main/res/drawable/bracket_horiz_close.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bracket_horiz_open.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MISTER-CHAN/icon-editor/HEAD/app/src/main/res/drawable/bracket_horiz_open.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bracket_vert_close.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MISTER-CHAN/icon-editor/HEAD/app/src/main/res/drawable/bracket_vert_close.9.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MISTER-CHAN/icon-editor/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MISTER-CHAN/icon-editor/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MISTER-CHAN/icon-editor/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MISTER-CHAN/icon-editor/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MISTER-CHAN/icon-editor/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/render.experimental.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/chessboard.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/provider_paths.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
--------------------------------------------------------------------------------
/.idea/AndroidProjectSystem.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/deploymentTargetDropDown.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/Guide.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor;
2 |
3 | public class Guide {
4 | public static final boolean ORIENTATION_HORIZONTAL = false, ORIENTATION_VERTICAL = true;
5 |
6 | public boolean orientation;
7 | public int position;
8 | }
9 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Jun 16 23:21:44 CST 2025
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/util/RunnableRunnable.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.util;
2 |
3 | public interface RunnableRunnable {
4 | /**
5 | * @param wait Wait for the lock.
6 | */
7 | void runRunnable(final Runnable target, final boolean wait);
8 | }
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/color_picker.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.idea/migrations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/bracket.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_void.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/color/item_checked.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/layer_visible.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/tool/Shape.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.tool;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Rect;
5 |
6 | public interface Shape {
7 | Rect mapRect(int x0, int y0, int x1, int y1);
8 |
9 | String drawShapeOntoCanvas(Canvas canvas, int x0, int y0, int x1, int y1);
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/settings_activity.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/listener/OnAdapterViewItemSelectedListener.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.listener;
2 |
3 | import android.widget.AdapterView;
4 |
5 | public interface OnAdapterViewItemSelectedListener extends AdapterView.OnItemSelectedListener {
6 | @Override
7 | default void onNothingSelected(AdapterView> parent) {
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/ui/CoordinateConversions.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.ui;
2 |
3 | public interface CoordinateConversions {
4 | int toBitmapX(float x);
5 |
6 | int toBitmapY(float y);
7 |
8 | float toViewX(int x);
9 |
10 | float toViewX(float x);
11 |
12 | float toViewY(int y);
13 |
14 | float toViewY(float y);
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_add.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_done.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_expand_more.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_format_italic.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_arrow_upward.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_format_strikethrough.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/brush_tip_shape.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_arrow_downward.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/tool/Ruler.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.tool;
2 |
3 | public class Ruler {
4 | public boolean enabled = false;
5 | public int startX, startY;
6 | public int stopX, stopY;
7 |
8 | public void set(int startX, int startY, int stopX, int stopY) {
9 | this.startX = startX;
10 | this.startY = startY;
11 | this.stopX = stopX;
12 | this.stopY = stopY;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_delete.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | maven { url 'https://repo1.maven.org/maven2' }
14 | }
15 | }
16 | rootProject.name = "IconEditor"
17 | include ':app'
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_crop.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_arrow_back.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_clear.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_close.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_info.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_format_align_center.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_layers.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_transform.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/listener/OnItemSelectedListener.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.listener;
2 |
3 | import android.view.View;
4 | import android.widget.AdapterView;
5 |
6 | public interface OnItemSelectedListener extends AdapterView.OnItemSelectedListener {
7 |
8 | @Override
9 | void onItemSelected(AdapterView> parent, View view, int position, long id);
10 |
11 | @Override
12 | default void onNothingSelected(AdapterView> parent) {
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_movie.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFD0BCFF
4 | #FFCCC2DC
5 | #FFEFB8C8
6 | #FFFFFFFF
7 |
8 | #FF6650A4
9 | #FF625B71
10 | #FF7D5260
11 | #FF000000
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_contrast.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_image.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_photo_size_select_actual.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/test/java/com/misterchan/iconeditor/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_filter_b_and_w.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_folder_open.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_format_align_right.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_format_underlined.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/listener/OnCBCheckedListener.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.listener;
2 |
3 | import android.widget.CompoundButton;
4 |
5 | public interface OnCBCheckedListener extends CompoundButton.OnCheckedChangeListener {
6 |
7 | void onChecked(CompoundButton buttonView);
8 |
9 | @Override
10 | default void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
11 | if (isChecked) {
12 | onChecked(buttonView);
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_format_align_left.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_redo.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_undo.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_edit.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_content_copy.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_insert_drive_file.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_save.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_create_new_folder.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/new-issue.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: New issue
3 | about: Open a new issue
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 |
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/ui/ViewModel.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.ui;
2 |
3 | import android.graphics.Color;
4 |
5 | import com.misterchan.iconeditor.Project;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | public class ViewModel extends androidx.lifecycle.ViewModel {
11 | private List projects;
12 |
13 | public List getProjects() {
14 | if (projects == null) {
15 | projects = new ArrayList<>();
16 | }
17 | return projects;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_brightness_5.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_more_horiz.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_more_vert.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_fit_screen.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/action_mode_color.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_drive_file_rename_outline.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_filter.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_push_pin.xml:
--------------------------------------------------------------------------------
1 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/listener/OnSliderValueChangeListener.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.listener;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.google.android.material.slider.Slider;
6 |
7 | public interface OnSliderValueChangeListener extends Slider.OnChangeListener {
8 | void onValueChange(@NonNull Slider slider, float value);
9 |
10 | @Override
11 | default void onValueChange(@NonNull Slider slider, float value, boolean fromUser) {
12 | if (fromUser) {
13 | onValueChange(slider, value);
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_flip.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_opacity.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/progress_indicator.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_visibility.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/FloatingLayer.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor;
2 |
3 | import android.graphics.Bitmap;
4 |
5 | public interface FloatingLayer {
6 | default boolean hasRect() {
7 | return false;
8 | }
9 |
10 | Bitmap getBitmap();
11 |
12 | default int getLeft() {
13 | return 0;
14 | }
15 |
16 | default int getTop() {
17 | return 0;
18 | }
19 |
20 | default int getWidth() {
21 | return getBitmap().getWidth();
22 | }
23 |
24 | default int getHeight() {
25 | return getBitmap().getHeight();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_content_paste.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_grid_on.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_history.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_color.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_format_bold.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_rotate_90_degrees_cw.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/slider.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/listener/OnButtonCheckedListener.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.listener;
2 |
3 | import androidx.annotation.IdRes;
4 |
5 | import com.google.android.material.button.MaterialButtonToggleGroup;
6 |
7 | public interface OnButtonCheckedListener extends MaterialButtonToggleGroup.OnButtonCheckedListener {
8 | void onButtonChecked(MaterialButtonToggleGroup group, @IdRes int checkedId);
9 |
10 | default void onButtonChecked(MaterialButtonToggleGroup group, @IdRes int checkedId, boolean isChecked) {
11 | if (isChecked) {
12 | onButtonChecked(group, checkedId);
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_access_time.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_save_as.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/listener/AfterTextChangedListener.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.listener;
2 |
3 | import android.text.Editable;
4 | import android.text.TextWatcher;
5 |
6 | public interface AfterTextChangedListener extends TextWatcher {
7 |
8 | void afterTextChanged(String s);
9 |
10 | @Override
11 | default void afterTextChanged(Editable s) {
12 | afterTextChanged(s.toString());
13 | }
14 |
15 | @Override
16 | default void beforeTextChanged(CharSequence s, int start, int count, int after) {
17 | }
18 |
19 | @Override
20 | default void onTextChanged(CharSequence s, int start, int before, int count) {
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_photo_size_select_large.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/color_picker_hsv.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/Reference.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Rect;
5 |
6 | public class Reference {
7 | private Bitmap bitmap;
8 |
9 | public Bitmap bm() {
10 | return bitmap;
11 | }
12 |
13 | public void recycle() {
14 | if (bitmap != null) {
15 | bitmap.recycle();
16 | bitmap = null;
17 | }
18 | }
19 |
20 | public boolean recycled() {
21 | return bitmap == null;
22 | }
23 |
24 | public void set(Bitmap bm) {
25 | recycle();
26 | if (bm == null) {
27 | return;
28 | }
29 | bitmap = bm;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_select_all.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_balance.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/frame_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_deselect.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/main/res/layout/top_app_bar.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
12 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/palette.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_content_cut.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/listener/OnRSChangeListener.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.listener;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.google.android.material.slider.RangeSlider;
6 |
7 | public interface OnRSChangeListener extends RangeSlider.OnChangeListener, RangeSlider.OnSliderTouchListener {
8 | void onChange(@NonNull RangeSlider slider, boolean stopped);
9 |
10 | @Override
11 | default void onStartTrackingTouch(@NonNull RangeSlider slider) {
12 | }
13 |
14 | @Override
15 | default void onStopTrackingTouch(@NonNull RangeSlider slider) {
16 | onChange(slider, true);
17 | }
18 |
19 | @Override
20 | default void onValueChange(@NonNull RangeSlider slider, float value, boolean fromUser) {
21 | if (fromUser) {
22 | onChange(slider, false);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/listener/OnSliderChangeListener.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.listener;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.annotation.Nullable;
5 |
6 | import com.google.android.material.slider.Slider;
7 |
8 | public interface OnSliderChangeListener extends Slider.OnChangeListener, Slider.OnSliderTouchListener {
9 | void onChange(@Nullable Slider slider, float value, boolean stopped);
10 |
11 | @Override
12 | default void onStartTrackingTouch(@NonNull Slider slider) {
13 | }
14 |
15 | @Override
16 | default void onStopTrackingTouch(@NonNull Slider slider) {
17 | onChange(slider, slider.getValue(), true);
18 | }
19 |
20 | @Override
21 | default void onValueChange(@NonNull Slider slider, float value, boolean fromUser) {
22 | if (fromUser) {
23 | onChange(slider, value, false);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_palette.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/misterchan/iconeditor/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 | assertEquals("com.misterchan.iconeditor", appContext.getPackageName());
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_visibility_off.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/proj_tab.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/options_bucket_fill.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
18 |
19 |
25 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/ColorRange.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor;
2 |
3 | import androidx.annotation.FloatRange;
4 | import androidx.annotation.Size;
5 |
6 | import java.util.Arrays;
7 |
8 | public class ColorRange {
9 | public boolean enabled = false;
10 |
11 | @Size(6)
12 | public float[] cuboid; // HSV
13 |
14 | @FloatRange(from = 0.0f, to = 1.0f)
15 | public float transition;
16 |
17 | public ColorRange() {
18 | cuboid = new float[]{0.0f, 0.0f, 0.0f, 360.0f, 1.0f, 1.0f};
19 | transition = 0.0f;
20 | }
21 |
22 | public void set(ColorRange src) {
23 | if (!enabled && !src.enabled) {
24 | return;
25 | }
26 | enabled = src.enabled;
27 | transition = src.transition;
28 | cuboid = Arrays.copyOf(src.cuboid, 6);
29 | }
30 |
31 | public void update() {
32 | enabled = cuboid[0] != 0.0f || cuboid[1] != 0.0f || cuboid[2] != 0.0f
33 | || cuboid[3] != 360.0f || cuboid[4] != 1.0f || cuboid[5] != 1.0f;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/edit_text.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
22 |
23 |
--------------------------------------------------------------------------------
/.idea/appInsightsSettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/util/CanvasUtils.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.util;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Paint;
5 |
6 | public class CanvasUtils {
7 | private CanvasUtils() {
8 | }
9 |
10 | public static void drawInclusiveLine(Canvas canvas, int startX, int startY, int stopX, int stopY, Paint paint) {
11 | if (startX <= stopX) ++stopX;
12 | else ++startX;
13 | if (startY <= stopY) ++stopY;
14 | else ++startY;
15 |
16 | canvas.drawLine(startX, startY, stopX, stopY, paint);
17 | }
18 |
19 | public static void drawRightAngles(Canvas canvas, float l, float t, float r, float b, Paint paint) {
20 | canvas.drawLine(l, t, l - 100.0f, t, paint);
21 | canvas.drawLine(r, t, r + 100.0f, t, paint);
22 | canvas.drawLine(r, t - 100.0f, r, t, paint);
23 | canvas.drawLine(r, b, r, b + 100.0f, paint);
24 | canvas.drawLine(r + 100.0f, b, r, b, paint);
25 | canvas.drawLine(l, b, l - 100.0f, b, paint);
26 | canvas.drawLine(l, b + 100.0f, l, b, paint);
27 | canvas.drawLine(l, t, l, t - 100.0f, paint);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_settings.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/tool/Gradient.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.tool;
2 |
3 | import android.graphics.LinearGradient;
4 | import android.graphics.RadialGradient;
5 | import android.graphics.Shader;
6 | import android.graphics.SweepGradient;
7 |
8 | import androidx.annotation.ColorLong;
9 |
10 | public class Gradient {
11 | public enum Colors {
12 | PAINTS, PALETTE
13 | }
14 |
15 | public enum Type {
16 | LINEAR, RADIAL, SWEEP
17 | }
18 |
19 | public Colors colors = Colors.PAINTS;
20 | public Type type = Type.LINEAR;
21 |
22 | public Shader createShader(float x0, float y0, float x1, float y1, @ColorLong long... colors) {
23 | try {
24 | return switch (type) {
25 | case LINEAR ->
26 | new LinearGradient(x0, y0, x1, y1, colors, null, Shader.TileMode.CLAMP);
27 |
28 | case RADIAL -> new RadialGradient(x0, y0,
29 | (float) Math.hypot(Math.abs(x0 - x1), Math.abs(y0 - y1)), colors, null, Shader.TileMode.CLAMP);
30 |
31 | case SWEEP -> new SweepGradient(x0, y0, colors, null);
32 | };
33 | } catch (IllegalArgumentException e) {
34 | return null;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/.idea/deploymentTargetSelector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_language.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/animation_trimmer.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
19 |
20 |
25 |
26 |
27 |
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/tool/TextTool.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.tool;
2 |
3 | import android.graphics.Paint;
4 | import android.graphics.Rect;
5 |
6 | public class TextTool {
7 | public int x, y;
8 | private final Rect bounds = new Rect();
9 | private final Rect rect = new Rect();
10 | public String s = "";
11 |
12 | public Rect getMeasuredBounds(Paint paint, float outset) {
13 | Paint.FontMetrics fm = paint.getFontMetrics();
14 | return offset(paint.getTextAlign(), fm.top, outset);
15 | }
16 |
17 | public Rect measure(Paint paint, float outset) {
18 | Paint.FontMetrics fm = paint.getFontMetrics();
19 | bounds.set(0, 0, (int) Math.ceil(paint.measureText(s)), (int) Math.ceil(fm.bottom - fm.top));
20 | return offset(paint.getTextAlign(), fm.top, outset);
21 | }
22 |
23 | private Rect offset(Paint.Align align, float top, float outset) {
24 | Rect r = new Rect(rect);
25 | rect.set(bounds);
26 | rect.offset(switch (align) {
27 | case LEFT -> 0;
28 | case CENTER -> -(bounds.right >> 1);
29 | case RIGHT -> -bounds.right;
30 | } + x, (int) Math.floor(y + top));
31 | int o = (int) Math.ceil(outset);
32 | rect.inset(-o, -o);
33 | r.union(rect);
34 | return r;
35 | }
36 |
37 | public Rect rect() {
38 | return rect;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/options_gradient.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
21 |
22 |
28 |
29 |
36 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Enables namespacing of each library's R class so that its R class includes only the
19 | # resources declared in the library itself and none from the library's dependencies,
20 | # thereby reducing the size of the R class for that library
21 | android.nonTransitiveRClass=true
22 | android.defaults.buildfeatures.buildconfig=true
23 | android.nonFinalResIds=false
24 | org.gradle.unsafe.configuration-cache=true
--------------------------------------------------------------------------------
/app/src/main/res/layout/options_eyedropper.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
23 |
24 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fill_with_clip.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
17 |
18 |
25 |
26 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/DrawingPrimitivePreview.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BlendMode;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.graphics.Paint;
8 | import android.graphics.Rect;
9 |
10 | import com.misterchan.iconeditor.util.BitmapUtils;
11 |
12 | public class DrawingPrimitivePreview implements FloatingLayer {
13 | private Bitmap bitmap;
14 | private Canvas canvas;
15 |
16 | public void erase() {
17 | if (bitmap == null) return;
18 | bitmap.eraseColor(Color.TRANSPARENT);
19 | }
20 |
21 | public void erase(Rect rect) {
22 | canvas.drawRect(rect, BitmapUtils.PAINT_CLEAR);
23 | }
24 |
25 | @Override
26 | public Bitmap getBitmap() {
27 | return bitmap;
28 | }
29 |
30 | public Canvas getCanvas() {
31 | return canvas;
32 | }
33 |
34 | public boolean isRecycled() {
35 | return bitmap == null;
36 | }
37 |
38 | public void recycle() {
39 | if (bitmap == null) {
40 | return;
41 | }
42 | bitmap.recycle();
43 | bitmap = null;
44 | canvas = null;
45 | }
46 |
47 | public void setBitmap(int width, int height) {
48 | if (bitmap != null) {
49 | bitmap.recycle();
50 | }
51 | bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
52 | canvas = new Canvas(bitmap);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/res/values-land/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
13 |
14 |
18 |
19 |
26 |
27 |
30 |
31 |
34 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/frame.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/tool/CloneStamp.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.tool;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.graphics.Point;
7 | import android.graphics.Rect;
8 | import android.graphics.RectF;
9 |
10 | import com.misterchan.iconeditor.util.BitmapUtils;
11 |
12 | public class CloneStamp {
13 | public Point src;
14 |
15 | public void paint(Canvas canvas, Bitmap bitmap,
16 | float left, float top, int width, int height, int dx, int dy,
17 | float startX, float startY, float stopX, float stopY,
18 | Paint paint) {
19 | final int l = (int) (left + dx), t = (int) (top + dy);
20 | final Bitmap bm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
21 | final Canvas cv = new Canvas(bm);
22 | cv.drawLine(startX, startY, stopX, stopY, paint);
23 | cv.drawRect(0.0f, 0.0f, -l, height, BitmapUtils.PAINT_CLEAR);
24 | cv.drawRect(0.0f, 0.0f, width, -t, BitmapUtils.PAINT_CLEAR);
25 | cv.drawRect(bitmap.getWidth() - l, 0.0f, width, height, BitmapUtils.PAINT_CLEAR);
26 | cv.drawRect(0.0f, bitmap.getHeight() - t, width, height, BitmapUtils.PAINT_CLEAR);
27 | cv.drawBitmap(bitmap,
28 | new Rect(l, t, l + width, t + height),
29 | new RectF(0.0f, 0.0f, width, height),
30 | BitmapUtils.PAINT_SRC_IN);
31 | canvas.drawBitmap(bm, left, top, BitmapUtils.PAINT_SRC_OVER);
32 | bm.recycle();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | }
4 |
5 | android {
6 | namespace 'com.misterchan.iconeditor'
7 | compileSdk 35
8 |
9 | defaultConfig {
10 | applicationId "com.misterchan.iconeditor"
11 | minSdk 30
12 | targetSdk 35
13 | versionCode 1
14 | versionName '25.6.17'
15 |
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 | compileOptions {
26 | sourceCompatibility JavaVersion.VERSION_20
27 | targetCompatibility JavaVersion.VERSION_20
28 | }
29 | buildFeatures {
30 | viewBinding true
31 | }
32 | }
33 |
34 | dependencies {
35 |
36 | implementation 'androidx.activity:activity:1.10.1'
37 | implementation 'androidx.appcompat:appcompat:1.7.1'
38 | implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
39 | implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.1'
40 | implementation 'androidx.preference:preference:1.2.1'
41 | implementation 'com.google.android.material:material:1.12.0'
42 | implementation('io.github.waynejo:androidndkgif:1.0.1')
43 | implementation(platform('org.jetbrains.kotlin:kotlin-bom:2.1.21'))
44 | testImplementation 'junit:junit:4.13.2'
45 | androidTestImplementation 'androidx.test.ext:junit:1.2.1'
46 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
47 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_frame.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
16 |
17 |
21 |
22 |
28 |
29 |
35 |
36 |
37 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/action_mode_transformer.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/merge_as_hidden.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
21 |
22 |
26 |
27 |
33 |
34 |
38 |
39 |
45 |
46 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/lighting.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
24 |
25 |
28 |
29 |
36 |
37 |
40 |
41 |
42 |
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/colorpicker/HslColorPicker.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.colorpicker;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.graphics.Color;
5 |
6 | import androidx.annotation.ColorInt;
7 | import androidx.annotation.IntRange;
8 | import androidx.annotation.Size;
9 |
10 | import com.misterchan.iconeditor.Settings;
11 | import com.misterchan.iconeditor.util.ColorUtils;
12 |
13 | public class HslColorPicker extends ColorPicker {
14 | @Size(3)
15 | private final float[] hsl = new float[3];
16 |
17 | @ColorInt
18 | private int colorInt;
19 |
20 | @SuppressLint("ClickableViewAccessibility")
21 | public HslColorPicker(long color) {
22 | prop = new Properties("H", "S", "L", true);
23 |
24 | setColorFromRgb(color);
25 | }
26 |
27 | @Override
28 | public long color() {
29 | return Color.pack(colorInt);
30 | }
31 |
32 | @Override
33 | public int colorInt() {
34 | return colorInt;
35 | }
36 |
37 | @Override
38 | public float getComponent(int index) {
39 | return index == 0 ? hsl[0] : hsl[index] * 100.0f;
40 | }
41 |
42 | @Override
43 | public void setAlpha(float a) {
44 | colorInt = ColorUtils.setAlpha(colorInt, (int) (Settings.INST.colorRep() ? a * 0xFF : a));
45 | }
46 |
47 | @Override
48 | public void setComponent(@IntRange(from = 0, to = 2) int index, float c) {
49 | if (index == 1 || index == 2) c /= 100.0f;
50 | if (hsl[index] == c) return;
51 | hsl[index] = c;
52 | colorInt = colorInt & Color.BLACK | ColorUtils.HSLToColor(hsl);
53 | }
54 |
55 | private void setColorFromRgb(long color) {
56 | colorInt = Color.toArgb(color);
57 | ColorUtils.colorToHSL(colorInt, hsl);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/CellGrid.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Paint;
5 |
6 | public class CellGrid {
7 | public boolean enabled = false;
8 |
9 | public int sizeX = 16, sizeY = 16;
10 |
11 | public int spacingX = 0, spacingY = 0;
12 |
13 | public int offsetX = 0, offsetY = 0;
14 |
15 | public void draw(Canvas canvas, float l, float t, float r, float b, float tx, float ty, float s, Paint paint) {
16 | if (sizeX + spacingX > 1) {
17 | final float scaledSizeX = sizeX * s, scaledSpacingX = spacingX * s;
18 | float x = (tx >= 0.0f ? tx : tx % (scaledSizeX + scaledSpacingX)) + offsetX % (sizeX + spacingX) * s;
19 | while (true) {
20 | if (x >= l) canvas.drawLine(x, t, x, b, paint);
21 | if ((x += scaledSizeX) > r) break;
22 | if (spacingX != 0) {
23 | if (x >= l) canvas.drawLine(x, t, x, b, paint);
24 | if ((x += scaledSpacingX) > r) break;
25 | }
26 | }
27 | }
28 | if (sizeY + spacingY > 1) {
29 | final float scaledSizeY = sizeY * s, scaledSpacingY = spacingY * s;
30 | float y = (ty >= 0.0f ? ty : ty % (scaledSizeY + scaledSpacingY)) + offsetY % (sizeY + spacingY) * s;
31 | if (y < ty) y += scaledSizeY + scaledSpacingY;
32 | while (true) {
33 | if (y >= t) canvas.drawLine(l, y, r, y, paint);
34 | if ((y += scaledSizeY) > b) break;
35 | if (spacingY != 0) {
36 | if (y >= t) canvas.drawLine(l, y, r, y, paint);
37 | if ((y += scaledSpacingY) > b) break;
38 | }
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/dialog/FilterDialog.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.dialog;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.appcompat.app.AlertDialog;
6 |
7 | import com.google.android.material.dialog.MaterialAlertDialogBuilder;
8 | import com.misterchan.iconeditor.R;
9 |
10 | public abstract class FilterDialog {
11 | final AlertDialog.Builder builder;
12 |
13 | FilterDialog(Context context) {
14 | builder = new MaterialAlertDialogBuilder(context);
15 | }
16 |
17 | abstract void onFilterCommit();
18 |
19 | public FilterDialog setOnActionListener(Runnable onPBClickListener, Runnable onCancelListener) {
20 | return setOnActionListener(onPBClickListener, onCancelListener, true);
21 | }
22 |
23 | public FilterDialog setOnActionListener(Runnable onPBClickListener,
24 | Runnable onCancelListener, boolean showCancelButton) {
25 | builder.setPositiveButton(R.string.ok, (dialog, which) -> {
26 | onPBClickListener.run();
27 | onFilterCommit();
28 | });
29 |
30 | builder.setOnCancelListener(dialog -> onCancelListener.run());
31 | if (showCancelButton) {
32 | builder.setNegativeButton(R.string.cancel, (dialog, which) -> onCancelListener.run());
33 | }
34 |
35 | return this;
36 | }
37 |
38 | public FilterDialog setOnActionListener(Runnable onDismissListener) {
39 | builder.setOnDismissListener(dialog -> onDismissListener.run());
40 | builder.setPositiveButton(R.string.ok, null);
41 | return this;
42 | }
43 |
44 | public FilterDialog setOkButton() {
45 | builder.setPositiveButton(R.string.ok, null);
46 | return this;
47 | }
48 |
49 | public abstract void show();
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/file_name.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
20 |
21 |
28 |
29 |
30 |
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/Project.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor;
2 |
3 | import android.graphics.Bitmap;
4 |
5 | import com.google.android.material.tabs.TabLayout;
6 | import com.misterchan.iconeditor.ui.FrameAdapter;
7 | import com.waynejo.androidndkgif.GifEncoder;
8 |
9 | import java.util.ArrayList;
10 | import java.util.Deque;
11 | import java.util.LinkedList;
12 | import java.util.List;
13 |
14 | public class Project {
15 | public enum FileType {
16 | PNG, JPEG, GIF, WEBP
17 | }
18 |
19 | public boolean gifDither = true;
20 | public Bitmap.CompressFormat compressFormat;
21 | public final CellGrid cellGrid = new CellGrid();
22 | public float scale = Float.NaN;
23 | public float translationX = Float.NaN, translationY = Float.NaN;
24 | public FrameAdapter frameAdapter = new FrameAdapter(this);
25 | public FileType fileType;
26 | public GifEncoder.EncodingType gifEncodingType;
27 | public final Deque guides = new LinkedList<>();
28 | public final History history = new History();
29 | public int onionSkins = 1;
30 | public int quality = -1;
31 | public int selectedFrameIndex = 0;
32 | public final List frames = new ArrayList<>();
33 | public String filePath;
34 | private String title;
35 | public TabLayout.Tab tab;
36 |
37 | public Frame getFirstFrame() {
38 | return frames.get(0);
39 | }
40 |
41 | public String getName() {
42 | final int i = title.lastIndexOf('.');
43 | return i == -1 ? title : title.substring(0, i);
44 | }
45 |
46 | public Frame getSelectedFrame() {
47 | return frames.get(selectedFrameIndex);
48 | }
49 |
50 | public String getTitle() {
51 | return title;
52 | }
53 |
54 | public void setTitle(String title) {
55 | this.title = title;
56 | if (tab != null) {
57 | tab.setText(title);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/tool/MagicPaint.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.tool;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.graphics.Rect;
8 |
9 | import com.misterchan.iconeditor.util.BitmapUtils;
10 |
11 | public class MagicPaint {
12 | private MagicPaint() {
13 | }
14 |
15 | public static void draw(Canvas canvas, Bitmap source,
16 | int threshold,
17 | int left, int top, int right, int bottom,
18 | int startX, int startY, int stopX, int stopY,
19 | Paint magicPaint, Paint paint) {
20 | final int width = right - left, height = bottom - top;
21 | final Bitmap bLine = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
22 | final Canvas cLine = new Canvas(bLine);
23 | cLine.drawLine(startX, startY, stopX, stopY, paint);
24 | if (threshold < 0xFF) {
25 | final Bitmap bm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
26 | final Canvas cv = new Canvas(bm);
27 | cv.drawBitmap(bLine, 0.0f, 0.0f, BitmapUtils.PAINT_SRC);
28 | cv.drawBitmap(source,
29 | new Rect(left, top, right, bottom), new Rect(0, 0, width, height),
30 | BitmapUtils.PAINT_SRC_IN);
31 | final Bitmap bThr = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444); // Threshold
32 | BitmapUtils.floodFill(bm, null, bThr, null,
33 | stopX, stopY, Color.BLACK, true, threshold);
34 | bm.recycle();
35 | cLine.drawBitmap(bThr, 0.0f, 0.0f, BitmapUtils.PAINT_DST_IN);
36 | bThr.recycle();
37 | }
38 | canvas.drawBitmap(bLine, left, top, magicPaint);
39 | bLine.recycle();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/options_patcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
20 |
21 |
22 |
26 |
27 |
35 |
36 |
37 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/color_picker_tabs.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
18 |
19 |
23 |
24 |
28 |
29 |
33 |
34 |
38 |
39 |
43 |
44 |
48 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/options_gradient_line.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
20 |
21 |
22 |
26 |
27 |
35 |
36 |
37 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/options_brush.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
20 |
21 |
22 |
26 |
27 |
35 |
36 |
37 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/options_eraser.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
21 |
22 |
23 |
27 |
28 |
36 |
37 |
38 |
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/util/LightingToLevels.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.util;
2 |
3 | import androidx.annotation.Size;
4 |
5 | public class LightingToLevels {
6 | private static final int IS = 0, IH = 1, OS = 2, OH = 3;
7 | private static final int[] IS_IH_OS_OH = {IS, IH, OS, OH}, IS_OH_OS_IH = {IS, OH, OS, IH}, OS_IH_IS_OH = {OS, IH, IS, OH}, OS_OH_IS_IH = {OS, OH, IS, IH};
8 | private static final int[][] IND = {OS_OH_IS_IH, OS_IH_IS_OH, OS_IH_IS_OH, OS_IH_IS_OH, IS_OH_OS_IH, IS_OH_OS_IH, IS_OH_OS_IH, IS_IH_OS_OH, OS_OH_IS_IH, OS_IH_IS_OH, IS_OH_OS_IH, IS_IH_OS_OH};
9 | private static final float[] V_00_00 = {0x00, 0x00}, V_00_FF = {0x00, 0xFF}, V_FF_00 = {0xFF, 0x00}, V_FF_FF = {0xFF, 0xFF};
10 | private static final float[][] VAL = {V_00_FF, V_00_FF, V_00_00, V_FF_FF, V_00_FF, V_00_00, V_FF_FF, V_00_FF, V_FF_00, V_FF_00, V_FF_00, V_FF_00};
11 |
12 | private interface Func {
13 | float apply(float mul, float add, float[] arr);
14 | }
15 |
16 | private static final Func[] F = {(scale, shift, arr) -> (shift - arr[OS]) / -scale, (scale, shift, arr) -> (arr[OH] - arr[OS]) / scale + arr[IS], (scale, shift, arr) -> shift + arr[IS] * scale, (scale, shift, arr) -> (arr[IH] - arr[IS]) * scale + arr[OS]};
17 |
18 | private LightingToLevels() {
19 | }
20 |
21 | @Size(4)
22 | public static float[] lightingToLevels(float mul, float add) {
23 | if (Float.isInfinite(mul) || Float.isNaN(mul) || Float.isInfinite(add) || Float.isNaN(add))
24 | return new float[]{0x00, 0x00, 0x00, 0xFF};
25 |
26 | float[] arr = new float[4];
27 | for (int i = 0; ; ++i) {
28 | arr[IND[i][0]] = VAL[i][0];
29 | arr[IND[i][1]] = VAL[i][1];
30 | arr[IND[i][2]] = F[IND[i][2]].apply(mul, add, arr);
31 | arr[IND[i][3]] = F[IND[i][3]].apply(mul, add, arr);
32 | if (0x00 <= arr[IND[i][2]] && arr[IND[i][2]] <= 0xFF && 0x00 <= arr[IND[i][3]] && arr[IND[i][3]] <= 0xFF)
33 | return arr;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/dialog/NewImageDialog.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.dialog;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.appcompat.app.AlertDialog;
6 |
7 | import com.google.android.material.dialog.MaterialAlertDialogBuilder;
8 | import com.google.android.material.textfield.TextInputEditText;
9 | import com.misterchan.iconeditor.R;
10 | import com.misterchan.iconeditor.Settings;
11 |
12 | public class NewImageDialog {
13 |
14 | public interface OnApplyListener {
15 | void onApply(int width, int height);
16 | }
17 |
18 | private final AlertDialog.Builder builder;
19 | private TextInputEditText tietWidth, tietHeight;
20 |
21 | public NewImageDialog(Context context) {
22 | builder = new MaterialAlertDialogBuilder(context)
23 | .setNegativeButton(R.string.cancel, null)
24 | .setTitle(R.string.new_)
25 | .setView(R.layout.new_image);
26 | }
27 |
28 | public NewImageDialog setOnFinishSettingListener(OnApplyListener listener) {
29 | builder.setPositiveButton(R.string.ok, (dialog, which) -> {
30 | final int width, height;
31 | try {
32 | width = Integer.parseUnsignedInt(tietWidth.getText().toString());
33 | height = Integer.parseUnsignedInt(tietHeight.getText().toString());
34 | } catch (NumberFormatException e) {
35 | return;
36 | }
37 | if (!(width > 0 && height > 0)) {
38 | return;
39 | }
40 | listener.onApply(width, height);
41 | });
42 | return this;
43 | }
44 |
45 | public void show() {
46 | final AlertDialog dialog = builder.show();
47 |
48 | tietWidth = dialog.findViewById(R.id.tiet_width);
49 | tietHeight = dialog.findViewById(R.id.tiet_height);
50 |
51 | tietWidth.setText(String.valueOf(Settings.INST.newImageWidth()));
52 | tietHeight.setText(String.valueOf(Settings.INST.newImageHeight()));
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/tools.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
17 |
18 |
19 |
20 |
24 |
25 |
26 |
27 |
28 |
32 |
33 |
34 |
35 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/colorpicker/XyzColorPicker.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.colorpicker;
2 |
3 | import android.graphics.Color;
4 | import android.graphics.ColorSpace;
5 |
6 | import androidx.annotation.ColorLong;
7 | import androidx.annotation.Size;
8 |
9 | import com.misterchan.iconeditor.Settings;
10 | import com.misterchan.iconeditor.util.ColorUtils;
11 |
12 | public class XyzColorPicker extends ColorPicker {
13 | public static final ColorSpace XYZ = ColorSpace.get(ColorSpace.Named.CIE_XYZ);
14 |
15 | private final ColorSpace.Connector connectorFromXyz, connectorToSrgb;
16 |
17 | @Size(3)
18 | private final float[] xyz;
19 |
20 | @ColorLong
21 | private long color;
22 |
23 | public XyzColorPicker(long color) {
24 | prop = new Properties("X", "Y", "Z",
25 | -2.0f, 2.0f, -2.0f, 2.0f, -2.0f, 2.0f,
26 | EDITOR_TYPE_NUM_DEC_SIGNED, EDITOR_TYPE_NUM_DEC_SIGNED, EDITOR_TYPE_NUM_DEC_SIGNED);
27 |
28 | ColorSpace colorSpace = Color.colorSpace(color);
29 | connectorFromXyz = ColorSpace.connect(XYZ, colorSpace);
30 | ColorSpace.Connector connectorToXyz = ColorSpace.connect(colorSpace, XYZ);
31 | connectorToSrgb = ColorSpace.connect(colorSpace, SRGB);
32 |
33 | this.color = color;
34 | xyz = connectorToXyz.transform(Color.red(color), Color.green(color), Color.blue(color));
35 | }
36 |
37 | @Override
38 | public long color() {
39 | return color;
40 | }
41 |
42 | @Override
43 | public int colorInt() {
44 | return ColorUtils.convert(color, connectorToSrgb);
45 | }
46 |
47 | @Override
48 | public float getComponent(int index) {
49 | return xyz[index];
50 | }
51 |
52 | @Override
53 | public void setAlpha(float a) {
54 | color = ColorUtils.setAlpha(color, Settings.INST.colorRep() ? a : a / 0xFF);
55 | }
56 |
57 | @Override
58 | public void setComponent(int index, float c) {
59 | xyz[index] = c;
60 | color = Color.convert(xyz[0], xyz[1], xyz[2], Color.alpha(color), connectorFromXyz);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/colorpicker/LabColorPicker.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.colorpicker;
2 |
3 | import android.graphics.Color;
4 | import android.graphics.ColorSpace;
5 |
6 | import androidx.annotation.ColorLong;
7 | import androidx.annotation.Size;
8 |
9 | import com.misterchan.iconeditor.Settings;
10 | import com.misterchan.iconeditor.util.ColorUtils;
11 |
12 | public class LabColorPicker extends ColorPicker {
13 | private static final ColorSpace LAB = ColorSpace.get(ColorSpace.Named.CIE_LAB);
14 |
15 | private final ColorSpace.Connector connectorFromLab, connectorToSrgb;
16 |
17 | @Size(3)
18 | private final float[] lab;
19 |
20 | @ColorLong
21 | private long color;
22 |
23 | public LabColorPicker(long color) {
24 | prop = new Properties("L*", "a*", "b*",
25 | 0.0f, 100.0f, -128.0f, 128.0f, -128.0f, 128.0f,
26 | EDITOR_TYPE_NUM_DEC, EDITOR_TYPE_NUM_DEC_SIGNED, EDITOR_TYPE_NUM_DEC_SIGNED);
27 |
28 | ColorSpace colorSpace = Color.colorSpace(color);
29 | connectorFromLab = ColorSpace.connect(LAB, colorSpace);
30 | ColorSpace.Connector connectorToLab = ColorSpace.connect(colorSpace, LAB);
31 | connectorToSrgb = ColorSpace.connect(colorSpace, SRGB);
32 |
33 | this.color = color;
34 | lab = connectorToLab.transform(Color.red(color), Color.green(color), Color.blue(color));
35 | }
36 |
37 | @Override
38 | public long color() {
39 | return color;
40 | }
41 |
42 | @Override
43 | public int colorInt() {
44 | return ColorUtils.convert(color, connectorToSrgb);
45 | }
46 |
47 | @Override
48 | public float getComponent(int index) {
49 | return lab[index];
50 | }
51 |
52 | @Override
53 | public void setAlpha(float a) {
54 | color = ColorUtils.setAlpha(color, Settings.INST.colorRep() ? a : a / 0xFF);
55 | }
56 |
57 | @Override
58 | public void setComponent(int index, float c) {
59 | lab[index] = c;
60 | color = Color.convert(lab[0], lab[1], lab[2], Color.alpha(color), connectorFromLab);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/options_pencil.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
20 |
21 |
22 |
26 |
27 |
35 |
36 |
37 |
42 |
43 |
48 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/options_clone_stamp.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
20 |
21 |
22 |
26 |
27 |
35 |
36 |
37 |
42 |
43 |
50 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/options_shape.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
21 |
22 |
23 |
27 |
28 |
36 |
37 |
38 |
43 |
44 |
50 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/options_text.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
22 |
23 |
24 |
28 |
29 |
37 |
38 |
39 |
44 |
45 |
51 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/colorpicker/CmykColorPicker.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.colorpicker;
2 |
3 | import android.graphics.Color;
4 | import android.graphics.ColorSpace;
5 |
6 | import androidx.annotation.ColorInt;
7 | import androidx.annotation.ColorLong;
8 | import androidx.annotation.Size;
9 |
10 | import com.misterchan.iconeditor.Settings;
11 | import com.misterchan.iconeditor.util.ColorUtils;
12 |
13 | public class CmykColorPicker extends ColorPicker {
14 | private final ColorSpace colorSpace;
15 | private final ColorSpace.Connector connectorToSrgb;
16 |
17 | @Size(4)
18 | private final float[] cmyk = new float[4];
19 |
20 | @ColorLong
21 | private long color;
22 |
23 | public CmykColorPicker(long color) {
24 | prop = new Properties(true, "C", "M", "Y");
25 |
26 | colorSpace = Color.colorSpace(color);
27 | connectorToSrgb = ColorSpace.connect(colorSpace);
28 |
29 | setCompsFromRgb(color);
30 | }
31 |
32 | @Override
33 | public long color() {
34 | return color;
35 | }
36 |
37 | @Override
38 | public int colorInt() {
39 | return ColorUtils.convert(color, connectorToSrgb);
40 | }
41 |
42 | @Override
43 | public float getComponent(int index) {
44 | return cmyk[index] * 100.0f;
45 | }
46 |
47 | @Override
48 | public void setAlpha(float alpha) {
49 | color = ColorUtils.setAlpha(color, Settings.INST.colorRep() ? alpha : alpha / 0xFF);
50 | }
51 |
52 | @Override
53 | public void setComponent(int index, float comp) {
54 | cmyk[index] = comp / 100.0f;
55 | float c = cmyk[0], m = cmyk[1], y = cmyk[2], k = cmyk[3];
56 | float invK = 1.0f - k;
57 | float r = 1.0f - c * invK - k, g = 1.0f - m * invK - k, b = 1.0f - y * invK - k;
58 | color = Color.pack(r, g, b, Color.alpha(color), colorSpace);
59 | }
60 |
61 | private void setCompsFromRgb(long color) {
62 | this.color = color;
63 | final float r = Color.red(color), g = Color.green(color), b = Color.blue(color);
64 | final float c_ = 1.0f - r, m_ = 1.0f - g, y_ = 1.0f - b;
65 | final float k = Math.min(Math.min(c_, m_), y_), invK = 1.0f - k;
66 | cmyk[0] = k == 1.0f ? 0.0f : (c_ - k) / invK;
67 | cmyk[1] = k == 1.0f ? 0.0f : (m_ - k) / invK;
68 | cmyk[2] = k == 1.0f ? 0.0f : (y_ - k) / invK;
69 | cmyk[3] = k;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/options_magic_paint.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
20 |
21 |
22 |
26 |
27 |
35 |
36 |
37 |
42 |
43 |
48 |
49 |
55 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/listener/OnCircularRSChangeListener.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.listener;
2 |
3 | import android.content.res.ColorStateList;
4 |
5 | import androidx.annotation.NonNull;
6 |
7 | import com.google.android.material.slider.RangeSlider;
8 |
9 | import java.util.List;
10 | import java.util.Objects;
11 |
12 | public abstract class OnCircularRSChangeListener implements RangeSlider.OnChangeListener, RangeSlider.OnSliderTouchListener {
13 | private boolean canToggleByBounds = true;
14 | private boolean inclusive = true;
15 | private boolean togglingEnabled = true;
16 |
17 | public OnCircularRSChangeListener() {
18 | }
19 |
20 | public OnCircularRSChangeListener(boolean canSlideThruBound) {
21 | canToggleByBounds = canSlideThruBound;
22 | }
23 |
24 | public abstract void onChange(@NonNull RangeSlider slider, float value, boolean inclusive, boolean stopped);
25 |
26 | @Override
27 | public void onStartTrackingTouch(@NonNull RangeSlider slider) {
28 | }
29 |
30 | @Override
31 | public void onStopTrackingTouch(@NonNull RangeSlider slider) {
32 | onChange(slider, Float.NaN, inclusive, true);
33 | }
34 |
35 | @Override
36 | public final void onValueChange(@NonNull RangeSlider slider, float value, boolean fromUser) {
37 | if (!fromUser) {
38 | return;
39 | }
40 | final List values = slider.getValues();
41 | if (canToggleByBounds && (value == slider.getValueFrom() || value == slider.getValueTo())
42 | || Objects.equals(values.get(0), values.get(values.size() - 1))) {
43 | if (togglingEnabled) {
44 | togglingEnabled = false;
45 | toggleInclusive(slider);
46 | }
47 | } else {
48 | togglingEnabled = true;
49 | }
50 | onChange(slider, value, inclusive, false);
51 | }
52 |
53 | public void toggleInclusive(@NonNull RangeSlider slider) {
54 | inclusive = !inclusive;
55 | final ColorStateList trackAtl = slider.getTrackActiveTintList(), trackItl = slider.getTrackInactiveTintList();
56 | slider.setTrackActiveTintList(trackItl);
57 | slider.setTrackInactiveTintList(trackAtl);
58 | final ColorStateList tickAtl = slider.getTickActiveTintList(), tickItl = slider.getTickInactiveTintList();
59 | slider.setTickActiveTintList(tickItl);
60 | slider.setTickInactiveTintList(tickAtl);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/colorpicker/XyYColorPicker.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.colorpicker;
2 |
3 | import android.graphics.Color;
4 | import android.graphics.ColorSpace;
5 |
6 | import androidx.annotation.ColorLong;
7 | import androidx.annotation.Size;
8 |
9 | import com.misterchan.iconeditor.Settings;
10 | import com.misterchan.iconeditor.util.ColorUtils;
11 |
12 | public class XyYColorPicker extends ColorPicker {
13 | private final ColorSpace.Connector connectorFromXyz, connectorToXyz, connectorToSrgb;
14 |
15 | @Size(3)
16 | private final float[] xyY = new float[3];
17 |
18 | @ColorLong
19 | private long color;
20 |
21 | public XyYColorPicker(long color) {
22 | prop = new Properties("x", "y", "Y",
23 | 0.0f, 0.8f, 0.0f, 0.9f, -2.0f, 2.0f,
24 | EDITOR_TYPE_NUM_DEC, EDITOR_TYPE_NUM_DEC, EDITOR_TYPE_NUM_DEC_SIGNED);
25 |
26 | ColorSpace colorSpace = Color.colorSpace(color);
27 | connectorFromXyz = ColorSpace.connect(XyzColorPicker.XYZ, colorSpace);
28 | connectorToXyz = ColorSpace.connect(colorSpace, XyzColorPicker.XYZ);
29 | connectorToSrgb = ColorSpace.connect(colorSpace, SRGB);
30 |
31 | setCompsFromRgb(color);
32 | }
33 |
34 | @Override
35 | public long color() {
36 | return color;
37 | }
38 |
39 | @Override
40 | public int colorInt() {
41 | return ColorUtils.convert(color, connectorToSrgb);
42 | }
43 |
44 | @Override
45 | public float getComponent(int index) {
46 | return xyY[index];
47 | }
48 |
49 | @Override
50 | public void setAlpha(float a) {
51 | color = ColorUtils.setAlpha(color, Settings.INST.colorRep() ? a : a / 0xFF);
52 | }
53 |
54 | @Override
55 | public void setComponent(int index, float c) {
56 | xyY[index] = c;
57 | final float yoy = xyY[2] / xyY[1], x = yoy * xyY[0], z = yoy * (1.0f - xyY[0] - xyY[1]);
58 | color = Color.convert(x, xyY[2], z, Color.alpha(color), connectorFromXyz);
59 | }
60 |
61 | private void setCompsFromRgb(long color) {
62 | this.color = color;
63 | float[] xyz = connectorToXyz.transform(Color.red(color), Color.green(color), Color.blue(color));
64 | final float sum = xyz[0] + xyz[1] + xyz[2];
65 | if (sum == 0.0f) {
66 | xyY[0] = xyY[1] = xyY[2] = 0.0f;
67 | } else {
68 | xyY[0] = xyz[0] / sum;
69 | xyY[1] = xyz[1] / sum;
70 | xyY[2] = xyz[1];
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/tool/BrushTool.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.tool;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BlendMode;
5 | import android.graphics.Canvas;
6 | import android.graphics.Paint;
7 | import android.graphics.Rect;
8 |
9 | import androidx.annotation.NonNull;
10 |
11 | import com.misterchan.iconeditor.util.BitmapUtils;
12 |
13 | public class BrushTool {
14 | public enum TipShape {
15 | PRESET_BRUSH, CLIP;
16 |
17 | private static final TipShape[] values = values();
18 |
19 | public static TipShape valueAt(int ordinal) {
20 | return values[ordinal];
21 | }
22 | }
23 |
24 | private static final Paint PAINT = new Paint() {
25 | {
26 | setAntiAlias(false);
27 | setBlendMode(BlendMode.DST_IN);
28 | setFilterBitmap(false);
29 | }
30 | };
31 |
32 | private Bitmap src, dst, brush;
33 | public TipShape tipShape = TipShape.PRESET_BRUSH;
34 |
35 | public Bitmap bm() {
36 | return dst;
37 | }
38 |
39 | public void setBrush(Bitmap brush) {
40 | this.brush = brush;
41 | }
42 |
43 | public void setToBrush(long color) {
44 | if (brush == null) return;
45 | set(brush, color);
46 | tipShape = TipShape.PRESET_BRUSH;
47 | }
48 |
49 | public void setToClip(Bitmap src, long color) {
50 | if (src == null) return;
51 | set(src, color);
52 | tipShape = TipShape.CLIP;
53 | }
54 |
55 | public void set(long color) {
56 | set(src, color);
57 | }
58 |
59 | private void set(@NonNull Bitmap src, long color) {
60 | if (src != this.src) {
61 | if (this.src != null) this.src.recycle();
62 | this.src = BitmapUtils.createBitmap(src);
63 | }
64 | final Bitmap dst = Bitmap.createBitmap(src.getWidth(), src.getHeight(), Bitmap.Config.ARGB_8888);
65 | dst.eraseColor(color);
66 | new Canvas(dst).drawBitmap(src, 0.0f, 0.0f, PAINT);
67 | if (this.dst != null) this.dst.recycle();
68 | this.dst = dst;
69 | }
70 |
71 | public void recycle() {
72 | if (src != null) {
73 | src.recycle();
74 | src = null;
75 | }
76 | if (dst != null) {
77 | dst.recycle();
78 | dst = null;
79 | }
80 | }
81 |
82 | public void recycleAll() {
83 | recycle();
84 | if (brush != null) brush.recycle();
85 | }
86 |
87 | public boolean recycled() {
88 | return dst == null;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/ui/ColorAdapter.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.ui;
2 |
3 | import android.graphics.Color;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 |
8 | import androidx.annotation.NonNull;
9 | import androidx.recyclerview.widget.RecyclerView;
10 |
11 | import com.misterchan.iconeditor.databinding.ItemColorBinding;
12 |
13 | import java.util.List;
14 |
15 | public class ColorAdapter extends MovableItemAdapter {
16 |
17 | public interface OnItemClickListener {
18 | void onItemClick(View view, Long color);
19 | }
20 |
21 | public interface OnItemLongClickListener {
22 | boolean onItemLongClick(View view, Long color);
23 | }
24 |
25 | protected static class ViewHolder extends RecyclerView.ViewHolder {
26 | private final ItemColorBinding binding;
27 |
28 | public ViewHolder(@NonNull ItemColorBinding binding) {
29 | super(binding.getRoot());
30 | this.binding = binding;
31 | }
32 | }
33 |
34 | private final List colors;
35 | private OnItemClickListener onItemClickListener;
36 | private OnItemLongClickListener onItemLongClickListener;
37 |
38 | public ColorAdapter(List colors) {
39 | this.colors = colors;
40 | }
41 |
42 | @Override
43 | protected List getData() {
44 | return colors;
45 | }
46 |
47 | @Override
48 | public int getItemCount() {
49 | return colors.size();
50 | }
51 |
52 | @Override
53 | public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
54 | final Long color = colors.get(position);
55 | holder.binding.vColor.setBackgroundColor(Color.toArgb(color));
56 | holder.binding.vColor.setOnClickListener(v -> onItemClickListener.onItemClick(v, color));
57 | holder.binding.vColor.setOnLongClickListener(v -> onItemLongClickListener.onItemLongClick(v, color));
58 | }
59 |
60 | @NonNull
61 | @Override
62 | public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
63 | final ItemColorBinding binding = ItemColorBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
64 | return new ViewHolder(binding);
65 | }
66 |
67 | public void setOnItemClickListener(OnItemClickListener listener) {
68 | onItemClickListener = listener;
69 | }
70 |
71 | public void setOnItemLongClickListener(OnItemLongClickListener listener) {
72 | onItemLongClickListener = listener;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/dialog/FillWithClipDialog.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.dialog;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.content.DialogInterface;
6 | import android.graphics.Shader;
7 | import android.view.Gravity;
8 | import android.view.Window;
9 | import android.view.WindowManager;
10 |
11 | import androidx.appcompat.app.AlertDialog;
12 |
13 | import com.google.android.material.button.MaterialButtonToggleGroup;
14 | import com.misterchan.iconeditor.R;
15 |
16 | public class FillWithClipDialog extends FilterDialog {
17 | public interface OnChangeListener {
18 | void onChange(Shader.TileMode tileMode);
19 | }
20 |
21 | private final OnChangeListener listener;
22 | private Shader.TileMode tileMode = Shader.TileMode.REPEAT;
23 |
24 | @SuppressLint("NonConstantResourceId")
25 | private final MaterialButtonToggleGroup.OnButtonCheckedListener onTileModeButtonCheckedListener = (group, checkedId, isChecked) -> {
26 | if (!isChecked) {
27 | return;
28 | }
29 | tileMode = switch (checkedId) {
30 | case R.id.b_repeat -> Shader.TileMode.REPEAT;
31 | case R.id.b_mirror -> Shader.TileMode.MIRROR;
32 | default -> Shader.TileMode.REPEAT;
33 | };
34 | onChange();
35 | };
36 |
37 | public FillWithClipDialog(Context context, OnChangeListener listener) {
38 | super(context);
39 | builder.setTitle(R.string.fill_with_clip)
40 | .setView(R.layout.fill_with_clip);
41 |
42 | this.listener = listener;
43 | }
44 |
45 | private void onChange() {
46 | listener.onChange(tileMode);
47 | }
48 |
49 | @Override
50 | void onFilterCommit() {
51 | listener.onChange(tileMode);
52 | }
53 |
54 | public FillWithClipDialog setOnDismissListener(DialogInterface.OnDismissListener listener) {
55 | builder.setOnDismissListener(listener);
56 | return this;
57 | }
58 |
59 | @Override
60 | public void show() {
61 | final AlertDialog dialog = builder.show();
62 |
63 | final Window window = dialog.getWindow();
64 | final WindowManager.LayoutParams lp = window.getAttributes();
65 | lp.flags &= ~WindowManager.LayoutParams.FLAG_DIM_BEHIND;
66 | lp.gravity = Gravity.BOTTOM;
67 | window.setAttributes(lp);
68 |
69 | final MaterialButtonToggleGroup btgTileMode = dialog.findViewById(R.id.btg_tile_modes);
70 |
71 | btgTileMode.addOnButtonCheckedListener(onTileModeButtonCheckedListener);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/dialog/BitmapConfigModifier.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.dialog;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.ColorSpace;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.BaseAdapter;
9 | import android.widget.ListAdapter;
10 |
11 | import com.google.android.material.dialog.MaterialAlertDialogBuilder;
12 | import com.misterchan.iconeditor.R;
13 | import com.misterchan.iconeditor.util.BitmapUtils;
14 |
15 | public class BitmapConfigModifier {
16 | private static final Bitmap.Config[] VALUES = {
17 | null, Bitmap.Config.ALPHA_8, null, Bitmap.Config.RGB_565, Bitmap.Config.ARGB_4444, Bitmap.Config.ARGB_8888, null, null, null
18 | };
19 |
20 | public interface OnChangedListener {
21 | void onChanged(Bitmap bitmap);
22 | }
23 |
24 | private Bitmap.Config config;
25 |
26 | private BitmapConfigModifier(Bitmap.Config config) {
27 | this.config = config;
28 | }
29 |
30 | public static void showDialog(Context context, Bitmap bitmap, OnChangedListener listener) {
31 | final Bitmap.Config config = bitmap.getConfig();
32 | final BitmapConfigModifier bcm = new BitmapConfigModifier(config);
33 | final int checkedItem = switch (config) {
34 | default -> 0;
35 | case ALPHA_8 -> 1;
36 | case RGB_565 -> 3;
37 | case ARGB_4444 -> 4;
38 | case ARGB_8888 -> 5;
39 | case RGBA_F16 -> 6;
40 | case RGBA_1010102 -> 8;
41 | };
42 | new MaterialAlertDialogBuilder(context)
43 | .setTitle(R.string.configuration)
44 | .setSingleChoiceItems(R.array.bitmap_configs, checkedItem, (dialog, which) -> {
45 | bcm.config = VALUES[which];
46 | })
47 | .setPositiveButton(R.string.ok, (dialog, which) -> {
48 | if (bcm.config == null) {
49 | listener.onChanged(null);
50 | return;
51 | }
52 | final ColorSpace colorSpace = bitmap.getColorSpace();
53 | final Bitmap bm = BitmapUtils.createBitmap(bitmap, null, bcm.config, bitmap.hasAlpha(),
54 | colorSpace != null || bcm.config == Bitmap.Config.ALPHA_8 ? colorSpace : ColorSpace.get(ColorSpace.Named.SRGB));
55 | listener.onChanged(bm);
56 | })
57 | .setNegativeButton(R.string.cancel, null)
58 | .show();
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/options_transformer.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
20 |
21 |
27 |
28 |
32 |
33 |
41 |
42 |
43 |
47 |
48 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/app/src/main/res/layout-land/color_picker.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
26 |
27 |
37 |
38 |
47 |
48 |
49 |
50 |
51 |
60 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/color_picker.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
17 |
18 |
27 |
28 |
37 |
38 |
48 |
49 |
59 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
16 |
17 |
28 |
29 |
30 |
31 |
32 |
33 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
52 |
53 |
58 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/colorpicker/YuvColorPicker.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.colorpicker;
2 |
3 | import android.graphics.Color;
4 | import android.graphics.ColorMatrix;
5 | import android.graphics.ColorSpace;
6 |
7 | import androidx.annotation.ColorLong;
8 | import androidx.annotation.Size;
9 |
10 | import com.misterchan.iconeditor.Settings;
11 | import com.misterchan.iconeditor.util.ColorUtils;
12 |
13 | public class YuvColorPicker extends ColorPicker {
14 | private static final float[] RGB_TO_YUV = new ColorMatrix() {
15 | {
16 | setRGB2YUV();
17 | }
18 | }.getArray();
19 |
20 | private static final float[] YUV_TO_RGB = new ColorMatrix() {
21 | {
22 | setYUV2RGB();
23 | }
24 | }.getArray();
25 |
26 | private final ColorSpace colorSpace;
27 | private final ColorSpace.Connector connectorToSrgb;
28 |
29 | @Size(3)
30 | private final float[] yuv = new float[3], rgb = new float[3];
31 |
32 | @ColorLong
33 | private long color;
34 |
35 | public YuvColorPicker(long color) {
36 | prop = new Properties("Y", "U", "V",
37 | 0.0f, 1.0f, -0.5f, 0.5f, -0.5f, 0.5f,
38 | EDITOR_TYPE_NUM_DEC, EDITOR_TYPE_NUM_DEC_SIGNED, EDITOR_TYPE_NUM_DEC_SIGNED);
39 |
40 | colorSpace = Color.colorSpace(color);
41 | connectorToSrgb = ColorSpace.connect(colorSpace);
42 |
43 | this.color = color;
44 | convert(new float[]{Color.red(color), Color.green(color), Color.blue(color)}, yuv, RGB_TO_YUV);
45 | rgb[0] = Color.red(color);
46 | rgb[1] = Color.green(color);
47 | rgb[2] = Color.blue(color);
48 | }
49 |
50 | @Override
51 | public long color() {
52 | return color;
53 | }
54 |
55 | @Override
56 | public int colorInt() {
57 | return ColorUtils.convert(rgb[0], rgb[1], rgb[2], Color.alpha(color), connectorToSrgb);
58 | }
59 |
60 | public static void convert(@Size(3) final float[] src, @Size(3) final float[] dst, @Size(20) final float[] matrix) {
61 | dst[0] = src[0] * matrix[0] + src[1] * matrix[1] + src[2] * matrix[2];
62 | dst[1] = src[0] * matrix[5] + src[1] * matrix[6] + src[2] * matrix[7];
63 | dst[2] = src[0] * matrix[10] + src[1] * matrix[11] + src[2] * matrix[12];
64 | }
65 |
66 | @Override
67 | public float getComponent(int index) {
68 | return yuv[index];
69 | }
70 |
71 | @Override
72 | public void setAlpha(float a) {
73 | color = ColorUtils.setAlpha(color, Settings.INST.colorRep() ? a : a / 0xFF);
74 | }
75 |
76 | @Override
77 | public void setComponent(int index, float c) {
78 | yuv[index] = c;
79 | convert(yuv, rgb, YUV_TO_RGB);
80 | color = Color.pack(rgb[0], rgb[1], rgb[2], Color.alpha(color), colorSpace);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/dialog/EditNumberDialog.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.dialog;
2 |
3 | import android.content.Context;
4 | import android.graphics.drawable.Drawable;
5 |
6 | import androidx.annotation.DrawableRes;
7 | import androidx.annotation.Nullable;
8 | import androidx.annotation.StringRes;
9 | import androidx.appcompat.app.AlertDialog;
10 |
11 | import com.google.android.material.dialog.MaterialAlertDialogBuilder;
12 | import com.google.android.material.textfield.TextInputEditText;
13 | import com.google.android.material.textfield.TextInputLayout;
14 | import com.misterchan.iconeditor.R;
15 |
16 | public class EditNumberDialog {
17 |
18 | public interface OnPositiveButtonClickListener {
19 | void onClick(int number);
20 | }
21 |
22 | private final AlertDialog.Builder builder;
23 | private TextInputEditText tiet;
24 |
25 | public EditNumberDialog(Context context) {
26 | builder = new MaterialAlertDialogBuilder(context).setView(R.layout.edit_text)
27 | .setNegativeButton(R.string.cancel, null);
28 | }
29 |
30 | public EditNumberDialog setIcon(@Nullable Drawable drawable) {
31 | builder.setIcon(drawable);
32 | return this;
33 | }
34 |
35 | public EditNumberDialog setIcon(@DrawableRes int iconId) {
36 | builder.setIcon(iconId);
37 | return this;
38 | }
39 |
40 | public EditNumberDialog setNeutralButton(@StringRes int textId, OnPositiveButtonClickListener listener) {
41 | builder.setNeutralButton(textId, (dialog, which) -> {
42 | try {
43 | listener.onClick(Integer.parseUnsignedInt(tiet.getText().toString()));
44 | } catch (NumberFormatException e) {
45 | }
46 | });
47 | return this;
48 | }
49 |
50 | public EditNumberDialog setOnApplyListener(OnPositiveButtonClickListener listener) {
51 | builder.setPositiveButton(R.string.ok, (dialog, which) -> {
52 | try {
53 | listener.onClick(Integer.parseUnsignedInt(tiet.getText().toString()));
54 | } catch (NumberFormatException e) {
55 | }
56 | });
57 | return this;
58 | }
59 |
60 | public EditNumberDialog setTitle(@StringRes int titleId) {
61 | builder.setTitle(titleId);
62 | return this;
63 | }
64 |
65 | public void show(int defaultNumber) {
66 | show(defaultNumber, null);
67 | }
68 |
69 | public void show(int defaultNumber, CharSequence suffixText) {
70 | final AlertDialog dialog = builder.show();
71 |
72 | tiet = dialog.findViewById(R.id.tiet);
73 | final TextInputLayout til = dialog.findViewById(R.id.til);
74 |
75 | tiet.setText(String.valueOf(defaultNumber));
76 |
77 | if (suffixText != null) til.setSuffixText(suffixText);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/new_image.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
27 |
28 |
37 |
38 |
39 |
48 |
49 |
58 |
59 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/options_magic_eraser.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
21 |
22 |
23 |
28 |
29 |
32 |
33 |
41 |
42 |
48 |
49 |
55 |
56 |
57 |
63 |
64 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/noise_generator.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
24 |
25 |
31 |
32 |
38 |
39 |
45 |
46 |
47 |
52 |
53 |
58 |
59 |
66 |
67 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/dialog/SliderDialog.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.dialog;
2 |
3 | import android.content.Context;
4 | import android.graphics.drawable.Drawable;
5 | import android.view.Gravity;
6 | import android.view.Window;
7 | import android.view.WindowManager;
8 |
9 | import androidx.annotation.DrawableRes;
10 | import androidx.annotation.Nullable;
11 | import androidx.annotation.StringRes;
12 | import androidx.appcompat.app.AlertDialog;
13 |
14 | import com.google.android.material.slider.Slider;
15 | import com.misterchan.iconeditor.R;
16 | import com.misterchan.iconeditor.listener.OnSliderChangeListener;
17 |
18 | public class SliderDialog extends FilterDialog {
19 |
20 | private float stepSize, valueTo, valueFrom, value;
21 | private OnSliderChangeListener listener;
22 | private Slider slider;
23 |
24 | public SliderDialog(Context context) {
25 | super(context);
26 | builder.setView(R.layout.slider);
27 | }
28 |
29 | @Override
30 | void onFilterCommit() {
31 | listener.onChange(slider, slider.getValue(), true);
32 | }
33 |
34 | public SliderDialog setIcon(@DrawableRes int iconId) {
35 | builder.setIcon(iconId);
36 | return this;
37 | }
38 |
39 | public SliderDialog setIcon(@Nullable Drawable drawable) {
40 | builder.setIcon(drawable);
41 | return this;
42 | }
43 |
44 | public SliderDialog setOnChangeListener(OnSliderChangeListener listener) {
45 | this.listener = listener;
46 | return this;
47 | }
48 |
49 | public SliderDialog setStepSize(float stepSize) {
50 | this.stepSize = stepSize;
51 | return this;
52 | }
53 |
54 | public SliderDialog setTitle(@StringRes int titleId) {
55 | builder.setTitle(titleId);
56 | return this;
57 | }
58 |
59 | public SliderDialog setValue(float value) {
60 | this.value = value;
61 | return this;
62 | }
63 |
64 | public SliderDialog setValueFrom(float valueFrom) {
65 | this.valueFrom = valueFrom;
66 | return this;
67 | }
68 |
69 | public SliderDialog setValueTo(float valueTo) {
70 | this.valueTo = valueTo;
71 | return this;
72 | }
73 |
74 | @Override
75 | public void show() {
76 | final AlertDialog dialog = builder.show();
77 |
78 | final Window window = dialog.getWindow();
79 | final WindowManager.LayoutParams lp = window.getAttributes();
80 | lp.flags &= ~WindowManager.LayoutParams.FLAG_DIM_BEHIND;
81 | lp.gravity = Gravity.BOTTOM;
82 | window.setAttributes(lp);
83 |
84 | slider = dialog.findViewById(R.id.slider);
85 | slider.setStepSize(stepSize);
86 | slider.setValueTo(valueTo);
87 | slider.setValueFrom(valueFrom);
88 | slider.setValue(value);
89 | slider.addOnChangeListener(listener);
90 | slider.addOnSliderTouchListener(listener);
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/ImageStateAccumulator.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Canvas;
5 | import android.graphics.Rect;
6 |
7 | import com.misterchan.iconeditor.util.BitmapUtils;
8 |
9 | public class ImageStateAccumulator {
10 | private Bitmap bitmap;
11 | private Canvas canvas;
12 | public final Rect stateBounds = new Rect(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
13 |
14 | public boolean areStateBoundsEmpty() {
15 | return stateBounds.isEmpty();
16 | }
17 |
18 | public Bitmap bitmap() {
19 | return bitmap;
20 | }
21 |
22 | public boolean doStateBoundsIntersect() {
23 | return stateBounds.intersects(0, 0, bitmap.getWidth(), bitmap.getHeight());
24 | }
25 |
26 | public void draw(Bitmap bm, Rect dst) {
27 | canvas.drawBitmap(bm, null, dst, BitmapUtils.PAINT_SRC);
28 | }
29 |
30 | public void drawOnto(Canvas canvas) {
31 | if (bitmap == null || !doStateBoundsIntersect()) {
32 | return;
33 | }
34 | canvas.drawBitmap(bitmap, stateBounds, stateBounds, BitmapUtils.PAINT_SRC);
35 | resetBounds();
36 | }
37 |
38 | public void erase(Bitmap bm, Rect rect) {
39 | if (rect != null) {
40 | canvas.drawBitmap(bm, rect, rect, BitmapUtils.PAINT_SRC);
41 | } else {
42 | canvas.drawBitmap(bm, 0.0f, 0.0f, BitmapUtils.PAINT_SRC);
43 | }
44 | }
45 |
46 | public boolean isRecycled() {
47 | return bitmap == null || bitmap.isRecycled();
48 | }
49 |
50 | public void post(Bitmap bm, Rect rect) {
51 | if (bitmap == null) {
52 | return;
53 | }
54 | canvas.drawBitmap(bm, rect, rect, BitmapUtils.PAINT_SRC);
55 | resetBounds();
56 | }
57 |
58 | public void recycle() {
59 | if (bitmap == null) return;
60 | bitmap.recycle();
61 | bitmap = null;
62 | canvas = null;
63 | }
64 |
65 | public void resetBounds() {
66 | stateBounds.set(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
67 | }
68 |
69 | public void set(Bitmap src) {
70 | if (bitmap != null) bitmap.recycle();
71 | bitmap = Bitmap.createBitmap(src);
72 | canvas = new Canvas(bitmap);
73 | }
74 |
75 | public void unionBounds(int x0, int y0, int x1, int y1, float radius) {
76 | boolean x = x0 <= x1, y = y0 <= y1;
77 | int rad = (int) Math.ceil(radius + 1.0f);
78 | unionBounds((x ? x0 : x1) - rad, (y ? y0 : y1) - rad, (x ? x1 : x0) + rad + 1, (y ? y1 : y0) + rad + 1);
79 | }
80 |
81 | public void unionBounds(Rect rect) {
82 | unionBounds(rect.left, rect.top, rect.right, rect.bottom);
83 | }
84 |
85 | public void unionBounds(int left, int top, int right, int bottom) {
86 | if (bitmap == null) return;
87 | stateBounds.union(left, top, right, bottom);
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
20 |
21 |
25 |
26 |
30 |
31 |
37 |
38 |
41 |
42 |
45 |
46 |
52 |
53 |
56 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/new_guide.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
26 |
27 |
34 |
35 |
36 |
45 |
46 |
55 |
56 |
57 |
67 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layer_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
23 |
24 |
25 |
34 |
35 |
38 |
39 |
46 |
47 |
55 |
56 |
64 |
65 |
73 |
74 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/dialog/MatrixManager.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.dialog;
2 |
3 | import android.content.Context;
4 | import android.graphics.Matrix;
5 | import android.view.Gravity;
6 | import android.view.Window;
7 | import android.view.WindowManager;
8 | import android.widget.GridLayout;
9 |
10 | import androidx.annotation.Size;
11 | import androidx.appcompat.app.AlertDialog;
12 |
13 | import com.google.android.material.dialog.MaterialAlertDialogBuilder;
14 | import com.google.android.material.textfield.TextInputEditText;
15 | import com.misterchan.iconeditor.listener.AfterTextChangedListener;
16 | import com.misterchan.iconeditor.R;
17 |
18 | import java.text.DecimalFormat;
19 |
20 | public class MatrixManager extends FilterDialog {
21 |
22 | private static final DecimalFormat DEC_FORMAT = new DecimalFormat("0.#######");
23 |
24 | public interface OnMatrixElementsChangedListener {
25 | void onChanged(Matrix matrix);
26 | }
27 |
28 | private final Matrix matrix;
29 | private final OnMatrixElementsChangedListener listener;
30 |
31 | @Size(9)
32 | private final float[] values = new float[9];
33 |
34 | public MatrixManager(Context context,
35 | OnMatrixElementsChangedListener listener) {
36 | this(context, listener, new Matrix());
37 | }
38 |
39 | public MatrixManager(Context context,
40 | OnMatrixElementsChangedListener onMatrixElementsChangeListener,
41 | Matrix defaultMatrix) {
42 | super(context);
43 | builder.setView(R.layout.matrix);
44 |
45 | ((MaterialAlertDialogBuilder) builder).setBackgroundInsetBottom(0);
46 |
47 | matrix = defaultMatrix;
48 | matrix.getValues(values);
49 | this.listener = onMatrixElementsChangeListener;
50 | }
51 |
52 | @Override
53 | void onFilterCommit() {
54 | listener.onChanged(matrix);
55 | }
56 |
57 | @Override
58 | public void show() {
59 | final AlertDialog dialog = builder.show();
60 |
61 | final Window window = dialog.getWindow();
62 | final WindowManager.LayoutParams lp = window.getAttributes();
63 | lp.flags &= ~WindowManager.LayoutParams.FLAG_DIM_BEHIND;
64 | lp.gravity = Gravity.BOTTOM;
65 | window.setAttributes(lp);
66 |
67 | final GridLayout gl = dialog.findViewById(R.id.gl_k);
68 |
69 | for (int i = 0; i < 9; ++i) {
70 | TextInputEditText tiet = (TextInputEditText) gl.getChildAt(i);
71 | tiet.setText(DEC_FORMAT.format(values[i]));
72 | final int index = i;
73 | tiet.addTextChangedListener((AfterTextChangedListener) s -> tryParsing(index, s));
74 | }
75 | }
76 |
77 | private void tryParsing(int index, String s) {
78 | final float f;
79 | try {
80 | f = Float.parseFloat(s);
81 | } catch (NumberFormatException e) {
82 | return;
83 | }
84 | values[index] = f;
85 | matrix.setValues(values);
86 | listener.onChanged(matrix);
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/app/src/main/java/com/misterchan/iconeditor/tool/MagicEraser.java:
--------------------------------------------------------------------------------
1 | package com.misterchan.iconeditor.tool;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.graphics.Point;
7 | import android.graphics.PointF;
8 | import android.graphics.Rect;
9 |
10 | import androidx.annotation.ColorInt;
11 |
12 | import com.misterchan.iconeditor.util.BitmapUtils;
13 |
14 | public class MagicEraser {
15 | public Point b, f; // Background and foreground
16 | public final PointF bd = new PointF(0.0f, 0.0f), fd = new PointF(0.0f, 0.0f); // Distance
17 |
18 | public void eraseImprecisely(Canvas canvas, Bitmap source,
19 | @ColorInt int foregroundColor, @ColorInt int backgroundColor,
20 | int left, int top, int right, int bottom,
21 | float startX, float startY, float stopX, float stopY,
22 | Paint paint) {
23 | final int width = right - left, height = bottom - top;
24 | final Bitmap bLine = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
25 | final Canvas cLine = new Canvas(bLine);
26 | cLine.drawLine(startX, startY, stopX, stopY, paint);
27 | canvas.drawBitmap(bLine, left, top, BitmapUtils.PAINT_DST_OUT);
28 | final Bitmap bm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
29 | new Canvas(bm).drawBitmap(source,
30 | new Rect(left, top, right, bottom), new Rect(0, 0, width, height), BitmapUtils.PAINT_SRC);
31 | BitmapUtils.removeBackground(bm, foregroundColor, backgroundColor);
32 | cLine.drawBitmap(bm, 0.0f, 0.0f, BitmapUtils.PAINT_SRC_IN);
33 | bm.recycle();
34 | canvas.drawBitmap(bLine, left, top, BitmapUtils.PAINT_SRC_OVER);
35 | bLine.recycle();
36 | }
37 |
38 | public void erasePrecisely(Canvas canvas, Bitmap source,
39 | @ColorInt int foregroundColor, @ColorInt int backgroundColor,
40 | int left, int top, int right, int bottom,
41 | Paint paint) {
42 | final int width = right - left, height = bottom - top;
43 | final Bitmap bLine = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
44 | final Canvas cLine = new Canvas(bLine);
45 | cLine.drawLine(b.x - left, b.y - top,
46 | f.x - left, f.y - top,
47 | paint);
48 | canvas.drawBitmap(bLine, left, top, BitmapUtils.PAINT_DST_OUT);
49 | final Bitmap bm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
50 | new Canvas(bm).drawBitmap(source,
51 | new Rect(left, top, right, bottom), new Rect(0, 0, width, height),
52 | BitmapUtils.PAINT_SRC);
53 | BitmapUtils.removeBackground(bm, foregroundColor, backgroundColor);
54 | cLine.drawBitmap(bm, 0.0f, 0.0f, BitmapUtils.PAINT_SRC_IN);
55 | bm.recycle();
56 | canvas.drawBitmap(bLine, left, top, BitmapUtils.PAINT_SRC_OVER);
57 | bLine.recycle();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------