docs: public List<RasterTile> paint(CoordinateReferenceSystem targetCrs, Envelope bounds, double scale)
  			throws GeomajasException {
  		Envelope layerBounds = bounds;
  		double layerScale = scale;
  		CrsTransform layerToMap = null;
  		boolean needTransform = !crs.equals(targetCrs);
  
  		try {
  			// We don't necessarily need to split into same CRS and different CRS cases, the latter implementation uses
  			// identity transform if crs's are equal for map and layer but might introduce bugs in rounding and/or
  			// conversions.
  			if (needTransform) {
  				layerToMap = geoService.getCrsTransform(crs, targetCrs);
  				CrsTransform mapToLayer = geoService.getCrsTransform(targetCrs, crs);
  
  				// Translate the map coordinates to layer coordinates, assumes equal x-y orientation
  				layerBounds = geoService.transform(bounds, mapToLayer);
  				layerScale = bounds.getWidth() * scale / layerBounds.getWidth();
  			}
  		} catch (MismatchedDimensionException e) {
  			throw new GeomajasException(e, ExceptionCode.RENDER_DIMENSION_MISMATCH);
  		}
  		layerBounds = clipBounds(layerBounds);
  		if (layerBounds.isNull()) {
  			return new ArrayList<RasterTile>(0);
  		}
  
  		// Grid is in layer coordinate space!
  		Resolution bestResolution = getResolutionForScale(layerScale);
  		RasterGrid grid = getRasterGrid(layerBounds, bestResolution.getTileWidth(), bestResolution.getTileHeight());
  
  		// We calculate the first tile's screen box with this assumption
  		List<RasterTile> result = new ArrayList<RasterTile>();
  		for (int i = grid.getXmin(); i < grid.getXmax(); i++) {
  			for (int j = grid.getYmin(); j < grid.getYmax(); j++) {
  				double x = grid.getLowerLeft().x + (i - grid.getXmin()) * grid.getTileWidth();
  				double y = grid.getLowerLeft().y + (j - grid.getYmin()) * grid.getTileHeight();
  				// layer coordinates
  				Bbox worldBox;
  				Bbox layerBox;
  				if (needTransform) {
  					layerBox = new Bbox(x, y, grid.getTileWidth(), grid.getTileHeight());
  					// Transforming back to map coordinates will only result in a proper grid if the transformation
  					// is nearly affine
  					worldBox = geoService.transform(layerBox, layerToMap);
  				} else {
  					worldBox = new Bbox(x, y, grid.getTileWidth(), grid.getTileHeight());
  					layerBox = worldBox;
  				}
  				// Rounding to avoid white space between raster tiles lower-left becomes upper-left in inverted y-space
  				Bbox screenBox = new Bbox(Math.round(scale * worldBox.getX()), -Math.round(scale * worldBox.getMaxY()),
  						Math.round(scale * worldBox.getMaxX()) - Math.round(scale * worldBox.getX()), Math.round(scale
  								* worldBox.getMaxY())
  								- Math.round(scale * worldBox.getY()));
  
  				RasterTile image = new RasterTile(screenBox, getId() + "." + bestResolution.getLevel() + "." + i + ","
  						+ j);
  
  				image.setCode(new TileCode(bestResolution.getLevel(), i, j));
  				String url = formatUrl(bestResolution.getTileWidthPx(), bestResolution.getTileHeightPx(), layerBox);
  				image.setUrl(url);
  				result.add(image);
  			}
  		}
  
  		return result;
  	}