Web implementation of the Gourmet Recipe Manager
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

286 lines
7.3 KiB

/**
* Copyright (C) 2021, Tim Holloway
*
* Date written: Nov 26, 2021
* Author: Tim Holloway <timh@mousetech.com>
*/
package com.mousetech.gourmetj.springweb;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.mousetech.gourmetj.UserSession;
import com.mousetech.gourmetj.persistence.model.Recipe;
import com.mousetech.gourmetj.persistence.service.RecipeService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
/**
* @author timh
* @since Nov 26, 2021
*/
@Controller
@RequestMapping(path="/img")
public class PictureController {
/* Logger */
private static final Logger log =
LoggerFactory.getLogger(PictureController.class);
@Autowired
private RecipeService recipeService;
/**
* @return the recipeService
*/
public RecipeService getRecipeService() {
return recipeService;
}
/**
* @param recipeService the recipeService to set
*/
public void setRecipeService(RecipeService recipeService) {
this.recipeService = recipeService;
}
// ***
/**
* Default image returned if a recipe has no actual image.
* It's thumbnail-sized, so does double duty.
*/
static byte[] BLANK_IMAGE;
public PictureController() {
init();
}
/**
* General setup. Constructs BLANK_IMAGE.
*/
private void init() {
BufferedImage tbi = new BufferedImage(40, 40,
BufferedImage.TYPE_INT_ARGB);
Graphics2D ig2 = tbi.createGraphics();
ig2.setBackground(Color.WHITE);
ig2.clearRect(0, 0, 40, 40);
try {
ByteArrayOutputStream os =
new ByteArrayOutputStream(946);
ImageIO.write(tbi, "png", os);
BLANK_IMAGE = os.toByteArray();
} catch (IOException e) {
log.error("Unable to create BLANK_IMAGE", e);
}
}
// ==========================================
@GetMapping(value = "/picture/{recipeId}")
@PostMapping(value = "/picture/{recipeId}")
public ResponseEntity<byte[]> bigPicture(
@PathVariable long recipeId) {
Recipe recipe = recipeService.findByPrimaryKey(recipeId);
if (recipe != null) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set(HttpHeaders.CONTENT_TYPE,
MediaType.IMAGE_PNG_VALUE);
httpHeaders.set(HttpHeaders.CACHE_CONTROL,
"no-cache");
byte[] image = recipe.getImage();
if (image == null) {
image = BLANK_IMAGE;
}
return ResponseEntity.ok().headers(httpHeaders)
.body(image);
}
// NotFound, need dummy picture!:
return ResponseEntity.status(404).body(new byte[0]);
}
/**
* Dummy picture request. Made when recipeId is not yet
* assigned.
*
* @param req The HttpServletRequest from Spring.
* @return Image response.
*/
@GetMapping(value = "/picture/")
public ResponseEntity<byte[]> bigPicture(
HttpServletRequest req) {
final HttpSession httpSession = req.getSession(false);
final UserSession userSession = (UserSession) httpSession
.getAttribute("userSession");
Recipe editRecipe = userSession.getRecipe();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set(HttpHeaders.CONTENT_TYPE,
MediaType.IMAGE_PNG_VALUE);
httpHeaders.set(HttpHeaders.CACHE_CONTROL, "no-cache");
byte[] image = null;
if ( editRecipe != null ) {
// Otherwise we are creating new recipe, pre-display
image = editRecipe.getImage();
}
if (image == null) {
image = BLANK_IMAGE;
}
return ResponseEntity.ok().headers(httpHeaders)
.body(image);
}
@GetMapping(value = "/thumb/{recipeId}")
public ResponseEntity<byte[]> thumbnail(
@PathVariable long recipeId) {
Recipe recipe = recipeService.findByPrimaryKey(recipeId);
if (recipe != null) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set(HttpHeaders.CONTENT_TYPE,
MediaType.IMAGE_PNG_VALUE);
byte[] image = recipe.getThumb();
// Empty thumbs have been seen with "H"!
if (image == null || image.length < 2) {
image = BLANK_IMAGE;
}
return ResponseEntity.ok().headers(httpHeaders)
.body(image);
}
// NotFound, need dummy picture!:
return ResponseEntity.status(404).body(new byte[0]);
}
/**
* Take imageFile input and store it as image for recipe.
* Generate thumbnail
*
* @param recipe Recipe to store into.
* @param bs Info about uploaded data.
*
* CalledFrom @see
* RecipeDetailBean#ajaxUploadImage(AjaxBehaviorEvent
* event)
*/
public static void importImage(Recipe recipe,
byte[] bs) {
try {
byte[] bytes = null;
InputStream istream =new ByteArrayInputStream(bs);
ImageInputStream stream =
ImageIO.createImageInputStream(istream);
BufferedImage bi = ImageIO.read(stream);
if ((bi.getWidth() > 512) || (bi.getWidth() > 512)) {
bi = scaleImageSanely(bi);
}
ByteArrayOutputStream baos =
new ByteArrayOutputStream();
ImageIO.write(bi, "png", baos);
bytes = baos.toByteArray();
recipe.setImage(bytes);
final int thumbHeight = 40;
float ratio = ((float) bi.getHeight()) / thumbHeight;
int thumbWidth = (int) (bi.getWidth() / ratio);
BufferedImage resizedImg =
new BufferedImage(thumbWidth, thumbHeight,
BufferedImage.TRANSLUCENT);
// Create a device-independent object to draw the
// resized image
Graphics2D g2 = resizedImg.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
// Finally draw the source image in the Graphics2D
// with the desired size.
g2.drawImage(bi, 0, 0, thumbWidth, thumbHeight,
null);
// Disposes of this graphics context and releases any
// system resources that it is using
g2.dispose();
baos = new ByteArrayOutputStream();
ImageIO.write(resizedImg, "png", baos);
bytes = baos.toByteArray();
recipe.setThumb(bytes);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Reduce image so that width or height fit.
*
* @param bi
*
* @return
*/
private static BufferedImage scaleImageSanely(
BufferedImage bi) {
int w0 = bi.getWidth();
int h0 = bi.getHeight();
int w1 = w0;
int h1 = h0;
double ratio = 1.0;
if (h0 > 512) {
h1 = 512;
ratio = ((double) h0) / ((double) h1);
w1 = (int) (w0 / ratio);
}
if (w1 > 512) {
w0 = w1;
w1 = 512;
ratio = ((double) w0) / ((double) w1);
w1 = (int) (w0 / ratio);
}
BufferedImage resizedImg = new BufferedImage(w1, h1,
BufferedImage.TRANSLUCENT);
Graphics2D g2 = resizedImg.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(bi, 0, 0, w1, h1, null);
g2.dispose();
return resizedImg;
}
}