-
-
Notifications
You must be signed in to change notification settings - Fork 272
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add resize function for in-place resizing
- Loading branch information
1 parent
12f3e66
commit cf5f6d9
Showing
4 changed files
with
370 additions
and
2 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
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,190 @@ | ||
import 'dart:typed_data'; | ||
|
||
import '../color/color.dart'; | ||
import '../image/image.dart'; | ||
import '../image/interpolation.dart'; | ||
import '../util/image_exception.dart'; | ||
import 'bake_orientation.dart'; | ||
import 'copy_resize.dart'; | ||
|
||
Image resize(Image src, | ||
{int? width, | ||
int? height, | ||
bool? maintainAspect, | ||
Color? backgroundColor, | ||
Interpolation interpolation = Interpolation.nearest}) { | ||
if (width == null && height == null) { | ||
throw ImageException('Invalid size'); | ||
} | ||
|
||
// You can't interpolate index pixels | ||
if (src.hasPalette) { | ||
interpolation = Interpolation.nearest; | ||
} | ||
|
||
if (src.exif.imageIfd.hasOrientation && src.exif.imageIfd.orientation != 1) { | ||
src = bakeOrientation(src); | ||
} | ||
|
||
var x1 = 0; | ||
var y1 = 0; | ||
var x2 = 0; | ||
var y2 = 0; | ||
|
||
// this block sets [width] and [height] if null or negative. | ||
if (width != null && height != null && maintainAspect == true) { | ||
x1 = 0; | ||
x2 = width; | ||
final srcAspect = src.height / src.width; | ||
final h = (width * srcAspect).toInt(); | ||
final dy = (height - h) ~/ 2; | ||
y1 = dy; | ||
y2 = y1 + h; | ||
if (y1 < 0 || y2 > height) { | ||
y1 = 0; | ||
y2 = height; | ||
final srcAspect = src.width / src.height; | ||
final w = (height * srcAspect).toInt(); | ||
final dx = (width - w) ~/ 2; | ||
x1 = dx; | ||
x2 = x1 + w; | ||
} | ||
} else { | ||
maintainAspect = false; | ||
} | ||
|
||
if (height == null || height <= 0) { | ||
height = (width! * (src.height / src.width)).round(); | ||
} | ||
if (width == null || width <= 0) { | ||
width = (height * (src.width / src.height)).round(); | ||
} | ||
|
||
final w = maintainAspect! ? x2 - x1 : width; | ||
final h = maintainAspect ? y2 - y1 : height; | ||
|
||
if (!maintainAspect) { | ||
x1 = 0; | ||
x2 = width; | ||
y1 = 0; | ||
y2 = height; | ||
} | ||
|
||
if (width == src.width && height == src.height) { | ||
return src; | ||
} | ||
|
||
if ((width * height) > (src.width * src.height)) { | ||
return copyResize(src, width: width, height: height, | ||
maintainAspect: maintainAspect, backgroundColor: backgroundColor, | ||
interpolation: interpolation); | ||
} | ||
|
||
final scaleX = Int32List(w); | ||
final dx = src.width / w; | ||
for (var x = 0; x < w; ++x) { | ||
scaleX[x] = (x * dx).toInt(); | ||
} | ||
|
||
final origWidth = src.width; | ||
final origHeight = src.height; | ||
|
||
final numFrames = src.numFrames; | ||
for (var i = 0; i < numFrames; ++i) { | ||
final frame = src.frames[i]; | ||
final dst = frame; | ||
|
||
final dy = frame.height / h; | ||
final dx = frame.width / w; | ||
|
||
if (maintainAspect && backgroundColor != null) { | ||
dst.clear(backgroundColor); | ||
} | ||
|
||
if (interpolation == Interpolation.average) { | ||
for (var y = 0; y < h; ++y) { | ||
final ay1 = (y * dy).toInt(); | ||
var ay2 = ((y + 1) * dy).toInt(); | ||
if (ay2 == ay1) { | ||
ay2++; | ||
} | ||
|
||
for (var x = 0; x < w; ++x) { | ||
final ax1 = (x * dx).toInt(); | ||
var ax2 = ((x + 1) * dx).toInt(); | ||
if (ax2 == ax1) { | ||
ax2++; | ||
} | ||
|
||
num r = 0; | ||
num g = 0; | ||
num b = 0; | ||
num a = 0; | ||
var np = 0; | ||
for (var sy = ay1; sy < ay2; ++sy) { | ||
for (var sx = ax1; sx < ax2; ++sx, ++np) { | ||
final s = frame.getPixel(sx, sy); | ||
r += s.r; | ||
g += s.g; | ||
b += s.b; | ||
a += s.a; | ||
} | ||
} | ||
final c = dst.getColor(r / np, g / np, b / np, a / np); | ||
|
||
dst.data!.width = width; | ||
dst.data!.height = height; | ||
dst.setPixel(x1 + x, y1 + y, c); | ||
dst.data!.width = origWidth; | ||
dst.data!.height = origHeight; | ||
} | ||
} | ||
} else if (interpolation == Interpolation.nearest) { | ||
if (frame.hasPalette) { | ||
for (var y = 0; y < h; ++y) { | ||
final y2 = (y * dy).toInt(); | ||
for (var x = 0; x < w; ++x) { | ||
final p = frame.getPixelIndex(scaleX[x], y2); | ||
dst.data!.width = width; | ||
dst.data!.height = height; | ||
dst.setPixelIndex(x1 + x, y1 + y, p); | ||
dst.data!.width = origWidth; | ||
dst.data!.height = origHeight; | ||
} | ||
} | ||
} else { | ||
for (var y = 0; y < h; ++y) { | ||
final y2 = (y * dy).toInt(); | ||
for (var x = 0; x < w; ++x) { | ||
final p = frame.getPixel(scaleX[x], y2); | ||
dst.data!.width = width; | ||
dst.data!.height = height; | ||
dst.setPixel(x1 + x, y1 + y, p); | ||
dst.data!.width = origWidth; | ||
dst.data!.height = origHeight; | ||
} | ||
} | ||
} | ||
} else { | ||
// Copy the pixels from this image to the new image. | ||
for (var y = 0; y < h; ++y) { | ||
final sy2 = y * dy; | ||
for (var x = 0; x < w; ++x) { | ||
final sx2 = x * dx; | ||
final p = frame.getPixelInterpolate(x1 + sx2, y1 + sy2, | ||
interpolation: interpolation); | ||
dst.data!.width = width; | ||
dst.data!.height = height; | ||
dst.setPixel(x, y, p); | ||
dst.data!.width = origWidth; | ||
dst.data!.height = origHeight; | ||
} | ||
} | ||
} | ||
|
||
dst.data!.width = width; | ||
dst.data!.height = height; | ||
} | ||
|
||
return src; | ||
} |
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,177 @@ | ||
import 'dart:io'; | ||
import 'package:image/image.dart'; | ||
import 'package:test/test.dart'; | ||
|
||
import '../_test_util.dart'; | ||
|
||
void main() { | ||
group('Transform', () { | ||
test('resize nearest', () { | ||
final img = | ||
decodePng(File('test/_data/png/buck_24.png').readAsBytesSync())!; | ||
final i0 = resize(img, width: 64); | ||
expect(i0.width, equals(64)); | ||
expect(i0.height, equals(40)); | ||
File('$testOutputPath/transform/resize.png') | ||
..createSync(recursive: true) | ||
..writeAsBytesSync(encodePng(i0)); | ||
}); | ||
|
||
test('resize average', () { | ||
final img = | ||
decodePng(File('test/_data/png/buck_24.png').readAsBytesSync())!; | ||
final i0 = | ||
resize(img, width: 64, interpolation: Interpolation.average); | ||
expect(i0.width, equals(64)); | ||
expect(i0.height, equals(40)); | ||
File('$testOutputPath/transform/resize_average.png') | ||
..createSync(recursive: true) | ||
..writeAsBytesSync(encodePng(i0)); | ||
}); | ||
|
||
test('resize linear', () { | ||
final img = | ||
decodePng(File('test/_data/png/buck_24.png').readAsBytesSync())!; | ||
final i0 = | ||
resize(img, width: 64, interpolation: Interpolation.linear); | ||
expect(i0.width, equals(64)); | ||
expect(i0.height, equals(40)); | ||
File('$testOutputPath/transform/resize_linear.png') | ||
..createSync(recursive: true) | ||
..writeAsBytesSync(encodePng(i0)); | ||
}); | ||
|
||
test('resize cubic', () { | ||
final img = | ||
decodePng(File('test/_data/png/buck_24.png').readAsBytesSync())!; | ||
final i0 = resize(img, width: 64, interpolation: Interpolation.cubic); | ||
expect(i0.width, equals(64)); | ||
expect(i0.height, equals(40)); | ||
File('$testOutputPath/transform/resize_cubic.png') | ||
..createSync(recursive: true) | ||
..writeAsBytesSync(encodePng(i0)); | ||
}); | ||
|
||
test('resize maintainAspect', () { | ||
final img = | ||
decodePng(File('test/_data/png/buck_24.png').readAsBytesSync())!; | ||
final i0 = resize(img, | ||
width: 640, | ||
height: 640, | ||
maintainAspect: true, | ||
backgroundColor: ColorRgb8(0, 0, 255)); | ||
expect(i0.width, equals(640)); | ||
expect(i0.height, equals(640)); | ||
File('$testOutputPath/transform/resize_maintainAspect.png') | ||
..createSync(recursive: true) | ||
..writeAsBytesSync(encodePng(i0)); | ||
}); | ||
|
||
test('resize maintainAspect palette', () { | ||
final img = | ||
decodePng(File('test/_data/png/buck_8.png').readAsBytesSync())!; | ||
final i0 = resize(img, | ||
width: 640, | ||
height: 640, | ||
maintainAspect: true, | ||
backgroundColor: ColorRgb8(0, 0, 255)); | ||
expect(i0.width, equals(640)); | ||
expect(i0.height, equals(640)); | ||
File('$testOutputPath/transform/resize_maintainAspect_palette.png') | ||
..createSync(recursive: true) | ||
..writeAsBytesSync(encodePng(i0)); | ||
}); | ||
|
||
test('resize maintainAspect 2', () { | ||
final i0 = Image(width: 100, height: 50)..clear(ColorRgb8(255, 0, 0)); | ||
final i1 = resize(i0, | ||
width: 200, | ||
height: 200, | ||
maintainAspect: true, | ||
backgroundColor: ColorRgb8(0, 0, 255)); | ||
expect(i1.width, equals(200)); | ||
expect(i1.height, equals(200)); | ||
File('$testOutputPath/transform/resize_maintainAspect_2.png') | ||
..createSync(recursive: true) | ||
..writeAsBytesSync(encodePng(i1)); | ||
}); | ||
|
||
test('resize maintainAspect 3', () { | ||
final i0 = Image(width: 50, height: 100)..clear(ColorRgb8(0, 255, 0)); | ||
final i1 = resize(i0, | ||
width: 200, | ||
height: 200, | ||
maintainAspect: true, | ||
backgroundColor: ColorRgb8(0, 0, 255)); | ||
expect(i1.width, equals(200)); | ||
expect(i1.height, equals(200)); | ||
File('$testOutputPath/transform/resize_maintainAspect_3.png') | ||
..createSync(recursive: true) | ||
..writeAsBytesSync(encodePng(i1)); | ||
}); | ||
|
||
test('resize maintainAspect 4', () { | ||
final i0 = Image(width: 100, height: 50)..clear(ColorRgb8(255, 0, 0)); | ||
final i1 = resize(i0, | ||
width: 50, | ||
height: 100, | ||
maintainAspect: true, | ||
backgroundColor: ColorRgb8(0, 0, 255)); | ||
expect(i1.width, equals(50)); | ||
expect(i1.height, equals(100)); | ||
File('$testOutputPath/transform/resize_maintainAspect_4.png') | ||
..createSync(recursive: true) | ||
..writeAsBytesSync(encodePng(i1)); | ||
}); | ||
|
||
test('resize maintainAspect 5', () { | ||
final i0 = Image(width: 50, height: 100)..clear(ColorRgb8(0, 255, 0)); | ||
final i1 = resize(i0, | ||
width: 100, | ||
height: 50, | ||
maintainAspect: true, | ||
backgroundColor: ColorRgb8(0, 0, 255)); | ||
expect(i1.width, equals(100)); | ||
expect(i1.height, equals(50)); | ||
File('$testOutputPath/transform/resize_maintainAspect_5.png') | ||
..createSync(recursive: true) | ||
..writeAsBytesSync(encodePng(i1)); | ||
}); | ||
|
||
test('resize maintainAspect 5', () { | ||
final i0 = Image(width: 50, height: 100)..clear(ColorRgb8(0, 255, 0)); | ||
final i1 = resize(i0, | ||
width: 100, | ||
height: 500, | ||
maintainAspect: true, | ||
backgroundColor: ColorRgb8(0, 0, 255)); | ||
expect(i1.width, equals(100)); | ||
expect(i1.height, equals(500)); | ||
File('$testOutputPath/transform/resize_maintainAspect_5.png') | ||
..createSync(recursive: true) | ||
..writeAsBytesSync(encodePng(i1)); | ||
}); | ||
|
||
test('resize maintainAspect 6', () { | ||
final i0 = Image(width: 100, height: 50)..clear(ColorRgb8(0, 255, 0)); | ||
final i1 = resize(i0, | ||
width: 500, | ||
height: 100, | ||
maintainAspect: true, | ||
backgroundColor: ColorRgb8(0, 0, 255)); | ||
expect(i1.width, equals(500)); | ||
expect(i1.height, equals(100)); | ||
File('$testOutputPath/transform/resize_maintainAspect_6.png') | ||
..createSync(recursive: true) | ||
..writeAsBytesSync(encodePng(i1)); | ||
}); | ||
|
||
test('resize palette', () async { | ||
final img = await decodePngFile('test/_data/png/test.png'); | ||
final i0 = | ||
resize(img!, width: 64, interpolation: Interpolation.cubic); | ||
await encodePngFile( | ||
'$testOutputPath/transform/resize_palette.png', i0); | ||
}); | ||
}); | ||
} |