@@ -123,21 +123,17 @@ public GradientSpreadMethod getSpreadMethod() {
123
123
* the current space. The {@code null} value is valid and can be used
124
124
* if there is no transformation from base coordinates to current space
125
125
* specified, or it is equal to identity transformation.
126
- * @return the constructed {@link Color}
126
+ * @return the constructed {@link Color} or {@code null} if no color to be applied
127
+ * or base gradient vector has been specified
127
128
*/
128
129
public Color buildColor (Rectangle targetBoundingBox , AffineTransform contextTransform ) {
129
- Point [] coordinates = getGradientVector (targetBoundingBox , contextTransform );
130
- if (coordinates == null || this .stops .isEmpty ()) {
131
- // Can not create gradient color with 0 stops
130
+ Point [] baseCoordinatesVector = getGradientVector (targetBoundingBox , contextTransform );
131
+ if (baseCoordinatesVector == null || this .stops .isEmpty ()) {
132
+ // Can not create gradient color with 0 stops or null coordinates vector
132
133
return null ;
133
- } else if (this .stops .size () == 1 || coordinates [0 ].equals (coordinates [1 ])) {
134
- // single stop and zero vector case
135
- float [] lastStopRgb = this .stops .get (this .stops .size () - 1 ).getRgbArray ();
136
- return new DeviceRgb (lastStopRgb [0 ], lastStopRgb [1 ], lastStopRgb [2 ]);
137
134
}
138
135
139
136
// evaluate actual coordinates and transformation
140
- Point [] baseCoordinatesVector = new Point [] {coordinates [0 ].getLocation (), coordinates [1 ].getLocation ()};
141
137
AffineTransform shadingTransform = new AffineTransform ();
142
138
if (contextTransform != null ) {
143
139
shadingTransform .concatenate (contextTransform );
@@ -167,6 +163,9 @@ public Color buildColor(Rectangle targetBoundingBox, AffineTransform contextTran
167
163
168
164
PdfShading .Axial axial = createAxialShading (baseCoordinatesVector , this .stops , this .spreadMethod ,
169
165
targetBoundingBox );
166
+ if (axial == null ) {
167
+ return null ;
168
+ }
170
169
171
170
PdfPattern .Shading shading = new PdfPattern .Shading (axial );
172
171
if (!shadingTransform .isIdentity ()) {
@@ -271,91 +270,61 @@ protected static Point[] createCoordinatesForNewDomain(double[] newDomain, Point
271
270
272
271
private static PdfShading .Axial createAxialShading (Point [] baseCoordinatesVector ,
273
272
List <GradientColorStop > stops , GradientSpreadMethod spreadMethod , Rectangle targetBoundingBox ) {
274
- List < GradientColorStop > stopsToConstruct = normalizeStops ( stops , baseCoordinatesVector );
273
+ double baseVectorLength = baseCoordinatesVector [ 1 ]. distance ( baseCoordinatesVector [ 0 ] );
275
274
275
+ List <GradientColorStop > stopsToConstruct = normalizeStops (stops , baseVectorLength );
276
276
double [] coordinatesDomain = new double [] {0 , 1 };
277
- if (spreadMethod == GradientSpreadMethod .REPEAT || spreadMethod == GradientSpreadMethod .REFLECT ) {
278
- coordinatesDomain = evaluateCoveringDomain (baseCoordinatesVector , targetBoundingBox );
279
- stopsToConstruct = adjustNormalizedStopsToCoverDomain (stopsToConstruct , coordinatesDomain , spreadMethod );
277
+ Point [] actualCoordinates ;
278
+ if (baseVectorLength < ZERO_EPSILON || stopsToConstruct .size () == 1 ) {
279
+ // single color case
280
+ if (spreadMethod == GradientSpreadMethod .NONE ) {
281
+ return null ;
282
+ }
283
+ actualCoordinates = new Point []{new Point (targetBoundingBox .getLeft (), targetBoundingBox .getBottom ()),
284
+ new Point (targetBoundingBox .getRight (), targetBoundingBox .getBottom ())};
285
+
286
+ GradientColorStop lastColorStop = stopsToConstruct .get (stopsToConstruct .size () - 1 );
287
+ stopsToConstruct = Arrays .asList (new GradientColorStop (lastColorStop , 0d , OffsetType .RELATIVE ),
288
+ new GradientColorStop (lastColorStop , 1d , OffsetType .RELATIVE ));
280
289
} else {
281
- // to ensure that stops list covers the full domain. For repeat and reflect cases
282
- // this is done by adjusting the initial stops to cover the evaluated domain
283
- coordinatesDomain [0 ] = Math .max (coordinatesDomain [0 ], stopsToConstruct .get (0 ).getOffset ());
284
- coordinatesDomain [1 ] = Math .min (coordinatesDomain [1 ],
285
- stopsToConstruct .get (stopsToConstruct .size () - 1 ).getOffset ());
286
- coordinatesDomain [1 ] = Math .max (coordinatesDomain [0 ], coordinatesDomain [1 ]);
287
- }
290
+ coordinatesDomain = evaluateCoveringDomain (baseCoordinatesVector , targetBoundingBox );
291
+ if (spreadMethod == GradientSpreadMethod .REPEAT || spreadMethod == GradientSpreadMethod .REFLECT ) {
292
+ stopsToConstruct = adjustNormalizedStopsToCoverDomain (stopsToConstruct , coordinatesDomain ,
293
+ spreadMethod );
294
+ } else if (spreadMethod == GradientSpreadMethod .PAD ) {
295
+ adjustStopsForPadIfNeeded (stopsToConstruct , coordinatesDomain );
296
+ } else {
297
+ // none case
298
+ double firstStopOffset = stopsToConstruct .get (0 ).getOffset ();
299
+ double lastStopOffset = stopsToConstruct .get (stopsToConstruct .size () - 1 ).getOffset ();
300
+ if ((lastStopOffset - firstStopOffset < ZERO_EPSILON )
301
+ || coordinatesDomain [1 ] <= firstStopOffset
302
+ || coordinatesDomain [0 ] >= lastStopOffset ) {
303
+ return null ;
304
+ }
305
+ coordinatesDomain [0 ] = Math .max (coordinatesDomain [0 ], firstStopOffset );
306
+ coordinatesDomain [1 ] = Math .min (coordinatesDomain [1 ], lastStopOffset );
307
+ }
308
+ assert coordinatesDomain [0 ] <= coordinatesDomain [1 ];
288
309
289
- // workaround for PAD case
290
- if (spreadMethod == GradientSpreadMethod .PAD ) {
291
- coordinatesDomain = modifyNormalizedStopsForPad (stopsToConstruct , coordinatesDomain );
310
+ actualCoordinates = createCoordinatesForNewDomain (coordinatesDomain , baseCoordinatesVector );
292
311
}
293
312
294
- assert coordinatesDomain [0 ] <= coordinatesDomain [1 ];
295
-
296
- Point [] actualCoordinates = createCoordinatesForNewDomain (coordinatesDomain , baseCoordinatesVector );
297
-
298
- PdfShading .Axial axial = new PdfShading .Axial (
313
+ return new PdfShading .Axial (
299
314
new PdfDeviceCs .Rgb (),
300
315
createCoordsPdfArray (actualCoordinates ),
301
316
new PdfArray (coordinatesDomain ),
302
317
constructFunction (stopsToConstruct )
303
318
);
304
- // apply extended flag for PAD case
305
- if (spreadMethod == GradientSpreadMethod .PAD ) {
306
- axial .setExtend (true , true );
307
- }
308
- return axial ;
309
- }
310
-
311
- private static double [] modifyNormalizedStopsForPad (List <GradientColorStop > stopsToConstruct ,
312
- double [] coordinatesDomain ) {
313
- double [] newDomain = new double [] {coordinatesDomain [0 ], coordinatesDomain [1 ]};
314
- double eps = Math .max (1 , (coordinatesDomain [1 ] - coordinatesDomain [0 ])) * 0.05 ;
315
-
316
- for (int i = stopsToConstruct .size () - 1 ; i > 0 ; --i ) {
317
- GradientColorStop currentStop = stopsToConstruct .get (i );
318
- double currentStopOffset = currentStop .getOffset ();
319
- if (Math .abs (currentStopOffset - coordinatesDomain [1 ]) < eps ) {
320
- GradientColorStop prevStop = stopsToConstruct .get (i - 1 );
321
- if ((prevStop .getHintOffsetType () == HintOffsetType .RELATIVE_BETWEEN_COLORS
322
- && prevStop .getHintOffset () >= 1d - ZERO_EPSILON )
323
- || (prevStop .getOffset () > currentStopOffset - eps )) {
324
- double lastOffset = currentStopOffset + eps ;
325
- stopsToConstruct .add (i + 1 , new GradientColorStop (currentStop , lastOffset , OffsetType .RELATIVE ));
326
- newDomain [1 ] = lastOffset ;
327
- }
328
- break ;
329
- }
330
- if (currentStopOffset < coordinatesDomain [1 ]) {
331
- break ;
332
- }
333
- }
334
-
335
- for (int i = 0 ; i < stopsToConstruct .size () - 1 ; ++i ) {
336
- GradientColorStop currentStop = stopsToConstruct .get (i );
337
- double currentStopOffset = currentStop .getOffset ();
338
- if (Math .abs (currentStopOffset - coordinatesDomain [0 ]) < eps ) {
339
- if ((currentStop .getHintOffsetType () == HintOffsetType .RELATIVE_BETWEEN_COLORS
340
- && currentStop .getHintOffset () <= 0d + ZERO_EPSILON )
341
- || (stopsToConstruct .get (i + 1 ).getOffset () < currentStopOffset + eps )) {
342
- double firstOffset = currentStopOffset - eps ;
343
- stopsToConstruct .add (i , new GradientColorStop (currentStop , firstOffset , OffsetType .RELATIVE ));
344
- newDomain [0 ] = firstOffset ;
345
- }
346
- break ;
347
- }
348
- if (currentStopOffset > coordinatesDomain [0 ]) {
349
- break ;
350
- }
351
- }
352
- return newDomain ;
353
319
}
354
320
355
321
// the result list would have the same list of stop colors as the original one
356
322
// with all offsets on coordinates domain dimension and adjusted for ascending values
357
- private static List <GradientColorStop > normalizeStops (List <GradientColorStop > toNormalize , Point [] coordinates ) {
358
- double baseVectorLength = coordinates [1 ].distance (coordinates [0 ]);
323
+ private static List <GradientColorStop > normalizeStops (List <GradientColorStop > toNormalize , double baseVectorLength ) {
324
+ if (baseVectorLength < ZERO_EPSILON ) {
325
+ return Arrays .asList (new GradientColorStop (toNormalize .get (toNormalize .size () - 1 ),
326
+ 0d , OffsetType .RELATIVE ));
327
+ }
359
328
// get rid of all absolute on vector offsets and hint offsets
360
329
List <GradientColorStop > result = copyStopsAndNormalizeAbsoluteOffsets (toNormalize , baseVectorLength );
361
330
// normalize 1st stop as it may be a special case
@@ -502,6 +471,18 @@ private static List<GradientColorStop> copyStopsAndNormalizeAbsoluteOffsets(List
502
471
return copy ;
503
472
}
504
473
474
+ private static void adjustStopsForPadIfNeeded (List <GradientColorStop > stopsToConstruct ,
475
+ double [] coordinatesDomain ) {
476
+ GradientColorStop firstStop = stopsToConstruct .get (0 );
477
+ if (coordinatesDomain [0 ] < firstStop .getOffset ()) {
478
+ stopsToConstruct .add (0 , new GradientColorStop (firstStop , coordinatesDomain [0 ], OffsetType .RELATIVE ));
479
+ }
480
+ GradientColorStop lastStop = stopsToConstruct .get (stopsToConstruct .size () - 1 );
481
+ if (coordinatesDomain [1 ] > lastStop .getOffset ()) {
482
+ stopsToConstruct .add (new GradientColorStop (lastStop , coordinatesDomain [1 ], OffsetType .RELATIVE ));
483
+ }
484
+ }
485
+
505
486
private static List <GradientColorStop > adjustNormalizedStopsToCoverDomain (List <GradientColorStop > normalizedStops ,
506
487
double [] targetDomain , GradientSpreadMethod spreadMethod ) {
507
488
List <GradientColorStop > adjustedStops = new ArrayList <>();
0 commit comments