Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 58 additions & 46 deletions packages/mini_sprite_editor/lib/app/view/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:mini_sprite_editor/config/config.dart';
import 'package:mini_sprite_editor/l10n/arb/app_localizations.dart';
import 'package:mini_sprite_editor/library/library.dart';
import 'package:mini_sprite_editor/map/map.dart';
import 'package:mini_sprite_editor/services/image_importer.dart';
import 'package:mini_sprite_editor/sprite/sprite.dart';
import 'package:mini_sprite_editor/workspace/workspace.dart';

Expand All @@ -14,61 +15,72 @@ class App extends StatelessWidget {

@override
Widget build(BuildContext context) {
return MultiBlocProvider(
return MultiRepositoryProvider(
providers: [
BlocProvider<ConfigCubit>(create: (_) => ConfigCubit()),
BlocProvider<WorkspaceCubit>(create: (_) => WorkspaceCubit()),
BlocProvider<SpriteCubit>(create: (context) => SpriteCubit()),
BlocProvider<LibraryCubit>(create: (context) => LibraryCubit()),
BlocProvider<MapCubit>(create: (context) => MapCubit()),
RepositoryProvider<ImageImporterService>(
create: (_) => ImageImporterService(),
),
],
child: BlocBuilder<ConfigCubit, ConfigState>(
builder:
(context, state) => MaterialApp(
themeMode: state.themeMode,
theme: ThemeData(),
darkTheme: ThemeData.dark(),
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
],
supportedLocales: AppLocalizations.supportedLocales,
onGenerateRoute: (settings) {
final name = settings.name;
child: MultiBlocProvider(
providers: [
BlocProvider<ConfigCubit>(create: (_) => ConfigCubit()),
BlocProvider<WorkspaceCubit>(create: (_) => WorkspaceCubit()),
BlocProvider<SpriteCubit>(create: (context) => SpriteCubit()),
BlocProvider<LibraryCubit>(create: (context) => LibraryCubit()),
BlocProvider<MapCubit>(create: (context) => MapCubit()),
],
child: BlocBuilder<ConfigCubit, ConfigState>(
builder:
(context, state) => MaterialApp(
themeMode: state.themeMode,
theme: ThemeData(),
darkTheme: ThemeData.dark(),
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
],
supportedLocales: AppLocalizations.supportedLocales,
onGenerateRoute: (settings) {
final name = settings.name;

if (name != null && name != '/') {
final uri = Uri.parse(name);
final colors = uri.queryParameters['colors'];
if (name != null && name != '/') {
final uri = Uri.parse(name);
final colors = uri.queryParameters['colors'];

List<Color>? colorList;
if (colors != null) {
colorList =
colors
.split(',')
.map(int.parse)
.map(Color.new)
.toList();
}
List<Color>? colorList;
if (colors != null) {
colorList =
colors
.split(',')
.map(int.parse)
.map(Color.new)
.toList();
}

final spriteRaw = uri.queryParameters['sprite'];
MiniSprite? sprite;
if (spriteRaw != null) {
try {
sprite = MiniSprite.fromDataString(spriteRaw);
} on Exception catch (_) {
// ignore on invalid sprite data
final spriteRaw = uri.queryParameters['sprite'];
MiniSprite? sprite;
if (spriteRaw != null) {
try {
sprite = MiniSprite.fromDataString(spriteRaw);
} on Exception catch (_) {
// ignore on invalid sprite data
}
}
return MaterialPageRoute(
builder:
(_) => WorkspaceView(
colorList: colorList,
sprite: sprite,
),
);
}

return MaterialPageRoute(
builder:
(_) =>
WorkspaceView(colorList: colorList, sprite: sprite),
builder: (_) => const WorkspaceView(),
);
}

return MaterialPageRoute(builder: (_) => const WorkspaceView());
},
),
},
),
),
),
);
}
Expand Down
4 changes: 4 additions & 0 deletions packages/mini_sprite_editor/lib/l10n/arb/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -223,5 +223,9 @@
"rotateCounterClockwise": "Rotate counter clockwise",
"@rotateCounterClockwise": {
"description": "Label for rotating the sprite counter clockwise"
},
"importFromImage": "Import from image",
"@importFromImage": {
"description": "Import from image label"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,12 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Rotate counter clockwise'**
String get rotateCounterClockwise;

/// Import from image label
///
/// In en, this message translates to:
/// **'Import from image'**
String get importFromImage;
}

class _AppLocalizationsDelegate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,4 +175,7 @@ class AppLocalizationsEn extends AppLocalizations {

@override
String get rotateCounterClockwise => 'Rotate counter clockwise';

@override
String get importFromImage => 'Import from image';
}
56 changes: 56 additions & 0 deletions packages/mini_sprite_editor/lib/services/image_importer.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import 'dart:ui';

import 'package:file_selector/file_selector.dart';
import 'package:image/image.dart' as img;

class ImageImporterService {
ImageImporterService();

Future<(List<List<int>> pixels, List<Color> colors)?> importImage() async {
const typeGroup = XTypeGroup(
label: 'images',
extensions: <String>['jpg', 'png'],
);
final file = await openFile(
acceptedTypeGroups: <XTypeGroup>[typeGroup],
);

if (file != null) {
final bytes = await file.readAsBytes();
final image = img.decodeImage(bytes);

if (image != null) {
final colors = <Color>[];

final pixels = List<List<int>>.generate(
image.height,
(_) => List<int>.generate(image.width, (index) => -1),
);

for (var y = 0; y < image.height; y++) {
for (var x = 0; x < image.width; x++) {
final pixel = image.getPixelSafe(x, y);

final alpha = pixel.a.toInt();
final red = pixel.r.toInt();
final green = pixel.g.toInt();
final blue = pixel.b.toInt();

final color = Color.fromARGB(alpha, red, green, blue);

if (!colors.contains(color) && color.a != 0) {
colors.add(color);
}

final colorIndex = colors.indexOf(color);
pixels[y][x] = colorIndex;
}
}

return (pixels, colors);
}
}

return null;
}
}
1 change: 1 addition & 0 deletions packages/mini_sprite_editor/lib/services/services.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export 'image_importer.dart';
29 changes: 29 additions & 0 deletions packages/mini_sprite_editor/lib/sprite/view/sprite_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:mini_sprite_editor/config/config.dart';
import 'package:mini_sprite_editor/l10n/l10n.dart';
import 'package:mini_sprite_editor/library/library.dart';
import 'package:mini_sprite_editor/services/services.dart';
import 'package:mini_sprite_editor/sprite/cubit/tools_cubit.dart';
import 'package:mini_sprite_editor/sprite/sprite.dart';
import 'package:mini_sprite_editor/widgets/composed_icon.dart';
Expand Down Expand Up @@ -250,6 +251,34 @@ class SpriteView extends StatelessWidget {
tooltip: l10n.importFromClipBoard,
icon: const Icon(Icons.import_export),
),
Tooltip(
message: l10n.importFromImage,
child: GestureDetector(
key: const Key('import_image_key'),
onTap: () async {
final spriteCubit = context.read<SpriteCubit>();
final configCubit = context.read<ConfigCubit>();

final scaffoldMessenger = ScaffoldMessenger.of(context);

final imageService =
context.read<ImageImporterService>();
final result = await imageService.importImage();
if (result != null) {
final (pixels, colors) = result;
spriteCubit.setSprite(pixels);
configCubit.setColors(colors);
scaffoldMessenger.showSnackBar(
SnackBar(content: Text(l10n.importSuccess)),
);
}
},
child: const ComposedIcon(
primary: Icons.image,
secondary: Icons.download,
),
),
),
IconButton(
key: const Key('export_to_image'),
onPressed: () async {
Expand Down
40 changes: 40 additions & 0 deletions packages/mini_sprite_editor/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.7.1"
archive:
dependency: transitive
description:
name: archive
sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
url: "https://pub.dev"
source: hosted
version: "4.0.7"
args:
dependency: transitive
description:
Expand Down Expand Up @@ -340,6 +348,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "9.1.5"
image:
dependency: "direct main"
description:
name: image
sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928"
url: "https://pub.dev"
source: hosted
version: "4.5.4"
intl:
dependency: "direct main"
description:
Expand Down Expand Up @@ -531,6 +547,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.0"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1"
url: "https://pub.dev"
source: hosted
version: "7.0.1"
platform:
dependency: transitive
description:
Expand All @@ -555,6 +579,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.5.1"
posix:
dependency: transitive
description:
name: posix
sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61"
url: "https://pub.dev"
source: hosted
version: "6.0.3"
provider:
dependency: transitive
description:
Expand Down Expand Up @@ -784,6 +816,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.0"
xml:
dependency: transitive
description:
name: xml
sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
url: "https://pub.dev"
source: hosted
version: "6.6.1"
yaml:
dependency: transitive
description:
Expand Down
1 change: 1 addition & 0 deletions packages/mini_sprite_editor/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dependencies:
flutter_localizations:
sdk: flutter
hydrated_bloc: ^9.0.0-dev.3
image: ^4.5.4
intl: ^0.20.2
mini_sprite: 0.1.0
path_provider: ^2.0.11
Expand Down
Loading