1 /* 2 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javafx.css.converter; 27 28 import javafx.css.Size; 29 import javafx.css.SizeUnits; 30 import com.sun.javafx.css.StyleManager; 31 import javafx.css.ParsedValue; 32 import javafx.css.StyleConverter; 33 import javafx.scene.image.Image; 34 import javafx.scene.paint.CycleMethod; 35 import javafx.scene.paint.ImagePattern; 36 import javafx.scene.paint.LinearGradient; 37 import javafx.scene.paint.Paint; 38 import javafx.scene.paint.RadialGradient; 39 import javafx.scene.paint.Stop; 40 import javafx.scene.text.Font; 41 42 43 /** 44 * @since 9 45 */ 46 public final class PaintConverter extends StyleConverter<ParsedValue<?, Paint>, Paint> { 47 48 // lazy, thread-safe instantiation 49 private static class Holder { 50 static final PaintConverter INSTANCE = new PaintConverter(); 51 static final SequenceConverter SEQUENCE_INSTANCE = new SequenceConverter(); 52 static final LinearGradientConverter LINEAR_GRADIENT_INSTANCE = new LinearGradientConverter(); 53 static final ImagePatternConverter IMAGE_PATTERN_INSTANCE = new ImagePatternConverter(); 54 static final RepeatingImagePatternConverter REPEATING_IMAGE_PATTERN_INSTANCE = new RepeatingImagePatternConverter(); 55 static final RadialGradientConverter RADIAL_GRADIENT_INSTANCE = new RadialGradientConverter(); 56 } 57 58 public static StyleConverter<ParsedValue<?, Paint>, Paint> getInstance() { 59 return Holder.INSTANCE; 60 } 61 62 private PaintConverter() { 63 super(); 64 } 65 66 @Override 67 public Paint convert(ParsedValue<ParsedValue<?, Paint>, Paint> value, Font font) { 68 Object obj = value.getValue(); 69 if (obj instanceof Paint) { 70 return (Paint) obj; 71 } 72 return value.getValue().convert(font); 73 } 74 75 @Override 76 public String toString() { 77 return "PaintConverter"; 78 } 79 80 /** 81 * Converts an array of parsed values to an array of Paint objects. 82 */ 83 public static final class SequenceConverter extends StyleConverter<ParsedValue<?, Paint>[], Paint[]> { 84 85 public static SequenceConverter getInstance() { 86 return Holder.SEQUENCE_INSTANCE; 87 } 88 89 private SequenceConverter() { 90 super(); 91 } 92 93 @Override 94 public Paint[] convert(ParsedValue<ParsedValue<?, Paint>[], Paint[]> value, Font font) { 95 ParsedValue<?, Paint>[] values = value.getValue(); 96 Paint[] paints = new Paint[values.length]; 97 for (int p = 0; p < values.length; p++) { 98 paints[p] = values[p].convert(font); 99 } 100 return paints; 101 } 102 103 @Override 104 public String toString() { 105 return "Paint.SequenceConverter"; 106 } 107 } 108 109 public static final class LinearGradientConverter extends StyleConverter<ParsedValue[], Paint> { 110 111 public static LinearGradientConverter getInstance() { 112 return Holder.LINEAR_GRADIENT_INSTANCE; 113 } 114 115 private LinearGradientConverter() { 116 super(); 117 } 118 119 @Override 120 public Paint convert(ParsedValue<ParsedValue[], Paint> value, Font font) { 121 122 Paint paint = super.getCachedValue(value); 123 if (paint != null) return paint; 124 125 ParsedValue[] values = value.getValue(); 126 int v = 0; 127 final Size startX = (Size) values[v++].convert(font); 128 final Size startY = (Size) values[v++].convert(font); 129 final Size endX = (Size) values[v++].convert(font); 130 final Size endY = (Size) values[v++].convert(font); 131 boolean proportional = startX.getUnits() == SizeUnits.PERCENT && startX.getUnits() == startY.getUnits() && startX.getUnits() == endX.getUnits() && startX.getUnits() == endY.getUnits(); 132 final CycleMethod cycleMethod = (CycleMethod) values[v++].convert(font); 133 final Stop[] stops = new Stop[values.length - v]; 134 for (int s = v; s < values.length; s++) { 135 stops[s - v] = (Stop) values[s].convert(font); 136 } 137 paint = new LinearGradient(startX.pixels(font), startY.pixels(font), endX.pixels(font), endY.pixels(font), proportional, cycleMethod, stops); 138 139 super.cacheValue(value, paint); 140 return paint; 141 } 142 143 @Override 144 public String toString() { 145 return "LinearGradientConverter"; 146 } 147 } 148 149 public static final class ImagePatternConverter extends StyleConverter<ParsedValue[], Paint> { 150 151 public static ImagePatternConverter getInstance() { 152 return Holder.IMAGE_PATTERN_INSTANCE; 153 } 154 155 private ImagePatternConverter() { 156 super(); 157 } 158 159 @Override 160 public Paint convert(ParsedValue<ParsedValue[], Paint> value, Font font) { 161 162 Paint paint = super.getCachedValue(value); 163 if (paint != null) return paint; 164 165 ParsedValue[] values = value.getValue(); 166 ParsedValue<?,?> urlParsedValue = values[0]; 167 String url = (String) urlParsedValue.convert(font); 168 if (values.length == 1) { 169 return new ImagePattern(StyleManager.getInstance().getCachedImage(url)); 170 } 171 172 Size x = (Size) values[1].convert(font); 173 Size y = (Size) values[2].convert(font); 174 Size w = (Size) values[3].convert(font); 175 Size h = (Size) values[4].convert(font); 176 boolean p = values.length < 6 ? true : (Boolean) values[5].getValue(); 177 178 paint = new ImagePattern( 179 new Image(url), 180 x.getValue(), 181 y.getValue(), 182 w.getValue(), 183 h.getValue(), p); 184 185 super.cacheValue(value, paint); 186 return paint; 187 } 188 189 @Override 190 public String toString() { 191 return "ImagePatternConverter"; 192 } 193 } 194 195 public static final class RepeatingImagePatternConverter extends StyleConverter<ParsedValue[], Paint> { 196 197 public static RepeatingImagePatternConverter getInstance() { 198 return Holder.REPEATING_IMAGE_PATTERN_INSTANCE; 199 } 200 201 private RepeatingImagePatternConverter() { 202 super(); 203 } 204 205 @Override 206 public Paint convert(ParsedValue<ParsedValue[], Paint> value, Font font) { 207 208 Paint paint = super.getCachedValue(value); 209 if (paint != null) return paint; 210 211 ParsedValue[] values = value.getValue(); 212 ParsedValue<?, ?> url = values[0]; 213 String u = (String) url.convert(font); 214 // If u is null, then we failed to locate the image associated with the url specified in the CSS file. 215 if (u == null) return null; 216 final Image image = new Image(u); 217 paint = new ImagePattern(image, 0, 0, image.getWidth(), image.getHeight(), false); 218 219 super.cacheValue(value, paint); 220 return paint; 221 } 222 223 @Override 224 public String toString() { 225 return "RepeatingImagePatternConverter"; 226 } 227 } 228 229 public static final class RadialGradientConverter extends StyleConverter<ParsedValue[], Paint> { 230 231 public static RadialGradientConverter getInstance() { 232 return Holder.RADIAL_GRADIENT_INSTANCE; 233 } 234 235 private RadialGradientConverter() { 236 super(); 237 } 238 239 @Override 240 public Paint convert(ParsedValue<ParsedValue[], Paint> value, Font font) { 241 242 Paint paint = super.getCachedValue(value); 243 if (paint != null) return paint; 244 245 final ParsedValue[] values = value.getValue(); 246 int v = 0; 247 // First four values are for startX, startY, endX, endY 248 // and are type ParsedValue<Value<?,Size>,Double>. To figure out 249 // proportional, we need to get to the Size. getValue() will 250 // return ParsedValue<?,Size>, so getValue().convert(font) will 251 // give us the size. 252 final Size focusAngle = values[v++] != null ? (Size) values[v-1].convert(font) : null; 253 final Size focusDistance = values[v++] != null ? (Size) values[v-1].convert(font) : null; 254 final Size centerX = values[v++] != null ? (Size) values[v-1].convert(font) : null; 255 final Size centerY = values[v++] != null ? (Size) values[v-1].convert(font) : null; 256 final Size radius = (Size) values[v++].convert(font); 257 boolean proportional = radius.getUnits().equals(SizeUnits.PERCENT); 258 boolean unitsAgree = centerX != null ? proportional == centerX.getUnits().equals(SizeUnits.PERCENT) : true; 259 unitsAgree = unitsAgree && centerY != null ? proportional == centerY.getUnits().equals(SizeUnits.PERCENT) : true; 260 if (!unitsAgree) { 261 throw new IllegalArgumentException("units do not agree"); 262 } 263 final CycleMethod cycleMethod = (CycleMethod) values[v++].convert(font); 264 final Stop[] stops = new Stop[values.length - v]; 265 for (int s = v; s < values.length; s++) { 266 stops[s - v] = (Stop) values[s].convert(font); 267 } 268 //If the focus-angle is a percentage, the value is mutiplied 269 // by 360, modulo 360. 270 double fa = 0; 271 if (focusAngle != null) { 272 fa = focusAngle.pixels(font); 273 if (focusAngle.getUnits().equals(SizeUnits.PERCENT)) { 274 fa = (fa * 360) % 360; 275 } 276 } 277 paint = new RadialGradient(fa, focusDistance != null ? focusDistance.pixels() : 0, centerX != null ? centerX.pixels() : 0, centerY != null ? centerY.pixels() : 0, radius != null ? radius.pixels() : 1, proportional, cycleMethod, stops); 278 279 super.cacheValue(value, paint); 280 return paint; 281 } 282 283 @Override 284 public String toString() { 285 return "RadialGradientConverter"; 286 } 287 } 288 }