001 /* 002 * Crop 003 * 004 * Copyright (c) 2001, 2002, 2003 Marco Schmidt. 005 * All rights reserved. 006 */ 007 008 package net.sourceforge.jiu.geometry; 009 010 import net.sourceforge.jiu.data.PixelImage; 011 import net.sourceforge.jiu.data.IntegerImage; 012 import net.sourceforge.jiu.ops.ImageToImageOperation; 013 import net.sourceforge.jiu.ops.MissingParameterException; 014 import net.sourceforge.jiu.ops.WrongParameterException; 015 016 /** 017 * Copies a rectangular area of one image to another image that is exactly as large 018 * as that rectangular area. 019 * Works with all image data classes implementing {@link net.sourceforge.jiu.data.IntegerImage}. 020 * <em>Make sure to use zero-based parameters when defining the bounds with 021 * {@link #setBounds}!</em> 022 * <h3>Usage example</h3> 023 * In this example we assume that the input image is larger than 20 pixels in both directions. 024 * Ten pixels will be removed from any of its four borders. 025 * <pre> 026 * PixelImage image = ...; // something implementing IntegerImage 027 * Crop crop = new Crop(); 028 * crop.setInputImage(image); 029 * crop.setBounds(10, 10, image.getWidth() - 9, image.getHeight() - 9); 030 * crop.process(); 031 * PixelImage croppedImage = crop.getOutputImage(); 032 * </pre> 033 * @author Marco Schmidt 034 */ 035 public class Crop extends ImageToImageOperation 036 { 037 private int x1; 038 private int y1; 039 private int x2; 040 private int y2; 041 042 private void checkBounds() throws WrongParameterException 043 { 044 PixelImage in = getInputImage(); 045 if (x1 >= in.getWidth()) 046 { 047 throw new WrongParameterException("x1 must be smaller than input image width."); 048 } 049 if (x2 >= in.getWidth()) 050 { 051 throw new WrongParameterException("x2 must be smaller than input image width."); 052 } 053 if (y1 >= in.getHeight()) 054 { 055 throw new WrongParameterException("y1 must be smaller than input image height."); 056 } 057 if (y2 >= in.getHeight()) 058 { 059 throw new WrongParameterException("y2 must be smaller than input image height."); 060 } 061 } 062 063 private void process(IntegerImage in, IntegerImage out) 064 { 065 final int OUT_WIDTH = x2 - x1 + 1; 066 final int OUT_HEIGHT = y2 - y1 + 1; 067 if (out == null) 068 { 069 out = (IntegerImage)in.createCompatibleImage(OUT_WIDTH, OUT_HEIGHT); 070 setOutputImage(out); 071 } 072 int totalItems = in.getNumChannels() * OUT_HEIGHT; 073 int processedItems = 0; 074 for (int c = 0; c < in.getNumChannels(); c++) 075 { 076 for (int yfrom = y1, yto = 0; yto < OUT_HEIGHT; yfrom++, yto++) 077 { 078 for (int xfrom = x1, xto = 0; xto < OUT_WIDTH; xfrom++, xto++) 079 { 080 out.putSample(c, xto, yto, in.getSample(c, xfrom, yfrom)); 081 } 082 setProgress(processedItems++, totalItems); 083 } 084 } 085 } 086 087 public void process() throws 088 MissingParameterException, 089 WrongParameterException 090 { 091 ensureInputImageIsAvailable(); 092 checkBounds(); 093 ensureOutputImageResolution(x2 - x1 + 1, y2 - y1 + 1); 094 if (getInputImage() instanceof IntegerImage) 095 { 096 process((IntegerImage)getInputImage(), (IntegerImage)getOutputImage()); 097 } 098 else 099 { 100 throw new WrongParameterException("Input image must implement IntegerImage."); 101 } 102 } 103 104 /** 105 * Specify the rectangular section of the original image that is to be 106 * copied to the output image by this operation. 107 * Note that the arguments are not checked directly against any input image that may have 108 * been provided to this Crop object, that checking is done later in {@link #process()}. 109 * If any of the arguments provided here are outside of the input image's resolution 110 * (e.g. x1 == 100 although the input image's width is only 60), a 111 * {@link net.sourceforge.jiu.ops.WrongParameterException} will be thrown from 112 * within {@link #process()}. 113 * <p> 114 * Note that the arguments to this method are zero-based, so the first column and row 115 * are 0, the second 1, the third 2, and so on. 116 * If you have a image that is 200 pixels wide and 100 pixels high, 117 * values from 0 to 199 are valid for the x arguments, and values from 0 to 99 are valid 118 * for the vertical direction. 119 * @param x1 horizontal position of upper left corner of the rectangle 120 * @param y1 vertical position of upper left corner of the rectangle 121 * @param x2 horizontal position of lower right corner of the rectangle 122 * @param y2 vertical position of lower right corner of the rectangle 123 * @throws IllegalArgumentException if any of the arguments is negative or x1 larger than x2 or y1 larger than y2 124 */ 125 public void setBounds(int x1, int y1, int x2, int y2) throws IllegalArgumentException 126 { 127 if (x1 < 0) 128 { 129 throw new IllegalArgumentException("x1 must not be negative."); 130 } 131 if (y1 < 0) 132 { 133 throw new IllegalArgumentException("y1 must not be negative."); 134 } 135 if (x2 < 0) 136 { 137 throw new IllegalArgumentException("x2 must not be negative."); 138 } 139 if (y2 < 0) 140 { 141 throw new IllegalArgumentException("y2 must not be negative."); 142 } 143 if (x1 > x2) 144 { 145 throw new IllegalArgumentException("x1 must not be larger than x2."); 146 } 147 if (y1 > y2) 148 { 149 throw new IllegalArgumentException("y1 must not be larger than y2."); 150 } 151 this.x1 = x1; 152 this.y1 = y1; 153 this.x2 = x2; 154 this.y2 = y2; 155 } 156 }