1 /*
   2  * Copyright (c) 2011, 2014, 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 com.sun.javafx.css.converters;
  27 
  28 import com.sun.javafx.css.Size;
  29 import com.sun.javafx.css.SizeUnits;
  30 import com.sun.javafx.css.StyleConverterImpl;
  31 import com.sun.javafx.css.StyleManager;
  32 import javafx.css.ParsedValue;
  33 import javafx.css.StyleConverter;
  34 import javafx.scene.image.Image;
  35 import javafx.scene.paint.CycleMethod;
  36 import javafx.scene.paint.ImagePattern;
  37 import javafx.scene.paint.LinearGradient;
  38 import javafx.scene.paint.Paint;
  39 import javafx.scene.paint.RadialGradient;
  40 import javafx.scene.paint.Stop;
  41 import javafx.scene.text.Font;
  42 
  43 
  44 public final class PaintConverter extends StyleConverterImpl<ParsedValue<?, Paint>, Paint> {
  45 
  46     // lazy, thread-safe instantiation
  47     private static class Holder {
  48         static final PaintConverter INSTANCE = new PaintConverter();
  49         static final SequenceConverter SEQUENCE_INSTANCE = new SequenceConverter();
  50         static final LinearGradientConverter LINEAR_GRADIENT_INSTANCE = new LinearGradientConverter();
  51         static final ImagePatternConverter IMAGE_PATTERN_INSTANCE = new ImagePatternConverter();
  52         static final RepeatingImagePatternConverter REPEATING_IMAGE_PATTERN_INSTANCE = new RepeatingImagePatternConverter();
  53         static final RadialGradientConverter RADIAL_GRADIENT_INSTANCE = new RadialGradientConverter();
  54     }
  55 
  56     public static StyleConverter<ParsedValue<?, Paint>, Paint> getInstance() {
  57         return Holder.INSTANCE;
  58     }
  59 
  60     private PaintConverter() {
  61         super();
  62     }
  63 
  64     @Override
  65     public Paint convert(ParsedValue<ParsedValue<?, Paint>, Paint> value, Font font) {
  66         Object obj = value.getValue();
  67         if (obj instanceof Paint) {
  68             return (Paint) obj;
  69         }
  70         return value.getValue().convert(font);
  71     }
  72 
  73     @Override
  74     public String toString() {
  75         return "PaintConverter";
  76     }
  77 
  78     /**
  79      * Convert [<paint]+ to Paint[]
  80      */
  81     public static final class SequenceConverter extends StyleConverterImpl<ParsedValue<?, Paint>[], Paint[]> {
  82 
  83         public static SequenceConverter getInstance() {
  84             return Holder.SEQUENCE_INSTANCE;
  85         }
  86 
  87         private SequenceConverter() {
  88             super();
  89         }
  90 
  91         @Override
  92         public Paint[] convert(ParsedValue<ParsedValue<?, Paint>[], Paint[]> value, Font font) {
  93             ParsedValue<?, Paint>[] values = value.getValue();
  94             Paint[] paints = new Paint[values.length];
  95             for (int p = 0; p < values.length; p++) {
  96                 paints[p] = values[p].convert(font);
  97             }
  98             return paints;
  99         }
 100 
 101         @Override
 102         public String toString() {
 103             return "Paint.SequenceConverter";
 104         }
 105     }
 106 
 107     public static final class LinearGradientConverter extends StyleConverterImpl<ParsedValue[], Paint> {
 108 
 109         public static LinearGradientConverter getInstance() {
 110             return Holder.LINEAR_GRADIENT_INSTANCE;
 111         }
 112 
 113         private LinearGradientConverter() {
 114             super();
 115         }
 116 
 117         @Override 
 118         public Paint convert(ParsedValue<ParsedValue[], Paint> value, Font font) {
 119 
 120             Paint paint = super.getCachedValue(value);
 121             if (paint != null) return paint;
 122 
 123             ParsedValue[] values = value.getValue();
 124             int v = 0;
 125             final Size startX = (Size) values[v++].convert(font);
 126             final Size startY = (Size) values[v++].convert(font);
 127             final Size endX = (Size) values[v++].convert(font);
 128             final Size endY = (Size) values[v++].convert(font);
 129             boolean proportional = startX.getUnits() == SizeUnits.PERCENT && startX.getUnits() == startY.getUnits() && startX.getUnits() == endX.getUnits() && startX.getUnits() == endY.getUnits();
 130             final CycleMethod cycleMethod = (CycleMethod) values[v++].convert(font);
 131             final Stop[] stops = new Stop[values.length - v];
 132             for (int s = v; s < values.length; s++) {
 133                 stops[s - v] = (Stop) values[s].convert(font);
 134             }
 135             paint = new LinearGradient(startX.pixels(font), startY.pixels(font), endX.pixels(font), endY.pixels(font), proportional, cycleMethod, stops);
 136 
 137             super.cacheValue(value, paint);
 138             return paint;
 139         }
 140 
 141         @Override
 142         public String toString() {
 143             return "LinearGradientConverter";
 144         }
 145     }
 146 
 147     public static final class ImagePatternConverter extends StyleConverterImpl<ParsedValue[], Paint> {
 148 
 149         public static ImagePatternConverter getInstance() {
 150             return Holder.IMAGE_PATTERN_INSTANCE;
 151         }
 152 
 153         private ImagePatternConverter() {
 154             super();
 155         }
 156 
 157         @Override
 158         public Paint convert(ParsedValue<ParsedValue[], Paint> value, Font font) {
 159 
 160             Paint paint = super.getCachedValue(value);
 161             if (paint != null) return paint;
 162 
 163             ParsedValue[] values = value.getValue();
 164             ParsedValue<?,?> urlParsedValue = values[0];
 165             String url = (String) urlParsedValue.convert(font);
 166             if (values.length == 1) {
 167                 return new ImagePattern(StyleManager.getInstance().getCachedImage(url));
 168             }
 169 
 170             Size x = (Size) values[1].convert(font);
 171             Size y = (Size) values[2].convert(font);
 172             Size w = (Size) values[3].convert(font);
 173             Size h = (Size) values[4].convert(font);
 174             boolean p = values.length < 6 ? true : (Boolean) values[5].getValue();
 175 
 176             paint = new ImagePattern(
 177                     new Image(url),
 178                     x.getValue(),
 179                     y.getValue(),
 180                     w.getValue(),
 181                     h.getValue(), p);
 182 
 183             super.cacheValue(value, paint);
 184             return paint;
 185         }
 186 
 187         @Override
 188         public String toString() {
 189             return "ImagePatternConverter";
 190         }
 191     }
 192 
 193     public static final class RepeatingImagePatternConverter extends StyleConverterImpl<ParsedValue[], Paint> {
 194 
 195         public static RepeatingImagePatternConverter getInstance() {
 196             return Holder.REPEATING_IMAGE_PATTERN_INSTANCE;
 197         }
 198 
 199         private RepeatingImagePatternConverter() {
 200             super();
 201         }
 202 
 203         @Override
 204         public Paint convert(ParsedValue<ParsedValue[], Paint> value, Font font) {
 205 
 206             Paint paint = super.getCachedValue(value);
 207             if (paint != null) return paint;
 208 
 209             ParsedValue[] values = value.getValue();
 210             ParsedValue<?, ?> url = values[0];
 211             String u = (String) url.convert(font);
 212             // If u is null, then we failed to locate the image associated with the url specified in the CSS file.
 213             if (u == null) return null;
 214             final Image image = new Image(u);
 215             paint = new ImagePattern(image, 0, 0, image.getWidth(), image.getHeight(), false);
 216 
 217             super.cacheValue(value, paint);
 218             return paint;
 219         }
 220 
 221         @Override
 222         public String toString() {
 223             return "RepeatingImagePatternConverter";
 224         }
 225     }
 226 
 227     public static final class RadialGradientConverter extends StyleConverterImpl<ParsedValue[], Paint> {
 228 
 229         public static RadialGradientConverter getInstance() {
 230             return Holder.RADIAL_GRADIENT_INSTANCE;
 231         }
 232 
 233         private RadialGradientConverter() {
 234             super();
 235         }
 236 
 237         @Override
 238         public Paint convert(ParsedValue<ParsedValue[], Paint> value, Font font) {
 239 
 240             Paint paint = super.getCachedValue(value);
 241             if (paint != null) return paint;
 242 
 243             final ParsedValue[] values = value.getValue();
 244             int v = 0;
 245             // First four values are for startX, startY, endX, endY
 246             // and are type ParsedValue<Value<?,Size>,Double>. To figure out
 247             // proportional, we need to get to the Size. getValue() will
 248             // return ParsedValue<?,Size>, so getValue().convert(font) will
 249             // give us the size.
 250             final Size focusAngle = values[v++] != null ? (Size) values[v-1].convert(font) : null;
 251             final Size focusDistance = values[v++] != null ? (Size) values[v-1].convert(font) : null;
 252             final Size centerX = values[v++] != null ? (Size) values[v-1].convert(font) : null;
 253             final Size centerY = values[v++] != null ? (Size) values[v-1].convert(font) : null;
 254             final Size radius = (Size) values[v++].convert(font);
 255             boolean proportional = radius.getUnits().equals(SizeUnits.PERCENT);
 256             boolean unitsAgree = centerX != null ? proportional == centerX.getUnits().equals(SizeUnits.PERCENT) : true;
 257             unitsAgree = unitsAgree && centerY != null ? proportional == centerY.getUnits().equals(SizeUnits.PERCENT) : true;
 258             if (!unitsAgree) {
 259                 throw new IllegalArgumentException("units do not agree");
 260             }
 261             final CycleMethod cycleMethod = (CycleMethod) values[v++].convert(font);
 262             final Stop[] stops = new Stop[values.length - v];
 263             for (int s = v; s < values.length; s++) {
 264                 stops[s - v] = (Stop) values[s].convert(font);
 265             }
 266             //If the focus-angle is a percentage, the value is mutiplied
 267             // by 360, modulo 360.
 268             double fa = 0;
 269             if (focusAngle != null) {
 270                 fa = focusAngle.pixels(font);
 271                 if (focusAngle.getUnits().equals(SizeUnits.PERCENT)) {
 272                     fa = (fa * 360) % 360;
 273                 }
 274             }
 275             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);
 276 
 277             super.cacheValue(value, paint);
 278             return paint;
 279         }
 280 
 281         @Override
 282         public String toString() {
 283             return "RadialGradientConverter";
 284         }
 285     }
 286 }