Skip to content
Draft
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
19 changes: 19 additions & 0 deletions hex_dump.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
foreach ($fname in @("image1.catrobat-image", "image2.catrobat-image", "img.catrobat-image")) {
$path = "C:\Users\amitm\Desktop\New folder\$fname"
$bytes = [System.IO.File]::ReadAllBytes($path)
Write-Host "=== $fname (size: $($bytes.Length)) ==="
Write-Host "First 100 bytes (hex):"
$hexLine = ""
for ($i = 0; $i -lt 100 -and $i -lt $bytes.Length; $i++) {
$hexLine += "{0:X2} " -f $bytes[$i]
if (($i + 1) % 16 -eq 0) {
Write-Host (" {0:D4}: {1}" -f ($i - 15), $hexLine)
$hexLine = ""
}
}
if ($hexLine -ne "") {
$padI = [math]::Floor(($i-1) / 16) * 16
Write-Host (" {0:D4}: {1}" -f $padI, $hexLine)
}
Write-Host ""
}
5 changes: 5 additions & 0 deletions lib/core/commands/command_painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:paintroid/core/providers/state/canvas_state_provider.dart';
import 'package:paintroid/core/providers/state/paint_provider.dart';
import 'package:paintroid/core/providers/state/toolbox_state_provider.dart';
import 'package:paintroid/core/tools/implementation/clipboard_tool.dart';
import 'package:paintroid/core/tools/implementation/import_tool.dart';
import 'package:paintroid/core/tools/implementation/cursor_tool.dart';
import 'package:paintroid/core/tools/implementation/shapes_tool.dart';
import 'package:paintroid/core/tools/implementation/text_tool.dart';
Expand Down Expand Up @@ -65,6 +66,10 @@ class CommandPainter extends CustomPainter {
break;
case ToolType.CLIPBOARD:
(currentTool as ClipboardTool).paint(canvas, size);
break;
case ToolType.IMPORT:
(currentTool as ImportTool).paint(canvas, size);
break;
case ToolType.TEXT:
(currentTool as TextTool).drawGuides(canvas, ref.read(paintProvider));
break;
Expand Down
29 changes: 29 additions & 0 deletions lib/core/providers/object/tools/import_tool_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import 'dart:ui';

import 'package:paintroid/core/enums/tool_types.dart';
import 'package:paintroid/core/providers/state/canvas_state_provider.dart';
import 'package:paintroid/core/tools/bounding_box.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:paintroid/core/commands/command_factory/command_factory_provider.dart';
import 'package:paintroid/core/commands/command_manager/command_manager_provider.dart';
import 'package:paintroid/core/tools/implementation/import_tool.dart';

part 'import_tool_provider.g.dart';

@Riverpod(keepAlive: true)
class ImportToolProvider extends _$ImportToolProvider {
@override
ImportTool build() {
Rect initialBoundingBox = Rect.fromCenter(
center: ref.read(canvasStateProvider).size.center(Offset.zero),
width: 300,
height: 300,
);
return ImportTool(
commandManager: ref.watch(commandManagerProvider),
commandFactory: ref.watch(commandFactoryProvider),
boundingBox: BoundingBox.fromRect(initialBoundingBox),
type: ToolType.IMPORT,
);
}
}
27 changes: 27 additions & 0 deletions lib/core/providers/object/tools/import_tool_provider.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions lib/core/providers/state/toolbox_state_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import 'package:paintroid/core/enums/tool_types.dart';
import 'package:paintroid/core/providers/object/canvas_painter_provider.dart';
import 'package:paintroid/core/providers/object/tools/brush_tool_provider.dart';
import 'package:paintroid/core/providers/object/tools/clipboard_tool_provider.dart';
import 'package:paintroid/core/providers/object/tools/import_tool_provider.dart';
import 'package:paintroid/core/providers/object/load_image_from_photo_library.dart';
import 'package:paintroid/core/tools/implementation/import_tool.dart';
import 'package:paintroid/core/providers/object/tools/cursor_tool_provider.dart';
import 'package:paintroid/core/providers/object/tools/eraser_tool_provider.dart';
import 'package:paintroid/core/providers/object/tools/hand_tool_provider.dart';
Expand Down Expand Up @@ -95,6 +98,14 @@ class ToolBoxStateProvider extends _$ToolBoxStateProvider {
state = state.copyWith(currentTool: ref.read(clipboardToolProvider));
ref.read(canvasPainterProvider.notifier).repaint();
break;
case ToolType.IMPORT:
final importTool = ref.read(importToolProvider);
state = state.copyWith(currentTool: importTool);
ref.read(canvasPainterProvider.notifier).repaint();
importTool.pickImage(ref.read(LoadImageFromPhotoLibrary.provider)).then((_) {
ref.read(canvasPainterProvider.notifier).repaint();
});
break;
case ToolType.PIPETTE:
state = state.copyWith(currentTool: ref.read(pipetteToolProvider));
break;
Expand Down
2 changes: 1 addition & 1 deletion lib/core/providers/state/toolbox_state_provider.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

156 changes: 156 additions & 0 deletions lib/core/tools/implementation/import_tool.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import 'dart:math' as dart_math;
import 'dart:ui' as ui;

import 'package:flutter/material.dart';
import 'package:paintroid/core/enums/bounding_box_action.dart';
import 'package:paintroid/core/providers/object/load_image_from_photo_library.dart';
import 'package:paintroid/core/tools/bounding_box.dart';
import 'package:paintroid/core/tools/tool.dart';

class ImportTool extends Tool {
final BoundingBox boundingBox;
ui.Image? importedImage;
bool _isInteracting = false;

ImportTool({
required super.commandManager,
required super.commandFactory,
required this.boundingBox,
required super.type,
super.hasAddFunctionality = false,
super.hasFinalizeFunctionality = true,
});

Future<void> pickImage(LoadImageFromPhotoLibrary loadImageFromPhotoLibrary) async {
final result = await loadImageFromPhotoLibrary.call();
result.match(
(image) {
importedImage = image;
// Keep the provider-created box centered on the canvas and reset its
// transform for each newly selected image.
boundingBox.width = image.width.toDouble();
boundingBox.height = image.height.toDouble();
boundingBox.angle = 0.0;
},
(failure) {
// If failed or cancelled, we don't change anything
},
);
}

@override
void onDown(ui.Offset point, Paint paint) {
boundingBox.determineAction(point);
_isInteracting = boundingBox.currentAction != BoundingBoxAction.none;
}

@override
void onDrag(ui.Offset point, Paint paint) {
if (_isInteracting) boundingBox.updateDrag(point);
}

@override
void onUp(ui.Offset point, Paint paint) {
if (_isInteracting) {
boundingBox.endDrag();
_isInteracting = false;
}
}

@override
void onCancel() {
if (_isInteracting) {
boundingBox.endDrag();
_isInteracting = false;
}
}

void paint(Canvas canvas, Size size) {
boundingBox.drawGuides(canvas);
final image = importedImage;
if (image == null) return;
if (image.width == 0 || image.height == 0) return;

final rect = boundingBox.rect;
if (rect.width <= 0 || rect.height <= 0) return;

double previewScale = 1.0;
if (image.width > 0 &&
image.height > 0 &&
rect.width > 0 &&
rect.height > 0) {
final double widthScale = rect.width / image.width.toDouble();
final double heightScale = rect.height / image.height.toDouble();
previewScale = dart_math.min(widthScale, heightScale);
}

final double scaledWidth = image.width.toDouble() * previewScale;
final double scaledHeight = image.height.toDouble() * previewScale;

final src = ui.Rect.fromLTWH(
0,
0,
image.width.toDouble(),
image.height.toDouble(),
);
final dst = ui.Rect.fromLTWH(
-scaledWidth / 2,
-scaledHeight / 2,
scaledWidth,
scaledHeight,
);

canvas.save();
canvas.translate(rect.center.dx, rect.center.dy);
canvas.rotate(boundingBox.angle);
final paintImage = Paint()..filterQuality = FilterQuality.high;
canvas.drawImageRect(image, src, dst, paintImage);
canvas.restore();
}

@override
Future<void> onCheckmark(Paint paint) async {
final image = importedImage;
if (image == null) return;

final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
if (byteData == null) return;
final bytes = byteData.buffer.asUint8List();

final pasteOffset = boundingBox.rect.center;
final pasteRotation = boundingBox.angle;

double pasteScale = 1.0;
if (image.width > 0 &&
image.height > 0 &&
boundingBox.rect.width > 0 &&
boundingBox.rect.height > 0) {
final double widthScale = boundingBox.rect.width / image.width.toDouble();
final double heightScale =
boundingBox.rect.height / image.height.toDouble();
pasteScale = dart_math.min(widthScale, heightScale);
} else if (image.width > 0 && boundingBox.rect.width > 0) {
pasteScale = boundingBox.rect.width / image.width.toDouble();
}

final command = commandFactory.createClipboardCommand(
paint,
bytes,
pasteOffset,
pasteScale,
pasteRotation,
);
await command.prepareForRuntime();
commandManager.addGraphicCommand(command);
importedImage = null;
}

@override
void onPlus() {}

@override
void onUndo() => commandManager.undo();

@override
void onRedo() => commandManager.redo();
}
3 changes: 2 additions & 1 deletion lib/core/tools/tool.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:ui';

import 'package:flutter/cupertino.dart';
Expand Down Expand Up @@ -28,7 +29,7 @@ abstract class Tool {

void onCancel();

void onCheckmark(Paint paint);
FutureOr<void> onCheckmark(Paint paint);

void onPlus();

Expand Down
11 changes: 7 additions & 4 deletions lib/ui/pages/workspace_page/components/top_bar/top_app_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import 'package:paintroid/core/providers/state/app_bar_provider.dart';
import 'package:paintroid/core/providers/state/canvas_state_provider.dart';
import 'package:paintroid/core/providers/state/paint_provider.dart';
import 'package:paintroid/core/providers/state/toolbox_state_provider.dart';
import 'package:paintroid/core/tools/implementation/text_tool.dart';
import 'package:paintroid/core/tools/implementation/import_tool.dart';
import 'package:paintroid/core/tools/line_tool/line_tool.dart';
import 'package:paintroid/core/tools/implementation/text_tool.dart';
import 'package:paintroid/core/tools/tool.dart';
import 'package:paintroid/ui/pages/workspace_page/components/top_bar/overflow_menu.dart';
import 'package:paintroid/ui/shared/action_button.dart';
Expand Down Expand Up @@ -74,9 +75,11 @@ class TopAppBar extends ConsumerWidget implements PreferredSizeWidget {
currentTool is LineTool && currentTool.vertexStack.isNotEmpty;
final isShapeTool = currentTool.type == ToolType.SHAPES;
final isTextTool = currentTool is TextTool;
if (isLineTool || isShapeTool || isTextTool) {
return () {
currentTool.onCheckmark(ref.read(paintProvider));
final isImportTool = currentTool is ImportTool &&
currentTool.importedImage != null;
if (isLineTool || isShapeTool || isTextTool || isImportTool) {
return () async {
await currentTool.onCheckmark(ref.read(paintProvider));
ref.read(appBarProvider.notifier).update();
ref
.read(canvasStateProvider.notifier)
Expand Down
Loading
Loading