David PHAM-VAN

Add more color functions

@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
2 2
3 ## 1.3.24 3 ## 1.3.24
4 4
  5 +- Add more color functions
5 - Fix Pdf format 6 - Fix Pdf format
6 - Fix warning in tests 7 - Fix warning in tests
7 - Fix warning in example 8 - Fix warning in example
@@ -40,6 +40,65 @@ class PdfColor { @@ -40,6 +40,65 @@ class PdfColor {
40 (int.parse(color.substring(6, 7), radix: 16) >> 24 & 0xff) / 255.0); 40 (int.parse(color.substring(6, 7), radix: 16) >> 24 & 0xff) / 255.0);
41 } 41 }
42 42
  43 + factory PdfColor.fromRYB(double red, double yellow, double blue,
  44 + [double alpha = 1.0]) {
  45 + assert(red >= 0 && red <= 1);
  46 + assert(yellow >= 0 && yellow <= 1);
  47 + assert(blue >= 0 && blue <= 1);
  48 + assert(alpha >= 0 && alpha <= 1);
  49 +
  50 + const List<List<double>> magic = <List<double>>[
  51 + <double>[1, 1, 1],
  52 + <double>[1, 1, 0],
  53 + <double>[1, 0, 0],
  54 + <double>[1, 0.5, 0],
  55 + <double>[0.163, 0.373, 0.6],
  56 + <double>[0.0, 0.66, 0.2],
  57 + <double>[0.5, 0.0, 0.5],
  58 + <double>[0.2, 0.094, 0.0]
  59 + ];
  60 +
  61 + double cubicInt(double t, double A, double B) {
  62 + final double weight = t * t * (3 - 2 * t);
  63 + return A + weight * (B - A);
  64 + }
  65 +
  66 + double getRed(double iR, double iY, double iB) {
  67 + final double x0 = cubicInt(iB, magic[0][0], magic[4][0]);
  68 + final double x1 = cubicInt(iB, magic[1][0], magic[5][0]);
  69 + final double x2 = cubicInt(iB, magic[2][0], magic[6][0]);
  70 + final double x3 = cubicInt(iB, magic[3][0], magic[7][0]);
  71 + final double y0 = cubicInt(iY, x0, x1);
  72 + final double y1 = cubicInt(iY, x2, x3);
  73 + return cubicInt(iR, y0, y1);
  74 + }
  75 +
  76 + double getGreen(double iR, double iY, double iB) {
  77 + final double x0 = cubicInt(iB, magic[0][1], magic[4][1]);
  78 + final double x1 = cubicInt(iB, magic[1][1], magic[5][1]);
  79 + final double x2 = cubicInt(iB, magic[2][1], magic[6][1]);
  80 + final double x3 = cubicInt(iB, magic[3][1], magic[7][1]);
  81 + final double y0 = cubicInt(iY, x0, x1);
  82 + final double y1 = cubicInt(iY, x2, x3);
  83 + return cubicInt(iR, y0, y1);
  84 + }
  85 +
  86 + double getBlue(double iR, double iY, double iB) {
  87 + final double x0 = cubicInt(iB, magic[0][2], magic[4][2]);
  88 + final double x1 = cubicInt(iB, magic[1][2], magic[5][2]);
  89 + final double x2 = cubicInt(iB, magic[2][2], magic[6][2]);
  90 + final double x3 = cubicInt(iB, magic[3][2], magic[7][2]);
  91 + final double y0 = cubicInt(iY, x0, x1);
  92 + final double y1 = cubicInt(iY, x2, x3);
  93 + return cubicInt(iR, y0, y1);
  94 + }
  95 +
  96 + final double redValue = getRed(red, yellow, blue);
  97 + final double greenValue = getGreen(red, yellow, blue);
  98 + final double blueValue = getBlue(red, yellow, blue);
  99 + return PdfColor(redValue, greenValue, blueValue, alpha);
  100 + }
  101 +
43 final double alpha; 102 final double alpha;
44 final double red; 103 final double red;
45 final double green; 104 final double green;
@@ -392,4 +392,13 @@ class PdfColors { @@ -392,4 +392,13 @@ class PdfColors {
392 orangeAccent, 392 orangeAccent,
393 deepOrangeAccent, 393 deepOrangeAccent,
394 ]; 394 ];
  395 +
  396 + static PdfColor getColor(int index) {
  397 + final double hue = index * 137.508;
  398 + final PdfColor color = PdfColorHsv(hue % 360, 1, 1);
  399 + if ((index / 3) % 2 == 0) {
  400 + return PdfColor.fromRYB(color.red, color.green, color.blue);
  401 + }
  402 + return color;
  403 + }
395 } 404 }
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 16
17 import 'dart:io'; 17 import 'dart:io';
  18 +import 'dart:math' as math;
18 19
19 import 'package:pdf/pdf.dart'; 20 import 'package:pdf/pdf.dart';
20 import 'package:pdf/widgets.dart'; 21 import 'package:pdf/widgets.dart';
@@ -57,10 +58,125 @@ class Color extends StatelessWidget { @@ -57,10 +58,125 @@ class Color extends StatelessWidget {
57 } 58 }
58 } 59 }
59 60
  61 +enum ColorSpace { rgb, ryb, cmy }
  62 +
  63 +class ColorWheel extends Widget {
  64 + ColorWheel({
  65 + this.colorSpace = ColorSpace.rgb,
  66 + this.divisions = 12,
  67 + this.rings = 5,
  68 + this.brightness = .5,
  69 + }) : assert(brightness >= 0 && brightness <= 1);
  70 +
  71 + final int divisions;
  72 + final int rings;
  73 + final double brightness;
  74 + final ColorSpace colorSpace;
  75 +
  76 + @override
  77 + void layout(Context context, BoxConstraints constraints,
  78 + {bool parentUsesSize = false}) {
  79 + box = PdfRect.fromPoints(PdfPoint.zero, constraints.biggest);
  80 + }
  81 +
  82 + void drawFilledArc(Context context, double centerX, double centerY,
  83 + double angleStart, double angleEnd, double radius1, double radius2) {
  84 + assert(radius1 > radius2);
  85 +
  86 + final PdfPoint startTop = PdfPoint(
  87 + box.x + centerX + math.cos(angleStart) * radius1,
  88 + box.y + centerY + math.sin(angleStart) * radius1,
  89 + );
  90 + final PdfPoint endTop = PdfPoint(
  91 + box.x + centerX + math.cos(angleEnd) * radius1,
  92 + box.y + centerY + math.sin(angleEnd) * radius1,
  93 + );
  94 + final PdfPoint startBottom = PdfPoint(
  95 + box.x + centerX + math.cos(angleStart) * radius2,
  96 + box.y + centerY + math.sin(angleStart) * radius2,
  97 + );
  98 + final PdfPoint endBottom = PdfPoint(
  99 + box.x + centerX + math.cos(angleEnd) * radius2,
  100 + box.y + centerY + math.sin(angleEnd) * radius2,
  101 + );
  102 +
  103 + context.canvas
  104 + ..moveTo(startTop.x, startTop.y)
  105 + ..bezierArc(startTop.x, startTop.y, radius1, radius1, endTop.x, endTop.y,
  106 + large: false, sweep: true)
  107 + ..lineTo(endBottom.x, endBottom.y)
  108 + ..bezierArc(endBottom.x, endBottom.y, radius2, radius2, startBottom.x,
  109 + startBottom.y, large: false)
  110 + ..lineTo(startTop.x, startTop.y)
  111 + ..fillPath()
  112 + ..moveTo(startTop.x, startTop.y)
  113 + ..bezierArc(startTop.x, startTop.y, radius1, radius1, endTop.x, endTop.y,
  114 + large: false, sweep: true)
  115 + ..lineTo(endBottom.x, endBottom.y)
  116 + ..bezierArc(endBottom.x, endBottom.y, radius2, radius2, startBottom.x,
  117 + startBottom.y,
  118 + large: false)
  119 + ..lineTo(startTop.x, startTop.y)
  120 + ..strokePath();
  121 + }
  122 +
  123 + @override
  124 + void paint(Context context) {
  125 + super.paint(context);
  126 +
  127 + final double centerX = box.width / 2;
  128 + final double centerY = box.height / 2;
  129 + final double step = math.pi * 2 / divisions;
  130 + final double angleStart = math.pi / 2 - step;
  131 +
  132 + final double ringStep = math.min(centerX, centerY) / rings;
  133 +
  134 + context.canvas.setStrokeColor(PdfColors.black);
  135 +
  136 + for (int ring = 0; ring <= rings; ring++) {
  137 + final double radius1 = ringStep * ring;
  138 + final double radius2 = ringStep * (ring - 1);
  139 + for (double angle = 0; angle < math.pi * 2; angle += step) {
  140 + final PdfColor ic =
  141 + PdfColorHsl(angle / math.pi * 180, ring / rings, brightness);
  142 +
  143 + switch (colorSpace) {
  144 + case ColorSpace.rgb:
  145 + context.canvas.setFillColor(ic);
  146 + break;
  147 + case ColorSpace.ryb:
  148 + context.canvas
  149 + .setFillColor(PdfColor.fromRYB(ic.red, ic.green, ic.blue));
  150 + break;
  151 + case ColorSpace.cmy:
  152 + context.canvas
  153 + .setFillColor(PdfColorCmyk(ic.red, ic.green, ic.blue, 0));
  154 + break;
  155 + }
  156 +
  157 + drawFilledArc(
  158 + context,
  159 + centerX,
  160 + centerY,
  161 + angleStart + angle,
  162 + angleStart + angle + step,
  163 + radius1,
  164 + radius2,
  165 + );
  166 + }
  167 + }
  168 + }
  169 +}
  170 +
  171 +Document pdf;
  172 +
60 void main() { 173 void main() {
61 - test('Pdf Colors', () {  
62 - final Document pdf = Document(title: 'Material Colors'); 174 + setUpAll(() {
  175 + Document.debug = true;
  176 + pdf = Document();
  177 + });
63 178
  179 + test('Pdf Colors', () {
64 pdf.addPage(MultiPage( 180 pdf.addPage(MultiPage(
65 pageFormat: PdfPageFormat.standard, 181 pageFormat: PdfPageFormat.standard,
66 build: (Context context) => <Widget>[ 182 build: (Context context) => <Widget>[
@@ -546,7 +662,52 @@ void main() { @@ -546,7 +662,52 @@ void main() {
546 Color(PdfColors.black, 'Black'), 662 Color(PdfColors.black, 'Black'),
547 ]), 663 ]),
548 ])); 664 ]));
  665 + });
  666 +
  667 + test('Pdf Colors Wheel', () {
  668 + const Map<ColorSpace, String> wheels = <ColorSpace, String>{
  669 + ColorSpace.rgb: 'Red Green Blue',
  670 + ColorSpace.ryb: 'Red Yellow Blue',
  671 + ColorSpace.cmy: 'Cyan Magenta Yellow',
  672 + };
  673 +
  674 + wheels.forEach((ColorSpace colorSpace, String name) {
  675 + pdf.addPage(Page(
  676 + build: (Context context) => Column(
  677 + children: <Widget>[
  678 + Header(text: name),
  679 + SizedBox(
  680 + height: context.page.pageFormat.availableWidth,
  681 + child: ColorWheel(
  682 + colorSpace: colorSpace,
  683 + ),
  684 + ),
  685 + ],
  686 + )));
  687 + });
  688 + });
  689 +
  690 + test('Pdf Colors Generator', () {
  691 + const double widthCount = 26;
  692 + const PdfPageFormat format = PdfPageFormat(400, 400);
  693 + final double w = (format.width - 1) / widthCount;
  694 + final int count = widthCount * (format.height - 1) ~/ w;
  695 +
  696 + pdf.addPage(MultiPage(
  697 + pageFormat: format,
  698 + build: (Context context) => <Widget>[
  699 + Wrap(
  700 + children: List<Widget>.generate(count, (int i) {
  701 + return Container(
  702 + width: w,
  703 + height: w,
  704 + color: PdfColors.getColor(i),
  705 + );
  706 + })),
  707 + ]));
  708 + });
549 709
  710 + tearDownAll(() {
550 final File file = File('colors.pdf'); 711 final File file = File('colors.pdf');
551 file.writeAsBytesSync(pdf.save()); 712 file.writeAsBytesSync(pdf.save());
552 }); 713 });