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 }