<commit_before>/*
 * Copyright 2013 Odysseus Software GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.musicmount.builder.impl;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.imageio.ImageIO;

import org.imgscalr.Scalr;
import org.musicmount.builder.model.Album;
import org.musicmount.builder.model.Library;
import org.musicmount.builder.model.Track;

public class ImageFormatter {
	static final Logger LOGGER = Logger.getLogger(ImageFormatter.class.getName());

	private final AssetParser assetParser;
	private final boolean retina;
	
	public ImageFormatter(AssetParser assetParser, boolean retina) {
		this.assetParser = assetParser;
		this.retina = retina;
	}
	
	private void writeImage(BufferedImage image, ImageType type, File targetFile, boolean retina) throws IOException {
		Scalr.Method method = type == ImageType.Artwork ? Scalr.Method.QUALITY : Scalr.Method.ULTRA_QUALITY;
		int w = image.getWidth();
		int h = image.getHeight();
		int a = type.getMaxSize().width;
		if (retina) {
			a += a;
		}
		int b = type.getMaxSize().height;
		if (retina) {
			b += b;
		}
		double fx = (double)a / (double)w;
		double fy = (double)b / (double)h;
		BufferedImage scaledImage = null;
		switch (type) {
		case Artwork: // scale to width type.size.width, crop to height type.size.height
			w = a;
			h = (int)Math.round(fx * h);
			scaledImage = Scalr.resize(image, method, Scalr.Mode.FIT_EXACT, w, h);
			if (h > b) {
				BufferedImage tmpImage = scaledImage;
				scaledImage = Scalr.crop(tmpImage, w, h = b);
				tmpImage.flush();
			}
			break;
		case Tile: // scale to bounding box
			double f = Math.min(fx, fy);
			w = (int)Math.round(f * w);
			h = (int)Math.round(f * h);
			scaledImage = Scalr.resize(image, method, Scalr.Mode.FIT_EXACT, w, h);
			break;
		case Thumbnail: // scale and crop
			if (fy / fx < 1.0) { // (imageAspect / targetAspect < 1)  image is too tall -> scale to width, crop height
				h = (int)Math.round(fx * h);
				scaledImage = Scalr.resize(image, method, Scalr.Mode.FIT_EXACT, w = a, h);
				if (h > b) {
					int y = h < 1.8 * b ? (h - b) / 2 : 0; // select center or top part
					BufferedImage tmpImage = scaledImage;
					scaledImage = Scalr.crop(tmpImage, 0, y, w, h = b);
					tmpImage.flush();
				}
			} else { // image is to wide -> scale to height, crop width
				w = (int)Math.round(fy * w);
				scaledImage = Scalr.resize(image, method, Scalr.Mode.FIT_EXACT, w, h = b);
				if (w > a) {
					int x = w < 1.8 * a ? (w - a) / 2 : 0; // select center or left part
					BufferedImage tmpImage = scaledImage;
					scaledImage = Scalr.crop(tmpImage, x, 0, w = a, h);
					tmpImage.flush();
				}
			}
			break;
		}
		try {
	        ImageIO.write(scaledImage, type.getFileType(), targetFile);
		} finally {
			if (scaledImage != null) {
				scaledImage.flush();
			}
		}
	}

	private void formatImages(BufferedImage image, Map<ImageType, File> targets) {
		for (Map.Entry<ImageType, File> targetEntry : targets.entrySet()) {
			ImageType imageType = targetEntry.getKey();
			File imageFile = targetEntry.getValue();
			imageFile.getParentFile().mkdirs();
			try {
				writeImage(image, imageType, imageFile, retina);
			} catch (IOException e) {
				LOGGER.warning("Could not write image file: " + imageFile.getAbsolutePath());
			}
		}
	}

	private void formatAlbumImages(Album album, ResourceLocator resourceLocator, boolean overwrite) {
		Track track = album.representativeTrack();
		if (track.isArtworkAvailable()) {
			Map<ImageType, File> targets = new HashMap<ImageType, File>();
			for (ImageType type : ImageType.values()) {
				String imagePath = resourceLocator.getAlbumImagePath(album, type);
				if (imagePath != null) {
					File file = resourceLocator.getFile(imagePath);
					if (overwrite || !file.exists()) {
						targets.put(type, file);
					}
				}
			}
			if (!targets.isEmpty()) {
				if (LOGGER.isLoggable(Level.FINER)) {
					LOGGER.finer("Formatting images from assset: " + track.getAssetFile());
				}
		    	BufferedImage image;
		        try {
		        	image = assetParser.extractArtwork(track.getAssetFile());
		    		if (image.getTransparency() != Transparency.OPAQUE) {
		    			BufferedImage tmpImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);<commit_msg>delete image if write failed<commit_after>/*
 * Copyright 2013 Odysseus Software GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.musicmount.builder.impl;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.imageio.ImageIO;

import org.imgscalr.Scalr;
import org.musicmount.builder.model.Album;
import org.musicmount.builder.model.Library;
import org.musicmount.builder.model.Track;

public class ImageFormatter {
	static final Logger LOGGER = Logger.getLogger(ImageFormatter.class.getName());

	private final AssetParser assetParser;
	private final boolean retina;
	
	public ImageFormatter(AssetParser assetParser, boolean retina) {
		this.assetParser = assetParser;
		this.retina = retina;
	}
	
	private void writeImage(BufferedImage image, ImageType type, File targetFile, boolean retina) throws IOException {
		Scalr.Method method = type == ImageType.Artwork ? Scalr.Method.QUALITY : Scalr.Method.ULTRA_QUALITY;
		int w = image.getWidth();
		int h = image.getHeight();
		int a = type.getMaxSize().width;
		if (retina) {
			a += a;
		}
		int b = type.getMaxSize().height;
		if (retina) {
			b += b;
		}
		double fx = (double)a / (double)w;
		double fy = (double)b / (double)h;
		BufferedImage scaledImage = null;
		switch (type) {
		case Artwork: // scale to width type.size.width, crop to height type.size.height
			w = a;
			h = (int)Math.round(fx * h);
			scaledImage = Scalr.resize(image, method, Scalr.Mode.FIT_EXACT, w, h);
			if (h > b) {
				BufferedImage tmpImage = scaledImage;
				scaledImage = Scalr.crop(tmpImage, w, h = b);
				tmpImage.flush();
			}
			break;
		case Tile: // scale to bounding box
			double f = Math.min(fx, fy);
			w = (int)Math.round(f * w);
			h = (int)Math.round(f * h);
			scaledImage = Scalr.resize(image, method, Scalr.Mode.FIT_EXACT, w, h);
			break;
		case Thumbnail: // scale and crop
			if (fy / fx < 1.0) { // (imageAspect / targetAspect < 1)  image is too tall -> scale to width, crop height
				h = (int)Math.round(fx * h);
				scaledImage = Scalr.resize(image, method, Scalr.Mode.FIT_EXACT, w = a, h);
				if (h > b) {
					int y = h < 1.8 * b ? (h - b) / 2 : 0; // select center or top part
					BufferedImage tmpImage = scaledImage;
					scaledImage = Scalr.crop(tmpImage, 0, y, w, h = b);
					tmpImage.flush();
				}
			} else { // image is to wide -> scale to height, crop width
				w = (int)Math.round(fy * w);
				scaledImage = Scalr.resize(image, method, Scalr.Mode.FIT_EXACT, w, h = b);
				if (w > a) {
					int x = w < 1.8 * a ? (w - a) / 2 : 0; // select center or left part
					BufferedImage tmpImage = scaledImage;
					scaledImage = Scalr.crop(tmpImage, x, 0, w = a, h);
					tmpImage.flush();
				}
			}
			break;
		}
		try {
	        ImageIO.write(scaledImage, type.getFileType(), targetFile);
		} finally {
			if (scaledImage != null) {
				scaledImage.flush();
			}
		}
	}

	private void formatImages(BufferedImage image, Map<ImageType, File> targets) {
		for (Map.Entry<ImageType, File> targetEntry : targets.entrySet()) {
			ImageType imageType = targetEntry.getKey();
			File imageFile = targetEntry.getValue();
			imageFile.getParentFile().mkdirs();
			try {
				writeImage(image, imageType, imageFile, retina);
			} catch (IOException e) {
				LOGGER.warning("Could not write image file: " + imageFile.getAbsolutePath());
				if (imageFile.exists()) {
					imageFile.delete();
				}
			}
		}
	}

	private void formatAlbumImages(Album album, ResourceLocator resourceLocator, boolean overwrite) {
		Track track = album.representativeTrack();
		if (track.isArtworkAvailable()) {
			Map<ImageType, File> targets = new HashMap<ImageType, File>();
			for (ImageType type : ImageType.values()) {
				String imagePath = resourceLocator.getAlbumImagePath(album, type);
				if (imagePath != null) {
					File file = resourceLocator.getFile(imagePath);
					if (overwrite || !file.exists()) {
						targets.put(type, file);
					}
				}
			}
			if (!targets.isEmpty()) {
				if (LOGGER.isLoggable(Level.FINER)) {
					LOGGER.finer("Formatting images from assset: " + track.getAssetFile());
				}
		    	BufferedImage image;
		        try {
		        	image = assetParser.extractArtwork(track.getAssetFile());
		    		if (image.getTransparency() != Transparency.OPAQUE) {
		    			BufferedImage tmpImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
{'fraction_non_alphanumeric': 0.12380679648720885, 'fraction_numerical': 0.004200076365024819, 'mean_word_length': 3.4450572761985576, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3138512', 'n_tokens_mistral': 3893, 'n_tokens_neox': 3378, 'n_words': 942}