1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.imaging.palette;
18
19 import java.util.ArrayList;
20 import java.util.Comparator;
21 import java.util.List;
22
23 import org.apache.commons.imaging.ImagingException;
24
25 public class LongestAxisMedianCut implements MedianCut {
26 private static final Comparator<ColorGroup> COMPARATOR = (cg1, cg2) -> {
27 if (cg1.maxDiff == cg2.maxDiff) {
28 return cg2.diffTotal - cg1.diffTotal;
29 }
30 return cg2.maxDiff - cg1.maxDiff;
31 };
32
33 private void doCut(final ColorGroup colorGroup, final ColorComponent mode, final List<ColorGroup> colorGroups, final boolean ignoreAlpha)
34 throws ImagingException {
35
36 final List<ColorCount> colorCounts = colorGroup.getColorCounts();
37 colorCounts.sort(new ColorCountComparator(mode));
38 final int countHalf = (int) Math.round((double) colorGroup.totalPoints / 2);
39 int oldCount = 0;
40 int newCount = 0;
41 int medianIndex;
42 for (medianIndex = 0; medianIndex < colorCounts.size(); medianIndex++) {
43 final ColorCount colorCount = colorCounts.get(medianIndex);
44
45 newCount += colorCount.count;
46
47 if (newCount >= countHalf) {
48 break;
49 }
50 oldCount = newCount;
51 }
52
53 if (medianIndex == colorCounts.size() - 1) {
54 medianIndex--;
55 } else if (medianIndex > 0) {
56 final int newDiff = Math.abs(newCount - countHalf);
57 final int oldDiff = Math.abs(countHalf - oldCount);
58 if (oldDiff < newDiff) {
59 medianIndex--;
60 }
61 }
62
63 colorGroups.remove(colorGroup);
64 final List<ColorCount> colorCounts1 = new ArrayList<>(colorCounts.subList(0, medianIndex + 1));
65 final List<ColorCount> colorCounts2 = new ArrayList<>(colorCounts.subList(medianIndex + 1, colorCounts.size()));
66
67 final ColorGroup less = new ColorGroup(new ArrayList<>(colorCounts1), ignoreAlpha);
68 colorGroups.add(less);
69 final ColorGroup more = new ColorGroup(new ArrayList<>(colorCounts2), ignoreAlpha);
70 colorGroups.add(more);
71
72 final ColorCount medianValue = colorCounts.get(medianIndex);
73 int limit;
74 switch (mode) {
75 case ALPHA:
76 limit = medianValue.alpha;
77 break;
78 case RED:
79 limit = medianValue.red;
80 break;
81 case GREEN:
82 limit = medianValue.green;
83 break;
84 case BLUE:
85 limit = medianValue.blue;
86 break;
87 default:
88 throw new IllegalArgumentException("Bad mode " + mode);
89 }
90 colorGroup.cut = new ColorGroupCut(less, more, mode, limit);
91 }
92
93 @Override
94 public boolean performNextMedianCut(final List<ColorGroup> colorGroups, final boolean ignoreAlpha) throws ImagingException {
95 colorGroups.sort(COMPARATOR);
96 final ColorGroup colorGroup = colorGroups.get(0);
97
98 if (colorGroup.maxDiff == 0) {
99 return false;
100 }
101 if (!ignoreAlpha && colorGroup.alphaDiff > colorGroup.redDiff && colorGroup.alphaDiff > colorGroup.greenDiff
102 && colorGroup.alphaDiff > colorGroup.blueDiff) {
103 doCut(colorGroup, ColorComponent.ALPHA, colorGroups, ignoreAlpha);
104 } else if (colorGroup.redDiff > colorGroup.greenDiff && colorGroup.redDiff > colorGroup.blueDiff) {
105 doCut(colorGroup, ColorComponent.RED, colorGroups, ignoreAlpha);
106 } else if (colorGroup.greenDiff > colorGroup.blueDiff) {
107 doCut(colorGroup, ColorComponent.GREEN, colorGroups, ignoreAlpha);
108 } else {
109 doCut(colorGroup, ColorComponent.BLUE, colorGroups, ignoreAlpha);
110 }
111 return true;
112 }
113 }