001/*******************************************************************************
002 * Copyright (c) 2024, 2026, Olivier Ayache.  All rights reserved.
003 *
004 * This file is part of AVPKit.
005 *
006 * AVPKit is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU Lesser General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * AVPKit is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public License
017 * along with AVPKit.  If not, see <http://www.gnu.org/licenses/>.
018 *******************************************************************************/
019
020package com.avpkit.core.video;
021
022import java.awt.image.BufferedImage;
023import com.avpkit.core.IVideoPicture;
024import com.avpkit.core.IVideoResampler;
025import com.avpkit.core.IPixelFormat;
026
027/** An abstract converter class from which specific converters can be
028 * derived to do the actual conversions.  This class establishes if
029 * the {@link IVideoPicture} needs to be re-sampled, and
030 * if so, creates appropriate {@link IVideoResampler} objects to do
031 * that.
032 */
033
034abstract public class AConverter implements IConverter
035{
036  /** Re-sampler called when converting image to picture, may be null. */
037
038  protected IVideoResampler mToPictureResampler = null;
039  
040  /** Re-sampler called when converting picture to image, may be null. */
041
042  protected IVideoResampler mToImageResampler = null;
043
044  /** The width of the pictures. */
045 
046  protected int mPictureWidth;
047
048  /** The height of the pictures. */
049
050  protected int mPictureHeight;
051
052  /** The width of the images. */
053
054  protected int mImageWidth;
055
056  /** The height of the images. */
057
058  protected int mImageHeight;
059
060  // the recognized BufferedImage type
061
062  final private IPixelFormat.Type mPictureType;
063
064  // the recognized BufferedImage type
065
066  final private IPixelFormat.Type mRequiredPictureType;
067
068  // the recognized BufferedImage type
069
070  final private int mImageType;
071
072  // the description of this convert
073
074  final private String mDescription;
075
076  /** 
077   * Construct an abstract Converter.  This will create a
078   * {@link IVideoResampler}
079   * to change color-space or resize the picture as needed for the
080   * conversions specified.
081   *
082   * @param pictureType the recognized {@link IVideoPicture} type
083   * @param requiredPictureType the picture type requred to translate to
084   *        and from the BufferedImage
085   * @param imageType the recognized {@link BufferedImage} type
086   * @param pictureWidth the width of picture
087   * @param pictureHeight the height of picture
088   * @param imageWidth the width of image
089   * @param imageHeight the height of image
090   */
091
092  public AConverter(
093    IPixelFormat.Type pictureType, 
094    IPixelFormat.Type requiredPictureType, 
095    int imageType,
096    int pictureWidth, 
097    int pictureHeight,
098    int imageWidth, 
099    int imageHeight)
100  {
101    // by default there is no resample description
102
103    String resampleDescription = "";
104
105    // record the image and picture parameters
106    
107    mPictureType = pictureType;
108    mRequiredPictureType = requiredPictureType;
109    mImageType = imageType;
110    mPictureWidth = pictureWidth;
111    mPictureHeight = pictureHeight;
112    mImageWidth = imageWidth;
113    mImageHeight = imageHeight;
114
115    // if the picture type is not the type or size required, create the
116    // resamplers to fix that
117
118    if (!pictureType.equals(requiredPictureType) 
119      || (mPictureWidth != mImageWidth)
120      || (mPictureHeight != mImageHeight))
121    {
122      if (!IVideoResampler.isSupported(
123          IVideoResampler.Feature.FEATURE_COLORSPACECONVERSION))
124        throw new RuntimeException(
125          "0 Could not create could resampler to translate from " + 
126          pictureType + " to " + requiredPictureType + ".  " + 
127          "Color space conversion is not supported by this version of" +
128          "AVPKit.  Recompile AVPKit with the GPL option enabled.");
129
130      mToImageResampler = IVideoResampler.make(
131        imageWidth, imageHeight, requiredPictureType,
132        mPictureWidth, mPictureHeight, pictureType);
133      
134      if (mToImageResampler == null)
135        throw new RuntimeException(
136          "1 Could not create could resampler to translate from " + 
137          pictureType + " to " + requiredPictureType + ".");
138
139      mToPictureResampler = IVideoResampler.make(
140        mPictureWidth, mPictureHeight, pictureType,
141        imageWidth, imageHeight, requiredPictureType);
142      
143      if (mToPictureResampler == null)
144        throw new RuntimeException(
145          "2 Could not create could resampler to translate from " + 
146          requiredPictureType + " to " + pictureType);
147
148      resampleDescription = "Pictures will be resampled to and from " + 
149        requiredPictureType + " during translation.";
150    }
151
152    // construct the description of this converter
153
154    mDescription = "A converter which translates [" +
155      pictureWidth + "x" + pictureHeight + "] IVideoPicture type " + 
156      pictureType + " to and from [" + imageWidth + "x" + imageHeight +
157      "] BufferedImage type " + imageType + ".  " + resampleDescription;
158  }
159
160  /** {@inheritDoc} */
161
162  public IPixelFormat.Type getPictureType()
163  {
164    return mPictureType;
165  }
166
167  /**
168   * Return the Type which matches the {@link
169   * BufferedImage} type.
170   * 
171   * @return the picture type which allows for image translation.
172   */
173
174  protected IPixelFormat.Type getRequiredPictureType()
175  {
176    return mRequiredPictureType;
177  }
178
179  /** {@inheritDoc} */
180
181  public int getImageType()
182  {
183    return mImageType;
184  }
185
186  /** {@inheritDoc} */
187
188  public boolean willResample()
189  {
190    return null != mToPictureResampler && null != mToImageResampler;
191  }
192
193  /** 
194   * Re-sample a picture.
195   * 
196   * @param picture1 the picture to re-sample
197   * @param resampler the picture re-samper to use
198   *
199   * @throws RuntimeException if could not re-sample picture
200   **/
201
202  protected static IVideoPicture resample(IVideoPicture picture1,
203    IVideoResampler resampler)
204  {
205    // create new picture object
206
207    IVideoPicture picture2 = IVideoPicture.make(
208      resampler.getOutputPixelFormat(),
209      resampler.getOutputWidth(),
210      resampler.getOutputHeight());
211
212    // resample
213
214    if (resampler.resample(picture2, picture1) < 0)
215      throw new RuntimeException(
216        "could not resample from " + resampler.getInputPixelFormat() +
217        " to " + resampler.getOutputPixelFormat() + 
218        " for picture of type " + picture1.getPixelType());
219
220    // test that it worked
221
222    if (picture2.getPixelType() != resampler.getOutputPixelFormat() 
223      || !picture2.isComplete())
224    {
225      throw new RuntimeException(
226        "did not resample from " + resampler.getInputPixelFormat() +
227        " to " + resampler.getOutputPixelFormat() +
228        " for picture of type " + picture1.getPixelType());
229    }
230
231    // return the resample picture
232
233    return picture2;
234  }
235
236  /** 
237   * Test that the passed image is valid and conforms to the
238   * converters specifications.
239   *
240   * @param image the image to test
241   *
242   * @throws IllegalArgumentException if the passed {@link
243   *         BufferedImage} is NULL;
244   * @throws IllegalArgumentException if the passed {@link
245   *         BufferedImage} is not the correct type. See {@link
246   *         #getImageType}.
247   */
248
249  protected void validateImage(BufferedImage image)
250  {
251    // if the image is NULL, throw up
252
253    if (image == null)
254      throw new IllegalArgumentException("The passed image is NULL.");
255
256    // if image is not the correct type, throw up
257
258    if (image.getType() != getImageType())
259      throw new IllegalArgumentException(
260        "The passed image is of type #" + image.getType() +
261        " but is required to be of BufferedImage type #" +
262        getImageType() + ".");
263  }
264
265  /** 
266   * Test that the passed picture is valid and conforms to the
267   * converters specifications.
268   *
269   * @param picture the picture to test
270   *
271   * @throws IllegalArgumentException if the passed {@link
272   *         IVideoPicture} is NULL;
273   * @throws IllegalArgumentException if the passed {@link
274   *         IVideoPicture} is not complete.
275   * @throws IllegalArgumentException if the passed {@link
276   *         IVideoPicture} is not the correct type.
277   */
278
279  protected void validatePicture(IVideoPicture picture)
280  {
281    // if the pictre is NULL, throw up
282    
283    if (picture == null)
284      throw new IllegalArgumentException("The picture is NULL.");
285
286    // if the picture is not complete, throw up
287    
288    if (!picture.isComplete())
289      throw new IllegalArgumentException("The picture is not complete.");
290
291    // if the picture is an invalid type throw up
292
293    IPixelFormat.Type type = picture.getPixelType();
294    if ((type != getPictureType()) && (willResample() && 
295        type != mToImageResampler.getOutputPixelFormat()))
296      throw new IllegalArgumentException(
297        "Picture is of type: " + type + ", but must be " + 
298        getPictureType() + (willResample() 
299          ? " or " + mToImageResampler.getOutputPixelFormat()
300          : "") +
301        ".");
302  }
303
304  /** {@inheritDoc} */
305
306  public String getDescription()
307  {
308    return mDescription;
309  }
310
311  /** Get a string representation of this converter. */
312
313  public String toString()
314  {
315    return getDescription();
316  }
317
318  public void close()
319  {
320    if (mToPictureResampler != null)
321      mToPictureResampler.delete();
322    mToPictureResampler = null;
323    if (mToImageResampler != null)
324      mToImageResampler.delete();
325    mToImageResampler = null;
326  }
327
328}