-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Flood function stub * Test filling transparent image * Pass test * Add test for perfect rectangle fill * Pass rect fill test * Red test * Helper class * Simple recursive implementation * Test filling rectable with rounded corners * Print color map * Pass rounded rect fill * Blob test * Sort keys for better debug experience * Pass all tests * Fill apple * Fix project save data test * Bucket icon button * Refactoring WIP * Plug in flood algo * Temporarily use duncan algo * Convert to duncan color * Fill at touch point * Run fill algo in an isolate * Refactor brush WIP * WIP * Fix canvas bug * Remove unrasterized strokes once rasterized * Easel shouldn't know which tools return strokes * Remove unused depend * Make abstract * Freeze state for painting task * Remove unused imports * Move lasso state * Fix lasso bug * Fix lasso behaviour * First task q test * Fix test * Test 2 tasks running consec * Test running 5 tasks * Use our algorithm for bucket * Extensions folder * Extract color method * Doc * Queue instead of recursion stack * Prevent inf loop * Time bucket * Remove coord * Inside helper func * Store fillmask * Efficient flood fill * Use compute * Save failed graphics gem translation * Working scanline woohooo * Fix scanline algo * Refactor scanline * Rename * Comments * Modified scanline * Setup ffi for android * Convert color * Port scanline to cpp * Fix scanline * Port modified scanline to cpp * Fix bucket test * Remove unused dependency * Compile library in codemagic * Temp set to internal track * Change names * Add image cpp source to xcode * Endl * Return exit code * Refactor flood fill * Don't push new snapshot if bucket is cancelled * Remove stopwatch * Clean up bin button * Dispose of images before removing from history stack * Properly dispose of undo stack * Reduce max number of undos * Doc * Doc * Properly free project memory * Dispose of undo stack when closing drawing page * Dispose of picture * Dispose of generated thumbnail in memory * Remove unrasterized strokes outside the canvas * Remove listeners in dispose * Missing controller dispose * Cleaner return * Fix bucket memory leak * Bump version
- Loading branch information
1 parent
f2da5b2
commit 90fa912
Showing
74 changed files
with
1,219 additions
and
184 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,3 +5,4 @@ gradle-wrapper.jar | |
/gradlew.bat | ||
/local.properties | ||
GeneratedPluginRegistrant.java | ||
/app/.cxx |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
cmake_minimum_required(VERSION 3.4.1) | ||
set (CMAKE_CXX_STANDARD 11) | ||
|
||
add_library( | ||
image | ||
SHARED | ||
flood_fill.cpp | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
#include <stdlib.h> | ||
#include <queue> | ||
#include <utility> | ||
|
||
#define PUBLIC extern "C" __attribute__((visibility("default"))) __attribute__((used)) | ||
|
||
class Image { | ||
private: | ||
uint32_t* pixels_pointer; | ||
int width; | ||
int height; | ||
|
||
public: | ||
Image(uint32_t* pixels_pointer, int width, int height) { | ||
this->pixels_pointer = pixels_pointer; | ||
this->width = width; | ||
this->height = height; | ||
} | ||
|
||
uint32_t* getPixel(int x, int y) { | ||
return pixels_pointer + (y * width + x); | ||
} | ||
}; | ||
|
||
struct LineFillTask { | ||
int x; | ||
int y; | ||
int parentDy; // y + parentDy is the y coordinate of parent | ||
int parentXl; // left x of parent's filled line | ||
int parentXr; // right x of parent's filled line | ||
|
||
LineFillTask(int x, int y, int parentDy, int parentXl, int parentXr) { | ||
this->x = x; | ||
this->y = y; | ||
this->parentDy = parentDy; | ||
this->parentXl = parentXl; | ||
this->parentXr = parentXr; | ||
} | ||
}; | ||
|
||
PUBLIC | ||
// Floods the 4-connected color area with another color. | ||
// Returns 0 if successful. | ||
// Returns -1 if cancelled. | ||
int flood_fill(uint32_t* pixels_pointer, int width, int height, int x, int y, int fillColor) { | ||
auto image = Image(pixels_pointer, width, height); | ||
|
||
uint32_t oldColor = *image.getPixel(x, y); | ||
|
||
if (oldColor == fillColor) return -1; | ||
|
||
std::queue<LineFillTask> q; | ||
q.push(LineFillTask(x, y, 0, 0, 0)); | ||
|
||
auto scanLine = [&](int xl, int xr, int y, int parentDy) { | ||
bool streak = false; | ||
for (int x = xl; x <= xr; x++) { | ||
if (!streak && *image.getPixel(x, y) == oldColor) { | ||
q.push(LineFillTask(x, y, parentDy, xl, xr)); | ||
streak = true; | ||
} | ||
else if (streak && *image.getPixel(x, y) != oldColor) { | ||
streak = false; | ||
} | ||
} | ||
}; | ||
|
||
while (!q.empty()) { | ||
auto t = q.front(); | ||
q.pop(); | ||
|
||
x = t.x; | ||
y = t.y; | ||
|
||
int xl = x; | ||
int xr = x; | ||
|
||
// Find start of the line. | ||
while (xl - 1 >= 0 && *image.getPixel(xl - 1, y) == oldColor) xl--; | ||
|
||
// Fill the whole line. | ||
for (int x = xl; x < width && *image.getPixel(x, y) == oldColor; x++) { | ||
*image.getPixel(x, y) = fillColor; | ||
xr = x; | ||
} | ||
|
||
// Scan for new lines above. | ||
if (t.parentDy == -1) { | ||
if (xl < t.parentXl) scanLine(xl, t.parentXl, y - 1, 1); | ||
if (xr > t.parentXr) scanLine(t.parentXr, xr, y - 1, 1); | ||
} | ||
else if (y > 0) { | ||
scanLine(xl, xr, y - 1, 1); | ||
} | ||
|
||
// Scan for new lines below. | ||
if (t.parentDy == 1) { | ||
if (xl < t.parentXl) scanLine(xl, t.parentXl, y + 1, -1); | ||
if (xr > t.parentXr) scanLine(t.parentXr, xr, y + 1, -1); | ||
} | ||
else if (y < height - 1) { | ||
scanLine(xl, xr, y + 1, -1); | ||
} | ||
} | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import 'dart:ui'; | ||
|
||
extension ColorMethods on Color { | ||
int toRGBA() { | ||
return [ | ||
red, | ||
green, | ||
blue, | ||
alpha, | ||
].reduce((color, channel) => (color << 8) | channel); | ||
} | ||
|
||
int toABGR() { | ||
return [ | ||
alpha, | ||
blue, | ||
green, | ||
red, | ||
].reduce((color, channel) => (color << 8) | channel); | ||
} | ||
} |
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import 'dart:isolate'; | ||
import 'dart:typed_data'; | ||
import 'dart:ui' as ui; | ||
|
||
import 'package:mooltik/common/data/extensions/color_methods.dart'; | ||
import 'package:mooltik/common/data/io/image.dart'; | ||
import 'package:mooltik/ffi_bridge.dart'; | ||
|
||
/// Flood fills [image] with the given [color] starting at [startX], [startY]. | ||
Future<ui.Image?> floodFill( | ||
ui.Image source, | ||
int startX, | ||
int startY, | ||
ui.Color color, | ||
) async { | ||
final imageByteData = await source.toByteData(); | ||
|
||
// Can be refactored with `compute` after this PR (https://github.com/flutter/flutter/pull/86591) lands in stable. | ||
final receivePort = ReceivePort(); | ||
|
||
final isolate = await Isolate.spawn( | ||
_fillIsolate, | ||
_FillIsolateParams( | ||
imageByteData: imageByteData!, | ||
width: source.width, | ||
height: source.height, | ||
startX: startX, | ||
startY: startY, | ||
fillColor: color, | ||
sendPort: receivePort.sendPort, | ||
), | ||
); | ||
|
||
final resultByteData = await receivePort.first as ByteData?; | ||
|
||
receivePort.close(); | ||
isolate.kill(); | ||
|
||
if (resultByteData == null) return null; | ||
|
||
return imageFromBytes(resultByteData, source.width, source.height); | ||
} | ||
|
||
class _FillIsolateParams { | ||
final ByteData imageByteData; | ||
final int width; | ||
final int height; | ||
final int startX; | ||
final int startY; | ||
final ui.Color fillColor; | ||
final SendPort sendPort; | ||
|
||
_FillIsolateParams({ | ||
required this.imageByteData, | ||
required this.width, | ||
required this.height, | ||
required this.startX, | ||
required this.startY, | ||
required this.fillColor, | ||
required this.sendPort, | ||
}); | ||
} | ||
|
||
void _fillIsolate(_FillIsolateParams params) { | ||
final exitCode = FFIBridge().floodFill( | ||
params.imageByteData.buffer.asUint32List(), | ||
params.width, | ||
params.height, | ||
params.startX, | ||
params.startY, | ||
params.fillColor.toABGR(), | ||
); | ||
|
||
final result = exitCode == 0 ? params.imageByteData : null; | ||
|
||
params.sendPort.send(result); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import 'dart:async'; | ||
import 'dart:typed_data'; | ||
|
||
import 'dart:ui'; | ||
|
||
Future<Image> imageFromBytes(ByteData bytes, int width, int height) { | ||
final Completer<Image> completer = Completer<Image>(); | ||
decodeImageFromPixels( | ||
bytes.buffer.asUint8List(), | ||
width, | ||
height, | ||
PixelFormat.rgba8888, | ||
(Image image) => completer.complete(image), | ||
); | ||
return completer.future; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import 'dart:collection'; | ||
|
||
typedef AsyncTask = Future<void> Function(); | ||
|
||
/// Executes async functions in order. | ||
class TaskQueue { | ||
final _queue = Queue<AsyncTask>(); | ||
|
||
void add(AsyncTask task) { | ||
_queue.add(task); | ||
if (!_isRunning) _run(); | ||
} | ||
|
||
bool _isRunning = false; | ||
|
||
void _run() async { | ||
_isRunning = true; | ||
|
||
while (_queue.isNotEmpty) { | ||
final task = _queue.removeFirst(); | ||
await task(); | ||
} | ||
|
||
_isRunning = false; | ||
} | ||
} |
Oops, something went wrong.