fix: crop save always JPEG q93 + save rotated view 1:1 (v1.4.1)
- Output extension is always .jpg regardless of source format (was: PNG in -> PNG out) - Rotation now saved correctly: viewport grid resampled bilinearly in source space (was: unrotated bounding box copy) - Quality 90 -> 93; l10n strings updated (JPEG 93) - New: tool/crop_test.dart — pure-Dart end-to-end test (dart run tool/crop_test.dart)
This commit is contained in:
1 parent
268dc879c8
commit
c34a3bfd95
11 files changed
+225
-70
No files matched your search
@@ -5,6 +5,7 @@ import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
import '../services/crop_save_service.dart';
|
||||
|
||||
class ImageCanvas extends StatefulWidget {
|
||||
final String filePath;
|
||||
@@ -396,42 +397,28 @@ class ImageCanvasState extends State<ImageCanvas> {
|
||||
return _cachedMatrix;
|
||||
}
|
||||
|
||||
/// Viewport'un görünen bölgesini kaynak (tam çözünürlük) uzayına eşler.
|
||||
/// [w]x[h] = gerçek dosya boyutu. Degrade durumda null.
|
||||
Rect? cropSourceRect(int w, int h) {
|
||||
/// Viewport'un gorunen bolgesinin tam cozunurluktaki eslemesi (crop_save_service'e).
|
||||
/// Degrade durumda null.
|
||||
CropViewParams? cropViewParams(int w, int h) {
|
||||
if (_vpSize.width <= 0 || _vpSize.height <= 0 || w <= 0 || h <= 0) return null;
|
||||
final cx = _vpSize.width / 2, cy = _vpSize.height / 2;
|
||||
final rad = -_angle * math.pi / 180;
|
||||
final cos = math.cos(rad), sin = math.sin(rad);
|
||||
|
||||
Offset toSrc(Offset p) {
|
||||
final double qx = (p.dx - _tx) / _sc, qy = (p.dy - _ty) / _sc;
|
||||
final double rx = (_angle == 0) ? qx : (cos * (qx - cx) - sin * (qy - cy) + cx);
|
||||
final double ry = (_angle == 0) ? qy : (sin * (qx - cx) + cos * (qy - cy) + cy);
|
||||
final double k = _isFilled
|
||||
? math.max(_vpSize.width / w, _vpSize.height / h)
|
||||
: math.min(_vpSize.width / w, _vpSize.height / h);
|
||||
final double ox = (_vpSize.width - w * k) / 2, oy = (_vpSize.height - h * k) / 2;
|
||||
return Offset((rx - ox) / k, (ry - oy) / k);
|
||||
}
|
||||
|
||||
final corners = [
|
||||
toSrc(Offset.zero),
|
||||
toSrc(Offset(_vpSize.width, 0)),
|
||||
toSrc(Offset(_vpSize.width, _vpSize.height)),
|
||||
toSrc(Offset(0, _vpSize.height)),
|
||||
];
|
||||
double xmin = double.infinity, xmax = -double.infinity;
|
||||
double ymin = double.infinity, ymax = -double.infinity;
|
||||
for (final c in corners) {
|
||||
xmin = math.min(xmin, c.dx); xmax = math.max(xmax, c.dx);
|
||||
ymin = math.min(ymin, c.dy); ymax = math.max(ymax, c.dy);
|
||||
}
|
||||
return Rect.fromLTRB(
|
||||
xmin.clamp(0.0, w.toDouble()),
|
||||
ymin.clamp(0.0, h.toDouble()),
|
||||
xmax.clamp(0.0, w.toDouble()),
|
||||
ymax.clamp(0.0, h.toDouble()),
|
||||
final k = _isFilled
|
||||
? math.max(_vpSize.width / w, _vpSize.height / h)
|
||||
: math.min(_vpSize.width / w, _vpSize.height / h);
|
||||
final ox = (_vpSize.width - w * k) / 2, oy = (_vpSize.height - h * k) / 2;
|
||||
return CropViewParams(
|
||||
w: w,
|
||||
h: h,
|
||||
vw: _vpSize.width,
|
||||
vh: _vpSize.height,
|
||||
tx: _tx,
|
||||
ty: _ty,
|
||||
sc: _sc,
|
||||
angleDeg: _angle,
|
||||
cx: _vpSize.width / 2,
|
||||
cy: _vpSize.height / 2,
|
||||
k: k,
|
||||
ox: ox,
|
||||
oy: oy,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user