Skip to content

Commit

Permalink
Added Color and Image extensions (flame-engine#653)
Browse files Browse the repository at this point in the history
Co-authored-by: Erick <[email protected]>
  • Loading branch information
wolfenrain and erickzanardo authored Feb 9, 2021
1 parent b1fa449 commit ac40b2c
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 17 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
- Enabling direct import of Sprite and SpriteAnimation
- Renamed `Composition` to `ImageComposition` to prevent confusion with the composition component
- Added `rotation` and `anchor` arguments to `ImageComposition.add`
- Added `Image` extensions
- Added `Color` extensions
- Change RaisedButton to ElevatedButton in timer example

## 1.0.0-rc6
Expand Down
2 changes: 2 additions & 0 deletions lib/extensions.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export 'src/extensions/canvas.dart';
export 'src/extensions/color.dart';
export 'src/extensions/image.dart';
export 'src/extensions/offset.dart';
export 'src/extensions/rect.dart';
export 'src/extensions/size.dart';
Expand Down
27 changes: 10 additions & 17 deletions lib/image_composition.dart
Original file line number Diff line number Diff line change
Expand Up @@ -86,26 +86,19 @@ class ImageComposition {
}) {
assert(image != null, 'Image is required to add to the Atlas');
assert(position != null, 'Position is required');
assert(
source == null ||
source.width <= image.width &&
source.height <= image.height &&
source.top + source.height <= image.height &&
source.top >= 0 &&
source.left + source.width <= image.width &&
source.left >= 0,
'Source rect should fit within in the image constraints',
);
assert(angle != null, 'rotation can not be null');
assert(angle != null, 'angle can not be null');

final imageRect = image.getBoundingRect();
source ??= imageRect;
anchor ??= source.toVector2() / 2;
blendMode ??= defaultBlendMode;
isAntiAlias ??= defaultAntiAlias;
source ??= Rect.fromLTWH(
0,
0,
image.width.toDouble(),
image.height.toDouble(),

assert(
imageRect.contains(source.topLeft) &&
imageRect.contains(source.bottomRight),
'Source rect should fit within in the image constraints',
);
anchor ??= source.toVector2() / 2;

_composes.add(_Composed(
image, position, source, angle, anchor, isAntiAlias, blendMode));
Expand Down
38 changes: 38 additions & 0 deletions lib/src/extensions/color.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import 'dart:ui';

export 'dart:ui' show Color;

extension ColorExtension on Color {
/// Darken the shade of the color by the [amount].
///
/// [amount] is a double between 0 and 1.
///
/// Based on: https://stackoverflow.com/a/60191441.
Color darken(double amount) {
assert(amount >= 0 && amount <= 1);

final f = 1 - amount;
return Color.fromARGB(
alpha,
(red * f).round(),
(green * f).round(),
(blue * f).round(),
);
}

/// Brighten the shade of the color by the [amount].
///
/// [amount] is a double between 0 and 1.
///
/// Based on: https://stackoverflow.com/a/60191441.
Color brighten(double amount) {
assert(amount >= 0 && amount <= 1);

return Color.fromARGB(
alpha,
red + ((255 - red) * amount).round(),
green + ((255 - green) * amount).round(),
blue + ((255 - blue) * amount).round(),
);
}
}
73 changes: 73 additions & 0 deletions lib/src/extensions/image.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import 'dart:typed_data';
import 'dart:ui';

import '../flame.dart';
import 'color.dart';

export 'dart:ui' show Image;

extension ImageExtension on Image {
/// Helper method for retrieve the pixel data in a Uint8 format.
///
/// Pixel order used the [ImageByteFormat.rawRgba] meaning it is: R G B A.
Future<Uint8List> pixelsInUint8() async {
return (await toByteData()).buffer.asUint8List();
}

/// Returns the bounding [Rect] of the image.
Rect getBoundingRect() {
return Rect.fromLTWH(0, 0, width.toDouble(), height.toDouble());
}

/// Change each pixel's color to be darker and return a new [Image].
///
/// The [amount] is a double value between 0 and 1.
Future<Image> darken(double amount) async {
assert(amount >= 0 && amount <= 1);

final pixelData = await pixelsInUint8();
final newPixelData = Uint8List(pixelData.length);

for (var i = 0; i < pixelData.length; i += 4) {
final color = Color.fromARGB(
pixelData[i + 3],
pixelData[i + 0],
pixelData[i + 1],
pixelData[i + 2],
).darken(amount);

newPixelData[i] = color.red;
newPixelData[i + 1] = color.green;
newPixelData[i + 2] = color.blue;
newPixelData[i + 3] = color.alpha;
}

return Flame.images.decodeImageFromPixels(newPixelData, width, height);
}

/// Change each pixel's color to be brighter and return a new [Image].
///
/// The [amount] is a double value between 0 and 1.
Future<Image> brighten(double amount) async {
assert(amount >= 0 && amount <= 1);

final pixelData = await pixelsInUint8();
final newPixelData = Uint8List(pixelData.length);

for (var i = 0; i < pixelData.length; i += 4) {
final color = Color.fromARGB(
pixelData[i + 3],
pixelData[i + 0],
pixelData[i + 1],
pixelData[i + 2],
).brighten(amount);

newPixelData[i] = color.red;
newPixelData[i + 1] = color.green;
newPixelData[i + 2] = color.blue;
newPixelData[i + 3] = color.alpha;
}

return Flame.images.decodeImageFromPixels(newPixelData, width, height);
}
}

0 comments on commit ac40b2c

Please sign in to comment.