You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ki...@apache.org on 2022/01/16 02:03:02 UTC

[commons-imaging] 02/24: [IMAGING-159] Replace interface with base class, and more WIP of formats

This is an automated email from the ASF dual-hosted git repository.

kinow pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-imaging.git

commit 64234d40919b29b1ca14795c2653bc6f720dfa25
Author: Bruno P. Kinoshita <ki...@apache.org>
AuthorDate: Tue Feb 16 17:08:11 2021 +1300

    [IMAGING-159] Replace interface with base class, and more WIP of formats
---
 .../org/apache/commons/imaging/ImageParser.java    |  48 ++++----
 .../java/org/apache/commons/imaging/Imaging.java   | 121 +++++++++++----------
 .../apache/commons/imaging/ImagingConstants.java   |  19 ----
 .../apache/commons/imaging/ImagingParameters.java  |  86 ++++++++++++++-
 .../commons/imaging/common/BaseParameters.java     |  92 ----------------
 .../commons/imaging/common/XmpEmbeddable.java      |   5 +-
 .../XmpImagingParameters.java}                     |  22 +++-
 .../imaging/formats/bmp/BmpImageParser.java        |  66 ++---------
 .../bmp/BmpImagingParameters.java}                 |  10 +-
 .../imaging/formats/dcx/DcxImageParser.java        |  57 ++--------
 .../imaging/formats/gif/GifImageParser.java        |  38 ++-----
 .../gif/GifImagingParameters.java}                 |  12 +-
 .../imaging/formats/jpeg/iptc/IptcParser.java      |   4 +-
 .../formats/jpeg/iptc/JpegIptcRewriter.java        |   5 +-
 .../formats/jpeg/segments/App13Segment.java        |   4 +-
 .../commons/imaging/formats/pcx/PcxConstants.java  |   5 -
 .../imaging/formats/pcx/PcxImageParser.java        |  18 ++-
 .../imaging/formats/pcx/PcxImagingParameters.java  |  54 +++++++++
 .../commons/imaging/formats/pcx/PcxWriter.java     |  60 ++--------
 .../commons/imaging/formats/pnm/PamWriter.java     |   3 +-
 .../commons/imaging/formats/pnm/PbmWriter.java     |   3 +-
 .../commons/imaging/formats/pnm/PgmWriter.java     |   3 +-
 .../imaging/formats/pnm/PnmImageParser.java        |  48 ++------
 .../{PnmWriter.java => PnmImagingParameters.java}  |  34 ++++--
 .../commons/imaging/formats/pnm/PnmWriter.java     |   3 +-
 .../commons/imaging/formats/pnm/PpmWriter.java     |   3 +-
 .../imaging/formats/xpm/XpmImageParser.java        |  15 ++-
 .../XpmImagingParameters.java}                     |  20 ++--
 28 files changed, 363 insertions(+), 495 deletions(-)

diff --git a/src/main/java/org/apache/commons/imaging/ImageParser.java b/src/main/java/org/apache/commons/imaging/ImageParser.java
index fd653c7..4b4eed5 100644
--- a/src/main/java/org/apache/commons/imaging/ImageParser.java
+++ b/src/main/java/org/apache/commons/imaging/ImageParser.java
@@ -29,7 +29,6 @@ import java.util.Locale;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import org.apache.commons.imaging.common.BaseParameters;
 import org.apache.commons.imaging.common.BinaryFileParser;
 import org.apache.commons.imaging.common.BufferedImageFactory;
 import org.apache.commons.imaging.common.ImageMetadata;
@@ -82,13 +81,15 @@ import org.apache.commons.imaging.formats.xpm.XpmImageParser;
  * <h3>The "params" argument</h3>
  *
  * <p>Many of the methods specified by this class accept an argument of
- * type {@code BaseParameters} defining the parameters to be used when
+ * type {@code T} defining the parameters to be used when
  * processing an image. For example, some of the output formats permit
  * of different kinds of image compression or color models. Some of the
  * reading methods permit the calling application to require strict
  * format compliance.</p>
+ *
+ * @param <T> type of parameters used by this image parser
  */
-public abstract class ImageParser extends BinaryFileParser {
+public abstract class ImageParser<T extends ImagingParameters> extends BinaryFileParser {
 
     private static final Logger LOGGER = Logger.getLogger(ImageParser.class.getName());
 
@@ -97,7 +98,7 @@ public abstract class ImageParser extends BinaryFileParser {
      *
      * @return A valid array of image parsers
      */
-    public static ImageParser[] getAllImageParsers() {
+    public static ImageParser<?>[] getAllImageParsers() {
 
         return new ImageParser[]{
                 new BmpImageParser(),
@@ -164,7 +165,7 @@ public abstract class ImageParser extends BinaryFileParser {
      *                            implementation.
      * @throws IOException        In the event of unsuccessful data read operation.
      */
-    public abstract ImageMetadata getMetadata(ByteSource byteSource, BaseParameters params)
+    public abstract ImageMetadata getMetadata(ByteSource byteSource, T params)
             throws ImageReadException, IOException;
 
     /**
@@ -210,7 +211,7 @@ public abstract class ImageParser extends BinaryFileParser {
      *                            parser implementation.
      * @throws IOException        In the event of unsuccessful data read operation.
      */
-    public final ImageMetadata getMetadata(final byte[] bytes, final BaseParameters params)
+    public final ImageMetadata getMetadata(final byte[] bytes, final T params)
             throws ImageReadException, IOException {
         return getMetadata(new ByteSourceArray(bytes), params);
     }
@@ -259,7 +260,7 @@ public abstract class ImageParser extends BinaryFileParser {
      * @throws IOException        In the event of unsuccessful file read or
      *                            access operation.
      */
-    public final ImageMetadata getMetadata(final File file, final BaseParameters params)
+    public final ImageMetadata getMetadata(final File file, final T params)
             throws ImageReadException, IOException {
         if (LOGGER.isLoggable(Level.FINEST)) {
             LOGGER.finest(getName() + ".getMetadata" + ": " + file.getName());
@@ -296,7 +297,7 @@ public abstract class ImageParser extends BinaryFileParser {
      *                            parser implementation.
      * @throws IOException        In the event of unsuccessful data access operation.
      */
-    public abstract ImageInfo getImageInfo(ByteSource byteSource, BaseParameters params)
+    public abstract ImageInfo getImageInfo(ByteSource byteSource, T params)
             throws ImageReadException, IOException;
 
     /**
@@ -342,7 +343,7 @@ public abstract class ImageParser extends BinaryFileParser {
      * @throws IOException        In the event of unsuccessful data
      *                            access operation.
      */
-    public final ImageInfo getImageInfo(final byte[] bytes, final BaseParameters params)
+    public final ImageInfo getImageInfo(final byte[] bytes, final T params)
             throws ImageReadException, IOException {
         return getImageInfo(new ByteSourceArray(bytes), params);
     }
@@ -371,7 +372,7 @@ public abstract class ImageParser extends BinaryFileParser {
      * @throws IOException        In the event of unsuccessful file read or
      *                            access operation.
      */
-    public final ImageInfo getImageInfo(final File file, final BaseParameters params)
+    public final ImageInfo getImageInfo(final File file, final T params)
             throws ImageReadException, IOException {
         if (!canAcceptExtension(file)) {
             return null;
@@ -499,7 +500,7 @@ public abstract class ImageParser extends BinaryFileParser {
      *                            parser implementation.
      * @throws IOException        In the event of unsuccessful read or access operation.
      */
-    public abstract BufferedImage getBufferedImage(ByteSource byteSource, BaseParameters params)
+    public abstract BufferedImage getBufferedImage(ByteSource byteSource, T params)
             throws ImageReadException, IOException;
 
     /**
@@ -517,7 +518,7 @@ public abstract class ImageParser extends BinaryFileParser {
      *                            parser implementation.
      * @throws IOException        In the event of unsuccessful read or access operation.
      */
-    public final BufferedImage getBufferedImage(final byte[] bytes, final BaseParameters params)
+    public final BufferedImage getBufferedImage(final byte[] bytes, final T params)
             throws ImageReadException, IOException {
         return getBufferedImage(new ByteSourceArray(bytes), params);
     }
@@ -537,7 +538,7 @@ public abstract class ImageParser extends BinaryFileParser {
      *                            parser implementation.
      * @throws IOException        In the event of unsuccessful read or access operation.
      */
-    public final BufferedImage getBufferedImage(final File file, final BaseParameters params)
+    public final BufferedImage getBufferedImage(final File file, final T params)
             throws ImageReadException, IOException {
         if (!canAcceptExtension(file)) {
             return null;
@@ -565,7 +566,7 @@ public abstract class ImageParser extends BinaryFileParser {
      * @throws IOException         In the event of an write error from
      *                             the output stream.
      */
-    public void writeImage(final BufferedImage src, final OutputStream os, BaseParameters params)
+    public void writeImage(final BufferedImage src, final OutputStream os, T params)
             throws ImageWriteException, IOException {
         os.close(); // we are obligated to close stream.
 
@@ -599,7 +600,7 @@ public abstract class ImageParser extends BinaryFileParser {
      *                            parser implementation.
      * @throws IOException        In the event of unsuccessful read or access operation.
      */
-    public final Dimension getImageSize(final byte[] bytes, final BaseParameters params)
+    public final Dimension getImageSize(final byte[] bytes, final T params)
             throws ImageReadException, IOException {
         return getImageSize(new ByteSourceArray(bytes), params);
     }
@@ -630,7 +631,7 @@ public abstract class ImageParser extends BinaryFileParser {
      *                            parser implementation.
      * @throws IOException        In the event of unsuccessful read or access operation.
      */
-    public final Dimension getImageSize(final File file, final BaseParameters params)
+    public final Dimension getImageSize(final File file, final T params)
             throws ImageReadException, IOException {
 
         if (!canAcceptExtension(file)) {
@@ -652,7 +653,7 @@ public abstract class ImageParser extends BinaryFileParser {
      *                            parser implementation.
      * @throws IOException        In the event of unsuccessful read or access operation.
      */
-    public abstract Dimension getImageSize(ByteSource byteSource, BaseParameters params)
+    public abstract Dimension getImageSize(ByteSource byteSource, T params)
             throws ImageReadException, IOException;
 
     /**
@@ -685,7 +686,7 @@ public abstract class ImageParser extends BinaryFileParser {
      *                            parser implementation.
      * @throws IOException        In the event of unsuccessful read or access operation.
      */
-    public final byte[] getICCProfileBytes(final byte[] bytes, final BaseParameters params)
+    public final byte[] getICCProfileBytes(final byte[] bytes, final T params)
             throws ImageReadException, IOException {
         return getICCProfileBytes(new ByteSourceArray(bytes), params);
     }
@@ -720,7 +721,7 @@ public abstract class ImageParser extends BinaryFileParser {
      *                            parser implementation.
      * @throws IOException        In the event of unsuccessful read or access operation.
      */
-    public final byte[] getICCProfileBytes(final File file, final BaseParameters params)
+    public final byte[] getICCProfileBytes(final File file, final T params)
             throws ImageReadException, IOException {
         if (!canAcceptExtension(file)) {
             return null;
@@ -747,7 +748,7 @@ public abstract class ImageParser extends BinaryFileParser {
      *                            parser implementation.
      * @throws IOException        In the event of unsuccessful read or access operation.
      */
-    public abstract byte[] getICCProfileBytes(ByteSource byteSource, BaseParameters params)
+    public abstract byte[] getICCProfileBytes(ByteSource byteSource, T params)
             throws ImageReadException, IOException;
 
     /**
@@ -925,7 +926,7 @@ public abstract class ImageParser extends BinaryFileParser {
      * @param params optional parameters.
      * @return A valid instance of an implementation of a IBufferedImageFactory.
      */
-    protected BufferedImageFactory getBufferedImageFactory(final BaseParameters params) {
+    protected BufferedImageFactory getBufferedImageFactory(final T params) {
         if (params == null) {
             return new SimpleBufferedImageFactory();
         }
@@ -941,15 +942,14 @@ public abstract class ImageParser extends BinaryFileParser {
 
     /**
      * A utility method to search a params specification and determine
-     * whether it contains the ImagingConstants&#46;PARAM_KEY_STRICT
-     * specification. Intended
+     * whether it contains the parameters contain the strict flag. Intended
      * for internal use by ImageParser implementations.
      *
      * @param params optional parameters.
      * @return If the params specify strict format compliance, true;
      *         otherwise, false.
      */
-    public static boolean isStrict(final BaseParameters params) {
+    public static <T extends ImagingParameters> boolean isStrict(final T params) {
         return params.isStrict();
     }
 }
diff --git a/src/main/java/org/apache/commons/imaging/Imaging.java b/src/main/java/org/apache/commons/imaging/Imaging.java
index 32a28b0..8944a44 100644
--- a/src/main/java/org/apache/commons/imaging/Imaging.java
+++ b/src/main/java/org/apache/commons/imaging/Imaging.java
@@ -30,7 +30,6 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
 
-import org.apache.commons.imaging.common.BaseParameters;
 import org.apache.commons.imaging.common.ImageMetadata;
 import org.apache.commons.imaging.common.XmpEmbeddable;
 import org.apache.commons.imaging.common.bytesource.ByteSource;
@@ -89,7 +88,7 @@ import org.apache.commons.imaging.icc.IccProfileParser;
  * the image data.
  * </p>
  *
- * <p>Optional parameters are specified using a {@code BaseParameters} object.</p>
+ * <p>Optional parameters are specified using a {@code ImagingParameters} object.</p>
  *
  * <h3>Example code</h3>
  *
@@ -161,7 +160,7 @@ public final class Imaging {
 
         final String normalizedFilename = fileName.toLowerCase(Locale.ENGLISH);
 
-        for (final ImageParser imageParser : ImageParser.getAllImageParsers()) {
+        for (final ImageParser<?> imageParser : ImageParser.getAllImageParsers()) {
             for (final String extension : imageParser.getAcceptedExtensions()) {
                 if (normalizedFilename.endsWith(extension.toLowerCase(Locale.ENGLISH))) {
                     return true;
@@ -362,7 +361,7 @@ public final class Imaging {
      * @throws ImageReadException if it fails to parse the image
      * @throws IOException if it fails to read the image data
      */
-    public static ICC_Profile getICCProfile(final byte[] bytes, final BaseParameters params)
+    public static ICC_Profile getICCProfile(final byte[] bytes, final ImagingParameters params)
             throws ImageReadException, IOException {
         return getICCProfile(new ByteSourceArray(bytes), params);
     }
@@ -400,7 +399,7 @@ public final class Imaging {
      * @throws IOException if it fails to read the image data
      */
     public static ICC_Profile getICCProfile(final InputStream is, final String fileName,
-            final BaseParameters params) throws ImageReadException, IOException {
+            final ImagingParameters params) throws ImageReadException, IOException {
         return getICCProfile(new ByteSourceInputStream(is, fileName), params);
     }
 
@@ -432,12 +431,12 @@ public final class Imaging {
      * @throws ImageReadException if it fails to parse the image
      * @throws IOException if it fails to read the image data
      */
-    public static ICC_Profile getICCProfile(final File file, final BaseParameters params)
+    public static ICC_Profile getICCProfile(final File file, final ImagingParameters params)
             throws ImageReadException, IOException {
         return getICCProfile(new ByteSourceFile(file), params);
     }
 
-    protected static ICC_Profile getICCProfile(final ByteSource byteSource, final BaseParameters params)
+    protected static ICC_Profile getICCProfile(final ByteSource byteSource, final ImagingParameters params)
             throws ImageReadException, IOException {
         final byte[] bytes = getICCProfileBytes(byteSource, params);
         if (bytes == null) {
@@ -492,7 +491,7 @@ public final class Imaging {
      * @throws ImageReadException if it fails to parse the image
      * @throws IOException if it fails to read the image data
      */
-    public static byte[] getICCProfileBytes(final byte[] bytes, final BaseParameters params)
+    public static byte[] getICCProfileBytes(final byte[] bytes, final ImagingParameters params)
             throws ImageReadException, IOException {
         return getICCProfileBytes(new ByteSourceArray(bytes), params);
     }
@@ -533,14 +532,16 @@ public final class Imaging {
      * @throws ImageReadException if it fails to parse the image
      * @throws IOException if it fails to read the image data
      */
-    public static byte[] getICCProfileBytes(final File file, final BaseParameters params)
+    public static byte[] getICCProfileBytes(final File file, final ImagingParameters params)
             throws ImageReadException, IOException {
         return getICCProfileBytes(new ByteSourceFile(file), params);
     }
 
-    private static byte[] getICCProfileBytes(final ByteSource byteSource, final BaseParameters params)
+    private static <T extends ImagingParameters> byte[] getICCProfileBytes(final ByteSource byteSource, final T params)
             throws ImageReadException, IOException {
-        final ImageParser imageParser = getImageParser(byteSource);
+        // TODO: better generics or redesign API
+        @SuppressWarnings("unchecked")
+        final ImageParser<T> imageParser = (ImageParser<T>) getImageParser(byteSource);
 
         return imageParser.getICCProfileBytes(byteSource, params);
     }
@@ -564,7 +565,7 @@ public final class Imaging {
      * @throws IOException if it fails to read the image data
      */
     public static ImageInfo getImageInfo(final String fileName, final byte[] bytes,
-            final BaseParameters params) throws ImageReadException, IOException {
+            final ImagingParameters params) throws ImageReadException, IOException {
         return getImageInfo(new ByteSourceArray(fileName, bytes), params);
     }
 
@@ -631,7 +632,7 @@ public final class Imaging {
      * @throws IOException if it fails to read the image data
      */
     public static ImageInfo getImageInfo(final InputStream is, final String fileName,
-            final BaseParameters params) throws ImageReadException, IOException {
+            final ImagingParameters params) throws ImageReadException, IOException {
         return getImageInfo(new ByteSourceInputStream(is, fileName), params);
     }
 
@@ -671,7 +672,7 @@ public final class Imaging {
      * @throws ImageReadException if it fails to parse the image
      * @throws IOException if it fails to read the image data
      */
-    public static ImageInfo getImageInfo(final byte[] bytes, final BaseParameters params)
+    public static ImageInfo getImageInfo(final byte[] bytes, final ImagingParameters params)
             throws ImageReadException, IOException {
         return getImageInfo(new ByteSourceArray(bytes), params);
     }
@@ -692,7 +693,7 @@ public final class Imaging {
      * @throws ImageReadException if it fails to parse the image
      * @throws IOException if it fails to read the image data
      */
-    public static ImageInfo getImageInfo(final File file, final BaseParameters params)
+    public static ImageInfo getImageInfo(final File file, final ImagingParameters params)
             throws ImageReadException, IOException {
         return getImageInfo(new ByteSourceFile(file), params);
     }
@@ -717,32 +718,32 @@ public final class Imaging {
         return getImageInfo(file, null);
     }
 
-    private static ImageInfo getImageInfo(final ByteSource byteSource, final BaseParameters params)
+    private static ImageInfo getImageInfo(final ByteSource byteSource, final ImagingParameters params)
             throws ImageReadException, IOException {
         return getImageParser(byteSource).getImageInfo(byteSource, params);
     }
 
-    private static ImageParser getImageParser(final ByteSource byteSource)
+    private static ImageParser<?> getImageParser(final ByteSource byteSource)
             throws ImageReadException, IOException {
         final ImageFormat format = guessFormat(byteSource);
         if (!format.equals(ImageFormats.UNKNOWN)) {
 
-            final ImageParser[] imageParsers = ImageParser.getAllImageParsers();
+            final ImageParser<?>[] imageParsers = ImageParser.getAllImageParsers();
 
-            for (final ImageParser imageParser : imageParsers) {
+            for (final ImageParser<?> imageParser : imageParsers) {
                 if (imageParser.canAcceptType(format)) {
-                    return imageParser;
+                    return (ImageParser<?>) imageParser;
                 }
             }
         }
 
         final String fileName = byteSource.getFileName();
         if (fileName != null) {
-            final ImageParser[] imageParsers = ImageParser.getAllImageParsers();
+            final ImageParser<?>[] imageParsers = ImageParser.getAllImageParsers();
 
-            for (final ImageParser imageParser : imageParsers) {
+            for (final ImageParser<?> imageParser : imageParsers) {
                 if (imageParser.canAcceptExtension(fileName)) {
-                    return imageParser;
+                    return (ImageParser<?>) imageParser;
                 }
             }
         }
@@ -781,7 +782,7 @@ public final class Imaging {
      * @throws IOException if it fails to read the image data
      */
     public static Dimension getImageSize(final InputStream is, final String fileName,
-            final BaseParameters params) throws ImageReadException, IOException {
+            final ImagingParameters params) throws ImageReadException, IOException {
         return getImageSize(new ByteSourceInputStream(is, fileName), params);
     }
 
@@ -811,7 +812,7 @@ public final class Imaging {
      * @throws ImageReadException if it fails to parse the image
      * @throws IOException if it fails to read the image data
      */
-    public static Dimension getImageSize(final byte[] bytes, final BaseParameters params)
+    public static Dimension getImageSize(final byte[] bytes, final ImagingParameters params)
             throws ImageReadException, IOException {
         return getImageSize(new ByteSourceArray(bytes), params);
     }
@@ -842,14 +843,16 @@ public final class Imaging {
      * @throws ImageReadException if it fails to parse the image
      * @throws IOException if it fails to read the image data
      */
-    public static Dimension getImageSize(final File file, final BaseParameters params)
+    public static Dimension getImageSize(final File file, final ImagingParameters params)
             throws ImageReadException, IOException {
         return getImageSize(new ByteSourceFile(file), params);
     }
 
-    public static Dimension getImageSize(final ByteSource byteSource, final BaseParameters params)
+    public static <T extends ImagingParameters> Dimension getImageSize(final ByteSource byteSource, final T params)
             throws ImageReadException, IOException {
-        final ImageParser imageParser = getImageParser(byteSource);
+        // TODO: better generics or redesign API
+        @SuppressWarnings("unchecked")
+        final ImageParser<T> imageParser = (ImageParser<T>) getImageParser(byteSource);
 
         return imageParser.getImageSize(byteSource, params);
     }
@@ -884,7 +887,7 @@ public final class Imaging {
      * @throws ImageReadException if it fails to parse the image
      * @throws IOException if it fails to read the image data
      */
-    public static String getXmpXml(final InputStream is, final String fileName, final BaseParameters params)
+    public static String getXmpXml(final InputStream is, final String fileName, final ImagingParameters params)
             throws ImageReadException, IOException {
         return getXmpXml(new ByteSourceInputStream(is, fileName), params);
     }
@@ -915,7 +918,7 @@ public final class Imaging {
      * @throws ImageReadException if it fails to parse the image
      * @throws IOException if it fails to read the image data
      */
-    public static String getXmpXml(final byte[] bytes, final BaseParameters params)
+    public static String getXmpXml(final byte[] bytes, final ImagingParameters params)
             throws ImageReadException, IOException {
         return getXmpXml(new ByteSourceArray(bytes), params);
     }
@@ -946,7 +949,7 @@ public final class Imaging {
      * @throws ImageReadException if it fails to parse the image
      * @throws IOException if it fails to read the image data
      */
-    public static String getXmpXml(final File file, final BaseParameters params)
+    public static String getXmpXml(final File file, final ImagingParameters params)
             throws ImageReadException, IOException {
         return getXmpXml(new ByteSourceFile(file), params);
     }
@@ -962,9 +965,9 @@ public final class Imaging {
      * @throws ImageReadException if it fails to parse the image
      * @throws IOException if it fails to read the image data
      */
-    public static String getXmpXml(final ByteSource byteSource, final BaseParameters params)
+    public static String getXmpXml(final ByteSource byteSource, final ImagingParameters params)
             throws ImageReadException, IOException {
-        final ImageParser imageParser = getImageParser(byteSource);
+        final ImageParser<?> imageParser = getImageParser(byteSource);
         if (imageParser instanceof XmpEmbeddable) {
             return ((XmpEmbeddable) imageParser).getXmpXml(byteSource, params);
         }
@@ -1017,7 +1020,7 @@ public final class Imaging {
      * @throws ImageReadException if it fails to read the image metadata
      * @throws IOException if it fails to read the image data
      */
-    public static ImageMetadata getMetadata(final byte[] bytes, final BaseParameters params)
+    public static ImageMetadata getMetadata(final byte[] bytes, final ImagingParameters params)
             throws ImageReadException, IOException {
         return getMetadata(new ByteSourceArray(bytes), params);
     }
@@ -1073,7 +1076,7 @@ public final class Imaging {
      * @throws IOException if it fails to read the image data
      */
     public static ImageMetadata getMetadata(final InputStream is, final String fileName,
-            final BaseParameters params) throws ImageReadException, IOException {
+            final ImagingParameters params) throws ImageReadException, IOException {
         return getMetadata(new ByteSourceInputStream(is, fileName), params);
     }
 
@@ -1123,14 +1126,16 @@ public final class Imaging {
      * @throws ImageReadException if it fails to read the image metadata
      * @throws IOException if it fails to read the image data
      */
-    public static ImageMetadata getMetadata(final File file, final BaseParameters params)
+    public static ImageMetadata getMetadata(final File file, final ImagingParameters params)
             throws ImageReadException, IOException {
         return getMetadata(new ByteSourceFile(file), params);
     }
 
-    private static ImageMetadata getMetadata(final ByteSource byteSource, final BaseParameters params)
+    private static <T extends ImagingParameters> ImageMetadata getMetadata(final ByteSource byteSource, final T params)
             throws ImageReadException, IOException {
-        final ImageParser imageParser = getImageParser(byteSource);
+        // TODO: better generics or redesign API
+        @SuppressWarnings("unchecked")
+        final ImageParser<T> imageParser = (ImageParser<T>) getImageParser(byteSource);
 
         return imageParser.getMetadata(byteSource, params);
     }
@@ -1169,7 +1174,7 @@ public final class Imaging {
 
     private static String dumpImageFile(final ByteSource byteSource)
             throws ImageReadException, IOException {
-        final ImageParser imageParser = getImageParser(byteSource);
+        final ImageParser<?> imageParser = getImageParser(byteSource);
 
         return imageParser.dumpImageFile(byteSource);
     }
@@ -1206,7 +1211,7 @@ public final class Imaging {
 
     private static FormatCompliance getFormatCompliance(final ByteSource byteSource)
             throws ImageReadException, IOException {
-        final ImageParser imageParser = getImageParser(byteSource);
+        final ImageParser<?> imageParser = getImageParser(byteSource);
 
         return imageParser.getFormatCompliance(byteSource);
     }
@@ -1263,7 +1268,7 @@ public final class Imaging {
 
     private static List<BufferedImage> getAllBufferedImages(
             final ByteSource byteSource) throws ImageReadException, IOException {
-        final ImageParser imageParser = getImageParser(byteSource);
+        final ImageParser<?> imageParser = getImageParser(byteSource);
 
         return imageParser.getAllBufferedImages(byteSource);
     }
@@ -1312,7 +1317,7 @@ public final class Imaging {
      * while reading an image (i.e. a format violation, etc.).
      * @throws IOException  in the event of an unrecoverable I/O exception.
      */
-    public static BufferedImage getBufferedImage(final InputStream is, final BaseParameters params)
+    public static BufferedImage getBufferedImage(final InputStream is, final ImagingParameters params)
             throws ImageReadException, IOException {
         String fileName = params != null ? "" : null;
         return getBufferedImage(new ByteSourceInputStream(is, fileName), params);
@@ -1341,10 +1346,8 @@ public final class Imaging {
 
     /**
      * Reads the first image from a byte array
-     * using data-processing options specified through a parameters
-     * {@code BaseParameters}. Options may be configured using the ImagingConstants
-     * interface or the various format-specific implementations provided
-     * by this package.
+     * using data-processing options specified through the parameters
+     * {@code ImagingParameters} object.
      *
      * <p>For the most recent information on support for specific formats, refer to
      * <a href="https://commons.apache.org/imaging/formatsupport.html">Format Support</a>
@@ -1360,7 +1363,7 @@ public final class Imaging {
      * while reading an image (i.e. a format violation, etc.).
      * @throws IOException  in the event of an unrecoverable I/O exception.
      */
-    public static BufferedImage getBufferedImage(final byte[] bytes, final BaseParameters params)
+    public static BufferedImage getBufferedImage(final byte[] bytes, final ImagingParameters params)
             throws ImageReadException, IOException {
         return getBufferedImage(new ByteSourceArray(bytes), params);
     }
@@ -1406,16 +1409,18 @@ public final class Imaging {
      * while reading an image (i.e. a format violation, etc.).
      * @throws IOException  in the event of an unrecoverable I/O exception.
      */
-    public static BufferedImage getBufferedImage(final File file, BaseParameters params)
+    public static BufferedImage getBufferedImage(final File file, ImagingParameters params)
             throws ImageReadException, IOException {
         return getBufferedImage(new ByteSourceFile(file), params);
     }
 
 
 
-    private static BufferedImage getBufferedImage(final ByteSource byteSource,
-            BaseParameters params) throws ImageReadException, IOException {
-        final ImageParser imageParser = getImageParser(byteSource);
+    private static <T extends ImagingParameters> BufferedImage getBufferedImage(final ByteSource byteSource,
+            T params) throws ImageReadException, IOException {
+        // TODO: better generics or redesign API
+        @SuppressWarnings("unchecked")
+        final ImageParser<T> imageParser = (ImageParser<T>) getImageParser(byteSource);
         return imageParser.getBufferedImage(byteSource, params);
     }
 
@@ -1442,7 +1447,7 @@ public final class Imaging {
      * @see ImagingConstants
      */
     public static void writeImage(final BufferedImage src, final File file,
-            final BaseParameters params) throws ImageWriteException,
+            final ImagingParameters params) throws ImageWriteException,
             IOException {
         try (FileOutputStream fos = new FileOutputStream(file);
                 BufferedOutputStream os = new BufferedOutputStream(fos)) {
@@ -1474,7 +1479,7 @@ public final class Imaging {
      * @see ImagingConstants
      */
     public static byte[] writeImageToBytes(final BufferedImage src,
-            final BaseParameters params) throws ImageWriteException,
+            final ImagingParameters params) throws ImageWriteException,
             IOException {
         final ByteArrayOutputStream os = new ByteArrayOutputStream();
 
@@ -1505,14 +1510,14 @@ public final class Imaging {
      * @throws IOException in the event of an unrecoverable I/O exception.
      * @see ImagingConstants
      */
-    public static void writeImage(final BufferedImage src, final OutputStream os,
-            BaseParameters params) throws ImageWriteException,
+    public static <T extends ImagingParameters> void writeImage(final BufferedImage src, final OutputStream os,
+            ImagingParameters params) throws ImageWriteException,
             IOException {
         Objects.requireNonNull(params, "You must provide a valid imaging parameters object.");
-        final ImageParser[] imageParsers = ImageParser.getAllImageParsers();
+        final ImageParser<?>[] imageParsers = ImageParser.getAllImageParsers();
 
-        ImageParser imageParser = null;
-        for (final ImageParser imageParser2 : imageParsers) {
+        ImageParser<?> imageParser = null;
+        for (final ImageParser<?> imageParser2 : imageParsers) {
             if (imageParser2.canAcceptType(params.getImageFormat())) {
                 imageParser = imageParser2;
                 break;
diff --git a/src/main/java/org/apache/commons/imaging/ImagingConstants.java b/src/main/java/org/apache/commons/imaging/ImagingConstants.java
index d25430d..80de12d 100644
--- a/src/main/java/org/apache/commons/imaging/ImagingConstants.java
+++ b/src/main/java/org/apache/commons/imaging/ImagingConstants.java
@@ -74,25 +74,6 @@ public final class ImagingConstants {
     public static final String PARAM_KEY_EXIF = "EXIF";
 
     /**
-     * <p>Parameter key.</p>
-     *
-     * <p>Only used when writing images.</p>
-     *
-     * <p>Valid values: String of XMP XML.</p>
-     */
-    public static final String PARAM_KEY_XMP_XML = "XMP_XML";
-
-    /**
-     * <p>Parameter key. Used in write operations to indicate the desired pixel
-     * density (DPI), and/or aspect ratio.</p>
-     *
-     * <p>Valid values: PixelDensity</p>
-     *
-     * @see org.apache.commons.imaging.PixelDensity
-     */
-    public static final String PARAM_KEY_PIXEL_DENSITY = "PIXEL_DENSITY";
-
-    /**
      * Empty byte array.
      */
     public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
diff --git a/src/main/java/org/apache/commons/imaging/ImagingParameters.java b/src/main/java/org/apache/commons/imaging/ImagingParameters.java
index 6f39916..93e41d1 100644
--- a/src/main/java/org/apache/commons/imaging/ImagingParameters.java
+++ b/src/main/java/org/apache/commons/imaging/ImagingParameters.java
@@ -16,9 +16,93 @@
  */
 package org.apache.commons.imaging;
 
+import org.apache.commons.imaging.common.BufferedImageFactory;
+
 /**
  * Imaging parameters.
  *
+ * <p>Contains parameters that are common to all formats. Implementations must include
+ * the specific parameters for each image format.</p>
+ *
  * @since 1.0-alpha3
  */
-public interface ImagingParameters {}
+public class ImagingParameters {
+
+    /**
+     * Whether to throw an exception when any issue occurs during reading
+     * or writing a file format. Default is {@code false}.
+     */
+    private boolean strict = false;
+
+    /**
+     * An optional file name, used for the description of input streams
+     * where a file name would be hard (or not possible) to be identified.
+     * Default is {@code null}.
+     */
+    private String fileName = null;
+
+    /**
+     * Factory to create {@code BufferedImage}s. Default is {@code null}.
+     */
+    private BufferedImageFactory bufferedImageFactory = null;
+
+    /**
+     * Image format used in write operations to indicate desired image format.
+     * Default is {@code null}.
+     *
+     * <p>Valid values: Any format defined in ImageFormat, such as
+     * ImageFormat.IMAGE_FORMAT_PNG.</p>
+     *
+     * @see org.apache.commons.imaging.ImageFormats
+     */
+    private ImageFormat imageFormat;
+
+    /**
+     * <p>Parameter key. Used in write operations to indicate the desired pixel
+     * density (DPI), and/or aspect ratio.</p>
+     */
+    private PixelDensity pixelDensity;
+
+    // getters and setters
+
+    public boolean isStrict() {
+        return strict;
+    }
+
+    public void setStrict(boolean strict) {
+        this.strict = strict;
+    }
+
+    public String getFileName() {
+        return fileName;
+    }
+
+    public void setFileName(String fileName) {
+        this.fileName = fileName;
+    }
+
+    public BufferedImageFactory getBufferedImageFactory() {
+        return bufferedImageFactory;
+    }
+
+    public void setBufferedImageFactory(BufferedImageFactory bufferedImageFactory) {
+        this.bufferedImageFactory = bufferedImageFactory;
+    }
+
+    public ImageFormat getImageFormat() {
+        return imageFormat;
+    }
+
+    public void setImageFormat(ImageFormat imageFormat) {
+        this.imageFormat = imageFormat;
+    }
+
+    public PixelDensity getPixelDensity() {
+        return pixelDensity;
+    }
+
+    public void setPixelDensity(PixelDensity pixelDensity) {
+        this.pixelDensity = pixelDensity;
+    }
+
+}
diff --git a/src/main/java/org/apache/commons/imaging/common/BaseParameters.java b/src/main/java/org/apache/commons/imaging/common/BaseParameters.java
deleted file mode 100644
index 3e8e4c9..0000000
--- a/src/main/java/org/apache/commons/imaging/common/BaseParameters.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.apache.commons.imaging.common;
-
-import org.apache.commons.imaging.ImageFormat;
-import org.apache.commons.imaging.ImagingParameters;
-
-/**
- * Set of parameters that are common to all imaging formats.
- *
- * @since 1.0-alpha3
- */
-public class BaseParameters implements ImagingParameters {
-
-    /**
-     * Whether to throw an exception when any issue occurs during reading
-     * or writing a file format. Default is {@code false}.
-     */
-    private boolean strict = false;
-
-    /**
-     * An optional file name, used for the description of input streams
-     * where a file name would be hard (or not possible) to be identified.
-     * Default is {@code null}.
-     */
-    private String fileName = null;
-
-    /**
-     * Factory to create {@code BufferedImage}s. Default is {@code null}.
-     */
-    private BufferedImageFactory bufferedImageFactory = null;
-
-    /**
-     * Image format used in write operations to indicate desired image format.
-     * Default is {@code null}.
-     *
-     * <p>Valid values: Any format defined in ImageFormat, such as
-     * ImageFormat.IMAGE_FORMAT_PNG.</p>
-     *
-     * @see org.apache.commons.imaging.ImageFormats
-     */
-    private ImageFormat imageFormat;
-
-    // getters and setters
-
-    public boolean isStrict() {
-        return strict;
-    }
-
-    public void setStrict(boolean strict) {
-        this.strict = strict;
-    }
-
-    public String getFileName() {
-        return fileName;
-    }
-
-    public void setFileName(String fileName) {
-        this.fileName = fileName;
-    }
-
-    public BufferedImageFactory getBufferedImageFactory() {
-        return bufferedImageFactory;
-    }
-
-    public void setBufferedImageFactory(BufferedImageFactory bufferedImageFactory) {
-        this.bufferedImageFactory = bufferedImageFactory;
-    }
-
-    public ImageFormat getImageFormat() {
-        return imageFormat;
-    }
-
-    public void setImageFormat(ImageFormat imageFormat) {
-        this.imageFormat = imageFormat;
-    }
-
-}
diff --git a/src/main/java/org/apache/commons/imaging/common/XmpEmbeddable.java b/src/main/java/org/apache/commons/imaging/common/XmpEmbeddable.java
index 4c3f2ce..12af31f 100644
--- a/src/main/java/org/apache/commons/imaging/common/XmpEmbeddable.java
+++ b/src/main/java/org/apache/commons/imaging/common/XmpEmbeddable.java
@@ -19,6 +19,7 @@ package org.apache.commons.imaging.common;
 import java.io.IOException;
 
 import org.apache.commons.imaging.ImageReadException;
+import org.apache.commons.imaging.ImagingParameters;
 import org.apache.commons.imaging.common.bytesource.ByteSource;
 
 /**
@@ -28,7 +29,7 @@ import org.apache.commons.imaging.common.bytesource.ByteSource;
  * @see <a href="https://en.wikipedia.org/wiki/Extensible_Metadata_Platform">https://en.wikipedia.org/wiki/Extensible_Metadata_Platform</a>
  * @since 1.0
  */
-public interface XmpEmbeddable {
+public interface XmpEmbeddable<T extends ImagingParameters> {
 
     /**
      * Get a string containing XML-formatted text conforming to the Extensible
@@ -47,7 +48,7 @@ public interface XmpEmbeddable {
      *                            parser implementation.
      * @throws IOException        In the event of unsuccessful read or access operation.
      */
-    String getXmpXml(ByteSource byteSource, BaseParameters params)
+    String getXmpXml(ByteSource byteSource, T params)
             throws ImageReadException, IOException;
 
 }
diff --git a/src/main/java/org/apache/commons/imaging/ImagingParameters.java b/src/main/java/org/apache/commons/imaging/common/XmpImagingParameters.java
similarity index 68%
copy from src/main/java/org/apache/commons/imaging/ImagingParameters.java
copy to src/main/java/org/apache/commons/imaging/common/XmpImagingParameters.java
index 6f39916..3352274 100644
--- a/src/main/java/org/apache/commons/imaging/ImagingParameters.java
+++ b/src/main/java/org/apache/commons/imaging/common/XmpImagingParameters.java
@@ -14,11 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.imaging;
+
+package org.apache.commons.imaging.common;
+
+import org.apache.commons.imaging.ImagingParameters;
 
 /**
- * Imaging parameters.
- *
+ * Parameters for formats that support Xmp.
  * @since 1.0-alpha3
  */
-public interface ImagingParameters {}
+public class XmpImagingParameters extends ImagingParameters {
+
+    private String xmpXml;
+
+    public String getXmpXml() {
+        return xmpXml;
+    }
+
+    public void setXmpXml(String xmpXml) {
+        this.xmpXml = xmpXml;
+    }
+
+}
diff --git a/src/main/java/org/apache/commons/imaging/formats/bmp/BmpImageParser.java b/src/main/java/org/apache/commons/imaging/formats/bmp/BmpImageParser.java
index 23859e2..01938ac 100644
--- a/src/main/java/org/apache/commons/imaging/formats/bmp/BmpImageParser.java
+++ b/src/main/java/org/apache/commons/imaging/formats/bmp/BmpImageParser.java
@@ -16,9 +16,6 @@
  */
 package org.apache.commons.imaging.formats.bmp;
 
-import static org.apache.commons.imaging.ImagingConstants.BUFFERED_IMAGE_FACTORY;
-import static org.apache.commons.imaging.ImagingConstants.PARAM_KEY_FORMAT;
-import static org.apache.commons.imaging.ImagingConstants.PARAM_KEY_PIXEL_DENSITY;
 import static org.apache.commons.imaging.common.BinaryFunctions.read2Bytes;
 import static org.apache.commons.imaging.common.BinaryFunctions.read4Bytes;
 import static org.apache.commons.imaging.common.BinaryFunctions.readByte;
@@ -33,9 +30,7 @@ import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -54,7 +49,7 @@ import org.apache.commons.imaging.common.bytesource.ByteSource;
 import org.apache.commons.imaging.palette.PaletteFactory;
 import org.apache.commons.imaging.palette.SimplePalette;
 
-public class BmpImageParser extends ImageParser {
+public class BmpImageParser extends ImageParser<BmpImagingParameters> {
 
     private static final Logger LOGGER = Logger.getLogger(BmpImageParser.class.getName());
 
@@ -485,22 +480,14 @@ public class BmpImageParser extends ImageParser {
     }
 
     @Override
-    public byte[] getICCProfileBytes(final ByteSource byteSource, final Map<String, Object> params)
+    public byte[] getICCProfileBytes(final ByteSource byteSource, final BmpImagingParameters params)
             throws ImageReadException, IOException {
         return null;
     }
 
     @Override
-    public Dimension getImageSize(final ByteSource byteSource, Map<String, Object> params)
+    public Dimension getImageSize(final ByteSource byteSource, final BmpImagingParameters params)
             throws ImageReadException, IOException {
-        // make copy of params; we'll clear keys as we consume them.
-        params = (params == null) ? new HashMap<>() : new HashMap<>(params);
-
-        if (!params.isEmpty()) {
-            final Object firstKey = params.keySet().iterator().next();
-            throw new ImageReadException("Unknown parameter: " + firstKey);
-        }
-
         final BmpHeaderInfo bhi = readBmpHeaderInfo(byteSource);
 
         return new Dimension(bhi.width, bhi.height);
@@ -508,7 +495,7 @@ public class BmpImageParser extends ImageParser {
     }
 
     @Override
-    public ImageMetadata getMetadata(final ByteSource byteSource, final Map<String, Object> params)
+    public ImageMetadata getMetadata(final ByteSource byteSource, final BmpImagingParameters params)
             throws ImageReadException, IOException {
         // TODO this should throw UnsupportedOperationException, but RoundtripTest has to be refactored completely before this can be changed
         return null;
@@ -538,16 +525,8 @@ public class BmpImageParser extends ImageParser {
     }
 
     @Override
-    public ImageInfo getImageInfo(final ByteSource byteSource, Map<String, Object> params)
+    public ImageInfo getImageInfo(final ByteSource byteSource, final BmpImagingParameters params)
             throws ImageReadException, IOException {
-        // make copy of params; we'll clear keys as we consume them.
-        params = params == null ? new HashMap<>() : new HashMap<>(params);
-
-        if (!params.isEmpty()) {
-            final Object firstKey = params.keySet().iterator().next();
-            throw new ImageReadException("Unknown parameter: " + firstKey);
-        }
-
         BmpImageContents ic = null;
         try (InputStream is = byteSource.getInputStream()) {
             ic = readImageContents(is, FormatCompliance.getDefault());
@@ -628,27 +607,15 @@ public class BmpImageParser extends ImageParser {
     }
 
     @Override
-    public BufferedImage getBufferedImage(final ByteSource byteSource, final Map<String, Object> params)
+    public BufferedImage getBufferedImage(final ByteSource byteSource, final BmpImagingParameters params)
             throws ImageReadException, IOException {
         try (InputStream is = byteSource.getInputStream()) {
             return getBufferedImage(is, params);
         }
     }
 
-    public BufferedImage getBufferedImage(final InputStream inputStream, Map<String, Object> params)
+    public BufferedImage getBufferedImage(final InputStream inputStream, final BmpImagingParameters params)
             throws ImageReadException, IOException {
-        // make copy of params; we'll clear keys as we consume them.
-        params = (params == null) ? new HashMap<>() : new HashMap<>(params);
-
-        if (params.containsKey(BUFFERED_IMAGE_FACTORY)) {
-            params.remove(BUFFERED_IMAGE_FACTORY);
-        }
-
-        if (!params.isEmpty()) {
-            final Object firstKey = params.keySet().iterator().next();
-            throw new ImageReadException("Unknown parameter: " + firstKey);
-        }
-
         final BmpImageContents ic = readImageContents(inputStream, FormatCompliance.getDefault());
 
         final BmpHeaderInfo bhi = ic.bhi;
@@ -674,24 +641,9 @@ public class BmpImageParser extends ImageParser {
     }
 
     @Override
-    public void writeImage(final BufferedImage src, final OutputStream os, Map<String, Object> params)
+    public void writeImage(final BufferedImage src, final OutputStream os, final BmpImagingParameters params)
             throws ImageWriteException, IOException {
-        // make copy of params; we'll clear keys as we consume them.
-        params = (params == null) ? new HashMap<>() : new HashMap<>(params);
-
-        PixelDensity pixelDensity = null;
-
-        // clear format key.
-        if (params.containsKey(PARAM_KEY_FORMAT)) {
-            params.remove(PARAM_KEY_FORMAT);
-        }
-        if (params.containsKey(PARAM_KEY_PIXEL_DENSITY)) {
-            pixelDensity = (PixelDensity) params.remove(PARAM_KEY_PIXEL_DENSITY);
-        }
-        if (!params.isEmpty()) {
-            final Object firstKey = params.keySet().iterator().next();
-            throw new ImageWriteException("Unknown parameter: " + firstKey);
-        }
+        PixelDensity pixelDensity = params.getPixelDensity();
 
         final SimplePalette palette = new PaletteFactory().makeExactRgbPaletteSimple(
                 src, 256);
diff --git a/src/main/java/org/apache/commons/imaging/ImagingParameters.java b/src/main/java/org/apache/commons/imaging/formats/bmp/BmpImagingParameters.java
similarity index 81%
copy from src/main/java/org/apache/commons/imaging/ImagingParameters.java
copy to src/main/java/org/apache/commons/imaging/formats/bmp/BmpImagingParameters.java
index 6f39916..73cc803 100644
--- a/src/main/java/org/apache/commons/imaging/ImagingParameters.java
+++ b/src/main/java/org/apache/commons/imaging/formats/bmp/BmpImagingParameters.java
@@ -14,11 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.imaging;
+
+package org.apache.commons.imaging.formats.bmp;
+
+import org.apache.commons.imaging.ImagingParameters;
 
 /**
- * Imaging parameters.
- *
+ * Bmp format parameters.
  * @since 1.0-alpha3
  */
-public interface ImagingParameters {}
+public class BmpImagingParameters extends ImagingParameters {}
diff --git a/src/main/java/org/apache/commons/imaging/formats/dcx/DcxImageParser.java b/src/main/java/org/apache/commons/imaging/formats/dcx/DcxImageParser.java
index 0d02204..e4467a6 100644
--- a/src/main/java/org/apache/commons/imaging/formats/dcx/DcxImageParser.java
+++ b/src/main/java/org/apache/commons/imaging/formats/dcx/DcxImageParser.java
@@ -16,8 +16,6 @@
  */
 package org.apache.commons.imaging.formats.dcx;
 
-import static org.apache.commons.imaging.ImagingConstants.PARAM_KEY_FORMAT;
-import static org.apache.commons.imaging.ImagingConstants.PARAM_KEY_PIXEL_DENSITY;
 import static org.apache.commons.imaging.common.BinaryFunctions.read4Bytes;
 
 import java.awt.Dimension;
@@ -28,9 +26,7 @@ import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 import org.apache.commons.imaging.ImageFormat;
 import org.apache.commons.imaging.ImageFormats;
@@ -38,15 +34,14 @@ import org.apache.commons.imaging.ImageInfo;
 import org.apache.commons.imaging.ImageParser;
 import org.apache.commons.imaging.ImageReadException;
 import org.apache.commons.imaging.ImageWriteException;
-import org.apache.commons.imaging.PixelDensity;
 import org.apache.commons.imaging.common.BinaryOutputStream;
 import org.apache.commons.imaging.common.ImageMetadata;
 import org.apache.commons.imaging.common.bytesource.ByteSource;
 import org.apache.commons.imaging.common.bytesource.ByteSourceInputStream;
-import org.apache.commons.imaging.formats.pcx.PcxConstants;
 import org.apache.commons.imaging.formats.pcx.PcxImageParser;
+import org.apache.commons.imaging.formats.pcx.PcxImagingParameters;
 
-public class DcxImageParser extends ImageParser {
+public class DcxImageParser extends ImageParser<PcxImagingParameters> {
     // See http://www.fileformat.fine/format/pcx/egff.htm for documentation
     private static final String DEFAULT_EXTENSION = ".dcx";
     private static final String[] ACCEPTED_EXTENSIONS = { ".dcx", };
@@ -79,28 +74,28 @@ public class DcxImageParser extends ImageParser {
 
     // FIXME should throw UOE
     @Override
-    public ImageMetadata getMetadata(final ByteSource byteSource, final Map<String, Object> params)
+    public ImageMetadata getMetadata(final ByteSource byteSource, final PcxImagingParameters params)
             throws ImageReadException, IOException {
         return null;
     }
 
     // FIXME should throw UOE
     @Override
-    public ImageInfo getImageInfo(final ByteSource byteSource, final Map<String, Object> params)
+    public ImageInfo getImageInfo(final ByteSource byteSource, final PcxImagingParameters params)
             throws ImageReadException, IOException {
         return null;
     }
 
     // FIXME should throw UOE
     @Override
-    public Dimension getImageSize(final ByteSource byteSource, final Map<String, Object> params)
+    public Dimension getImageSize(final ByteSource byteSource, final PcxImagingParameters params)
             throws ImageReadException, IOException {
         return null;
     }
 
     // FIXME should throw UOE
     @Override
-    public byte[] getICCProfileBytes(final ByteSource byteSource, final Map<String, Object> params)
+    public byte[] getICCProfileBytes(final ByteSource byteSource, final PcxImagingParameters params)
             throws ImageReadException, IOException {
         return null;
     }
@@ -166,7 +161,7 @@ public class DcxImageParser extends ImageParser {
 
     @Override
     public final BufferedImage getBufferedImage(final ByteSource byteSource,
-            final Map<String, Object> params) throws ImageReadException, IOException {
+            final PcxImagingParameters params) throws ImageReadException, IOException {
         final List<BufferedImage> list = getAllBufferedImages(byteSource);
         if (list.isEmpty()) {
             return null;
@@ -185,7 +180,7 @@ public class DcxImageParser extends ImageParser {
                 final ByteSourceInputStream pcxSource = new ByteSourceInputStream(
                         stream, null);
                 final BufferedImage image = pcxImageParser.getBufferedImage(
-                        pcxSource, new HashMap<String, Object>());
+                        pcxSource, new PcxImagingParameters());
                 images.add(image);
             }
         }
@@ -193,40 +188,8 @@ public class DcxImageParser extends ImageParser {
     }
 
     @Override
-    public void writeImage(final BufferedImage src, final OutputStream os, Map<String, Object> params)
+    public void writeImage(final BufferedImage src, final OutputStream os, final PcxImagingParameters params)
             throws ImageWriteException, IOException {
-        // make copy of params; we'll clear keys as we consume them.
-        params = (params == null) ? new HashMap<>() : new HashMap<>(params);
-
-        final HashMap<String, Object> pcxParams = new HashMap<>();
-
-        // clear format key.
-        if (params.containsKey(PARAM_KEY_FORMAT)) {
-            params.remove(PARAM_KEY_FORMAT);
-        }
-
-        if (params.containsKey(PcxConstants.PARAM_KEY_PCX_COMPRESSION)) {
-            final Object value = params.remove(PcxConstants.PARAM_KEY_PCX_COMPRESSION);
-            pcxParams.put(PcxConstants.PARAM_KEY_PCX_COMPRESSION, value);
-        }
-
-        if (params.containsKey(PARAM_KEY_PIXEL_DENSITY)) {
-            final Object value = params.remove(PARAM_KEY_PIXEL_DENSITY);
-            if (value != null) {
-                if (!(value instanceof PixelDensity)) {
-                    throw new ImageWriteException(
-                            "Invalid pixel density parameter");
-                }
-                pcxParams.put(PARAM_KEY_PIXEL_DENSITY, value);
-            }
-        }
-
-
-        if (!params.isEmpty()) {
-            final Object firstKey = params.keySet().iterator().next();
-            throw new ImageWriteException("Unknown parameter: " + firstKey);
-        }
-
         final int headerSize = 4 + 1024 * 4;
 
         final BinaryOutputStream bos = new BinaryOutputStream(os,
@@ -238,6 +201,6 @@ public class DcxImageParser extends ImageParser {
             bos.write4Bytes(0);
         }
         final PcxImageParser pcxImageParser = new PcxImageParser();
-        pcxImageParser.writeImage(src, bos, pcxParams);
+        pcxImageParser.writeImage(src, bos, params);
     }
 }
diff --git a/src/main/java/org/apache/commons/imaging/formats/gif/GifImageParser.java b/src/main/java/org/apache/commons/imaging/formats/gif/GifImageParser.java
index a8e6a3f..2da2527 100644
--- a/src/main/java/org/apache/commons/imaging/formats/gif/GifImageParser.java
+++ b/src/main/java/org/apache/commons/imaging/formats/gif/GifImageParser.java
@@ -16,8 +16,6 @@
  */
 package org.apache.commons.imaging.formats.gif;
 
-import static org.apache.commons.imaging.ImagingConstants.PARAM_KEY_FORMAT;
-import static org.apache.commons.imaging.ImagingConstants.PARAM_KEY_XMP_XML;
 import static org.apache.commons.imaging.common.BinaryFunctions.compareBytes;
 import static org.apache.commons.imaging.common.BinaryFunctions.printByteBits;
 import static org.apache.commons.imaging.common.BinaryFunctions.printCharQuad;
@@ -35,9 +33,7 @@ import java.io.PrintWriter;
 import java.nio.ByteOrder;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -58,7 +54,7 @@ import org.apache.commons.imaging.common.mylzw.MyLzwDecompressor;
 import org.apache.commons.imaging.palette.Palette;
 import org.apache.commons.imaging.palette.PaletteFactory;
 
-public class GifImageParser extends ImageParser implements XmpEmbeddable {
+public class GifImageParser extends ImageParser<GifImagingParameters> implements XmpEmbeddable<GifImagingParameters> {
 
     private static final Logger LOGGER = Logger.getLogger(GifImageParser.class.getName());
 
@@ -476,13 +472,13 @@ public class GifImageParser extends ImageParser implements XmpEmbeddable {
     }
 
     @Override
-    public byte[] getICCProfileBytes(final ByteSource byteSource, final Map<String, Object> params)
+    public byte[] getICCProfileBytes(final ByteSource byteSource, final GifImagingParameters params)
             throws ImageReadException, IOException {
         return null;
     }
 
     @Override
-    public Dimension getImageSize(final ByteSource byteSource, final Map<String, Object> params)
+    public Dimension getImageSize(final ByteSource byteSource, final GifImagingParameters params)
             throws ImageReadException, IOException {
         final GifImageContents blocks = readFile(byteSource, false);
 
@@ -524,7 +520,7 @@ public class GifImageParser extends ImageParser implements XmpEmbeddable {
     }
 
     @Override
-    public ImageMetadata getMetadata(final ByteSource byteSource, final Map<String, Object> params)
+    public ImageMetadata getMetadata(final ByteSource byteSource, final GifImagingParameters params)
             throws ImageReadException, IOException {
         final GifImageContents imageContents = readFile(byteSource, false);
 
@@ -557,7 +553,7 @@ public class GifImageParser extends ImageParser implements XmpEmbeddable {
     }
 
     @Override
-    public ImageInfo getImageInfo(final ByteSource byteSource, final Map<String, Object> params)
+    public ImageInfo getImageInfo(final ByteSource byteSource, final GifImagingParameters params)
             throws ImageReadException, IOException {
         final GifImageContents blocks = readFile(byteSource, false);
 
@@ -818,7 +814,7 @@ public class GifImageParser extends ImageParser implements XmpEmbeddable {
     }
 
     @Override
-    public BufferedImage getBufferedImage(final ByteSource byteSource, final Map<String, Object> params)
+    public BufferedImage getBufferedImage(final ByteSource byteSource, final GifImagingParameters params)
             throws ImageReadException, IOException {
         final GifImageContents imageContents = readFile(byteSource, false);
 
@@ -845,26 +841,10 @@ public class GifImageParser extends ImageParser implements XmpEmbeddable {
     }
 
     @Override
-    public void writeImage(final BufferedImage src, final OutputStream os, Map<String, Object> params)
+    public void writeImage(final BufferedImage src, final OutputStream os, GifImagingParameters params)
             throws ImageWriteException, IOException {
-        // make copy of params; we'll clear keys as we consume them.
-        params = new HashMap<>(params);
 
-        // clear format key.
-        if (params.containsKey(PARAM_KEY_FORMAT)) {
-            params.remove(PARAM_KEY_FORMAT);
-        }
-
-        String xmpXml = null;
-        if (params.containsKey(PARAM_KEY_XMP_XML)) {
-            xmpXml = (String) params.get(PARAM_KEY_XMP_XML);
-            params.remove(PARAM_KEY_XMP_XML);
-        }
-
-        if (!params.isEmpty()) {
-            final Object firstKey = params.keySet().iterator().next();
-            throw new ImageWriteException("Unknown parameter: " + firstKey);
-        }
+        String xmpXml = params.getXmpXml();
 
         final int width = src.getWidth();
         final int height = src.getHeight();
@@ -1088,7 +1068,7 @@ public class GifImageParser extends ImageParser implements XmpEmbeddable {
      * @return Xmp Xml as String, if present. Otherwise, returns null.
      */
     @Override
-    public String getXmpXml(final ByteSource byteSource, final Map<String, Object> params)
+    public String getXmpXml(final ByteSource byteSource, final GifImagingParameters params)
             throws ImageReadException, IOException {
         try (InputStream is = byteSource.getInputStream()) {
             final GifHeaderInfo ghi = readHeader(is, null);
diff --git a/src/main/java/org/apache/commons/imaging/ImagingParameters.java b/src/main/java/org/apache/commons/imaging/formats/gif/GifImagingParameters.java
similarity index 80%
copy from src/main/java/org/apache/commons/imaging/ImagingParameters.java
copy to src/main/java/org/apache/commons/imaging/formats/gif/GifImagingParameters.java
index 6f39916..2e50660 100644
--- a/src/main/java/org/apache/commons/imaging/ImagingParameters.java
+++ b/src/main/java/org/apache/commons/imaging/formats/gif/GifImagingParameters.java
@@ -14,11 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.imaging;
+
+package org.apache.commons.imaging.formats.gif;
+
+import org.apache.commons.imaging.common.XmpImagingParameters;
 
 /**
- * Imaging parameters.
- *
+ * Gif format parameters.
  * @since 1.0-alpha3
  */
-public interface ImagingParameters {}
+public class GifImagingParameters extends XmpImagingParameters {
+
+}
diff --git a/src/main/java/org/apache/commons/imaging/formats/jpeg/iptc/IptcParser.java b/src/main/java/org/apache/commons/imaging/formats/jpeg/iptc/IptcParser.java
index 1fa5ba1..2bd435b 100644
--- a/src/main/java/org/apache/commons/imaging/formats/jpeg/iptc/IptcParser.java
+++ b/src/main/java/org/apache/commons/imaging/formats/jpeg/iptc/IptcParser.java
@@ -43,7 +43,7 @@ import java.util.logging.Logger;
 import org.apache.commons.imaging.ImageReadException;
 import org.apache.commons.imaging.ImageWriteException;
 import org.apache.commons.imaging.ImagingConstants;
-import org.apache.commons.imaging.common.BaseParameters;
+import org.apache.commons.imaging.ImagingParameters;
 import org.apache.commons.imaging.common.BinaryFileParser;
 import org.apache.commons.imaging.common.BinaryFunctions;
 import org.apache.commons.imaging.common.BinaryOutputStream;
@@ -123,7 +123,7 @@ public class IptcParser extends BinaryFileParser {
      * Some IPTC blocks are missing this first "record version" record, so we
      * don't require it.
      */
-    public PhotoshopApp13Data parsePhotoshopSegment(final byte[] bytes, final BaseParameters params)
+    public PhotoshopApp13Data parsePhotoshopSegment(final byte[] bytes, final ImagingParameters params)
             throws ImageReadException, IOException {
         final boolean strict =  params != null && params.isStrict();
 
diff --git a/src/main/java/org/apache/commons/imaging/formats/jpeg/iptc/JpegIptcRewriter.java b/src/main/java/org/apache/commons/imaging/formats/jpeg/iptc/JpegIptcRewriter.java
index 0d87860..f5639d9 100644
--- a/src/main/java/org/apache/commons/imaging/formats/jpeg/iptc/JpegIptcRewriter.java
+++ b/src/main/java/org/apache/commons/imaging/formats/jpeg/iptc/JpegIptcRewriter.java
@@ -22,13 +22,12 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 import org.apache.commons.imaging.ImageReadException;
 import org.apache.commons.imaging.ImageWriteException;
 import org.apache.commons.imaging.ImagingConstants;
+import org.apache.commons.imaging.ImagingParameters;
 import org.apache.commons.imaging.common.bytesource.ByteSource;
 import org.apache.commons.imaging.common.bytesource.ByteSourceArray;
 import org.apache.commons.imaging.common.bytesource.ByteSourceFile;
@@ -234,7 +233,7 @@ public class JpegIptcRewriter extends JpegRewriter {
         final List<JFIFPiece> newPieces = removePhotoshopApp13Segments(oldPieces);
         if (!removeSegment && photoshopApp13Segments.size() == 1) {
             final JFIFPieceSegment oldSegment = (JFIFPieceSegment) photoshopApp13Segments.get(0);
-            final Map<String, Object> params = new HashMap<>();
+            final ImagingParameters params = new ImagingParameters();
             final PhotoshopApp13Data oldData = new IptcParser().parsePhotoshopSegment(oldSegment.getSegmentData(), params);
             final List<IptcBlock> newBlocks = oldData.getNonIptcBlocks();
             final List<IptcRecord> newRecords = new ArrayList<>();
diff --git a/src/main/java/org/apache/commons/imaging/formats/jpeg/segments/App13Segment.java b/src/main/java/org/apache/commons/imaging/formats/jpeg/segments/App13Segment.java
index 831f8b3..238a225 100644
--- a/src/main/java/org/apache/commons/imaging/formats/jpeg/segments/App13Segment.java
+++ b/src/main/java/org/apache/commons/imaging/formats/jpeg/segments/App13Segment.java
@@ -19,9 +19,9 @@ package org.apache.commons.imaging.formats.jpeg.segments;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.Map;
 
 import org.apache.commons.imaging.ImageReadException;
+import org.apache.commons.imaging.ImagingParameters;
 import org.apache.commons.imaging.formats.jpeg.JpegImageParser;
 import org.apache.commons.imaging.formats.jpeg.iptc.IptcParser;
 import org.apache.commons.imaging.formats.jpeg.iptc.PhotoshopApp13Data;
@@ -57,7 +57,7 @@ public class App13Segment extends AppnSegment {
         return new IptcParser().isPhotoshopJpegSegment(getSegmentData());
     }
 
-    public PhotoshopApp13Data parsePhotoshopSegment(final Map<String, Object> params)
+    public PhotoshopApp13Data parsePhotoshopSegment(final ImagingParameters params)
             throws ImageReadException, IOException {
         /*
          * In practice, App13 segments are only used for Photoshop/IPTC
diff --git a/src/main/java/org/apache/commons/imaging/formats/pcx/PcxConstants.java b/src/main/java/org/apache/commons/imaging/formats/pcx/PcxConstants.java
index 7884153..0a230eb 100644
--- a/src/main/java/org/apache/commons/imaging/formats/pcx/PcxConstants.java
+++ b/src/main/java/org/apache/commons/imaging/formats/pcx/PcxConstants.java
@@ -16,14 +16,9 @@ package org.apache.commons.imaging.formats.pcx;
 
 
 public final class PcxConstants {
-    public static final String PARAM_KEY_PCX_COMPRESSION = "PCX_COMPRESSION";
     public static final int PCX_COMPRESSION_UNCOMPRESSED = 0;
     public static final int PCX_COMPRESSION_RLE = 1;
 
-    public static final String PARAM_KEY_PCX_BIT_DEPTH = "PCX_BIT_DEPTH";
-
-    public static final String PARAM_KEY_PCX_PLANES = "PCX_PLANES";
-
     private PcxConstants() {
     }
 }
diff --git a/src/main/java/org/apache/commons/imaging/formats/pcx/PcxImageParser.java b/src/main/java/org/apache/commons/imaging/formats/pcx/PcxImageParser.java
index c98cdd4..42b8a7b 100644
--- a/src/main/java/org/apache/commons/imaging/formats/pcx/PcxImageParser.java
+++ b/src/main/java/org/apache/commons/imaging/formats/pcx/PcxImageParser.java
@@ -16,7 +16,6 @@
  */
 package org.apache.commons.imaging.formats.pcx;
 
-import static org.apache.commons.imaging.ImagingConstants.PARAM_KEY_STRICT;
 import static org.apache.commons.imaging.common.BinaryFunctions.readBytes;
 import static org.apache.commons.imaging.common.BinaryFunctions.skipBytes;
 import static org.apache.commons.imaging.common.ByteConversions.toUInt16;
@@ -39,8 +38,6 @@ import java.io.PrintWriter;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
 import java.util.Properties;
 
 import org.apache.commons.imaging.ImageFormat;
@@ -49,11 +46,10 @@ import org.apache.commons.imaging.ImageInfo;
 import org.apache.commons.imaging.ImageParser;
 import org.apache.commons.imaging.ImageReadException;
 import org.apache.commons.imaging.ImageWriteException;
-import org.apache.commons.imaging.common.BaseParameters;
 import org.apache.commons.imaging.common.ImageMetadata;
 import org.apache.commons.imaging.common.bytesource.ByteSource;
 
-public class PcxImageParser extends ImageParser {
+public class PcxImageParser extends ImageParser<PcxImagingParameters> {
     // ZSoft's official spec is at http://www.qzx.com/pc-gpe/pcx.txt
     // (among other places) but it's pretty thin. The fileformat.fine document
     // at http://www.fileformat.fine/format/pcx/egff.htm is a little better
@@ -95,13 +91,13 @@ public class PcxImageParser extends ImageParser {
     }
 
     @Override
-    public ImageMetadata getMetadata(final ByteSource byteSource, final Map<String, Object> params)
+    public ImageMetadata getMetadata(final ByteSource byteSource, final PcxImagingParameters params)
             throws ImageReadException, IOException {
         return null;
     }
 
     @Override
-    public ImageInfo getImageInfo(final ByteSource byteSource, final Map<String, Object> params)
+    public ImageInfo getImageInfo(final ByteSource byteSource, final PcxImagingParameters params)
             throws ImageReadException, IOException {
         final PcxHeader pcxHeader = readPcxHeader(byteSource);
         final Dimension size = getImageSize(byteSource, params);
@@ -128,7 +124,7 @@ public class PcxImageParser extends ImageParser {
     }
 
     @Override
-    public Dimension getImageSize(final ByteSource byteSource, final Map<String, Object> params)
+    public Dimension getImageSize(final ByteSource byteSource, final PcxImagingParameters params)
             throws ImageReadException, IOException {
         final PcxHeader pcxHeader = readPcxHeader(byteSource);
         final int xSize = pcxHeader.xMax - pcxHeader.xMin + 1;
@@ -143,7 +139,7 @@ public class PcxImageParser extends ImageParser {
     }
 
     @Override
-    public byte[] getICCProfileBytes(final ByteSource byteSource, final Map<String, Object> params)
+    public byte[] getICCProfileBytes(final ByteSource byteSource, final PcxImagingParameters params)
             throws ImageReadException, IOException {
         return null;
     }
@@ -475,7 +471,7 @@ public class PcxImageParser extends ImageParser {
 
     @Override
     public final BufferedImage getBufferedImage(final ByteSource byteSource,
-            BaseParameters params) throws ImageReadException, IOException {
+            PcxImagingParameters params) throws ImageReadException, IOException {
         try (InputStream is = byteSource.getInputStream()) {
             final PcxHeader pcxHeader = readPcxHeader(is, params.isStrict());
             return readImage(pcxHeader, is, byteSource);
@@ -483,7 +479,7 @@ public class PcxImageParser extends ImageParser {
     }
 
     @Override
-    public void writeImage(final BufferedImage src, final OutputStream os, final BaseParameters params)
+    public void writeImage(final BufferedImage src, final OutputStream os, final PcxImagingParameters params)
             throws ImageWriteException, IOException {
         new PcxWriter(params).writeImage(src, os);
     }
diff --git a/src/main/java/org/apache/commons/imaging/formats/pcx/PcxImagingParameters.java b/src/main/java/org/apache/commons/imaging/formats/pcx/PcxImagingParameters.java
new file mode 100644
index 0000000..4fdfa6f
--- /dev/null
+++ b/src/main/java/org/apache/commons/imaging/formats/pcx/PcxImagingParameters.java
@@ -0,0 +1,54 @@
+/*
+ *  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.
+ *  under the License.
+ */
+
+package org.apache.commons.imaging.formats.pcx;
+
+import org.apache.commons.imaging.ImagingParameters;
+
+/**
+ * Parameters used by the Pcx format.
+ * @since 1.0-alpha3
+ */
+public class PcxImagingParameters extends ImagingParameters {
+
+    private int planes = -1;
+    private int bitDepth = -1;
+    private int compression = PcxConstants.PCX_COMPRESSION_UNCOMPRESSED;
+
+    public int getPlanes() {
+        return planes;
+    }
+
+    public void setPlanes(int planes) {
+        this.planes = planes;
+    }
+
+    public int getBitDepth() {
+        return bitDepth;
+    }
+
+    public void setBitDepth(int bitDepth) {
+        this.bitDepth = bitDepth;
+    }
+
+    public int getCompression() {
+        return compression;
+    }
+
+    public void setCompression(int compression) {
+        this.compression = compression;
+    }
+
+}
diff --git a/src/main/java/org/apache/commons/imaging/formats/pcx/PcxWriter.java b/src/main/java/org/apache/commons/imaging/formats/pcx/PcxWriter.java
index c89043a..3241ddc 100644
--- a/src/main/java/org/apache/commons/imaging/formats/pcx/PcxWriter.java
+++ b/src/main/java/org/apache/commons/imaging/formats/pcx/PcxWriter.java
@@ -20,12 +20,9 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.ByteOrder;
 import java.util.Arrays;
-import java.util.HashMap;
 
 import org.apache.commons.imaging.ImageWriteException;
-import org.apache.commons.imaging.ImagingConstants;
 import org.apache.commons.imaging.PixelDensity;
-import org.apache.commons.imaging.common.BaseParameters;
 import org.apache.commons.imaging.common.BinaryOutputStream;
 import org.apache.commons.imaging.palette.PaletteFactory;
 import org.apache.commons.imaging.palette.SimplePalette;
@@ -37,22 +34,13 @@ class PcxWriter {
     private PixelDensity pixelDensity;
     private final RleWriter rleWriter;
 
-    PcxWriter(BaseParameters params) throws ImageWriteException {
+    PcxWriter(PcxImagingParameters params) throws ImageWriteException {
         // uncompressed PCX files are not even documented in ZSoft's spec,
         // let alone supported by most image viewers
         encoding = PcxImageParser.PcxHeader.ENCODING_RLE;
-        if (params.containsKey(PcxConstants.PARAM_KEY_PCX_COMPRESSION)) {
-            final Object value = params.remove(PcxConstants.PARAM_KEY_PCX_COMPRESSION);
-            if (value != null) {
-                if (!(value instanceof Number)) {
-                    throw new ImageWriteException(
-                            "Invalid compression parameter: " + value);
-                }
-                final int compression = ((Number) value).intValue();
-                if (compression == PcxConstants.PCX_COMPRESSION_UNCOMPRESSED) {
-                    encoding = PcxImageParser.PcxHeader.ENCODING_UNCOMPRESSED;
-                }
-            }
+        final int compression = params.getCompression();
+        if (compression == PcxConstants.PCX_COMPRESSION_UNCOMPRESSED) {
+            encoding = PcxImageParser.PcxHeader.ENCODING_UNCOMPRESSED;
         }
         if (encoding == PcxImageParser.PcxHeader.ENCODING_UNCOMPRESSED) {
             rleWriter = new RleWriter(false);
@@ -60,47 +48,13 @@ class PcxWriter {
             rleWriter = new RleWriter(true);
         }
 
-        if (params.containsKey(PcxConstants.PARAM_KEY_PCX_BIT_DEPTH)) {
-            final Object value = params.remove(PcxConstants.PARAM_KEY_PCX_BIT_DEPTH);
-            if (value != null) {
-                if (!(value instanceof Number)) {
-                    throw new ImageWriteException(
-                            "Invalid bit depth parameter: " + value);
-                }
-                bitDepthWanted = ((Number) value).intValue();
-            }
-        }
-
-        if (params.containsKey(PcxConstants.PARAM_KEY_PCX_PLANES)) {
-            final Object value = params.remove(PcxConstants.PARAM_KEY_PCX_PLANES);
-            if (value != null) {
-                if (!(value instanceof Number)) {
-                    throw new ImageWriteException(
-                            "Invalid planes parameter: " + value);
-                }
-                planesWanted = ((Number) value).intValue();
-            }
-        }
-
-        if (params.containsKey(ImagingConstants.PARAM_KEY_PIXEL_DENSITY)) {
-            final Object value = params.remove(ImagingConstants.PARAM_KEY_PIXEL_DENSITY);
-            if (value != null) {
-                if (!(value instanceof PixelDensity)) {
-                    throw new ImageWriteException(
-                            "Invalid pixel density parameter");
-                }
-                pixelDensity = (PixelDensity) value;
-            }
-        }
+        bitDepthWanted = params.getBitDepth();
+        planesWanted = params.getPlanes();
+        pixelDensity = params.getPixelDensity();
         if (pixelDensity == null) {
             // DPI is mandatory, so we have to invent something
             pixelDensity = PixelDensity.createFromPixelsPerInch(72, 72);
         }
-
-        if (!params.isEmpty()) {
-            final Object firstKey = params.keySet().iterator().next();
-            throw new ImageWriteException("Unknown parameter: " + firstKey);
-        }
     }
 
     public void writeImage(final BufferedImage src, final OutputStream os)
diff --git a/src/main/java/org/apache/commons/imaging/formats/pnm/PamWriter.java b/src/main/java/org/apache/commons/imaging/formats/pnm/PamWriter.java
index 7ed25cb..5717d00 100644
--- a/src/main/java/org/apache/commons/imaging/formats/pnm/PamWriter.java
+++ b/src/main/java/org/apache/commons/imaging/formats/pnm/PamWriter.java
@@ -21,7 +21,6 @@ import java.awt.image.BufferedImage;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
-import java.util.Map;
 
 import org.apache.commons.imaging.ImageWriteException;
 
@@ -29,7 +28,7 @@ class PamWriter implements PnmWriter {
 
     @Override
     public void writeImage(final BufferedImage src, final OutputStream os,
-            final Map<String, Object> params) throws ImageWriteException, IOException {
+            final PnmImagingParameters params) throws ImageWriteException, IOException {
 
         os.write(PnmConstants.PNM_PREFIX_BYTE);
         os.write(PnmConstants.PAM_RAW_CODE);
diff --git a/src/main/java/org/apache/commons/imaging/formats/pnm/PbmWriter.java b/src/main/java/org/apache/commons/imaging/formats/pnm/PbmWriter.java
index 22119c2..0548181 100644
--- a/src/main/java/org/apache/commons/imaging/formats/pnm/PbmWriter.java
+++ b/src/main/java/org/apache/commons/imaging/formats/pnm/PbmWriter.java
@@ -20,7 +20,6 @@ import java.awt.image.BufferedImage;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
-import java.util.Map;
 
 import org.apache.commons.imaging.ImageWriteException;
 
@@ -33,7 +32,7 @@ class PbmWriter implements PnmWriter {
     }
 
     @Override
-    public void writeImage(final BufferedImage src, final OutputStream os, final Map<String, Object> params)
+    public void writeImage(final BufferedImage src, final OutputStream os, final PnmImagingParameters params)
             throws ImageWriteException, IOException {
         os.write(PnmConstants.PNM_PREFIX_BYTE);
         os.write(rawBits ? PnmConstants.PBM_RAW_CODE : PnmConstants.PBM_TEXT_CODE);
diff --git a/src/main/java/org/apache/commons/imaging/formats/pnm/PgmWriter.java b/src/main/java/org/apache/commons/imaging/formats/pnm/PgmWriter.java
index 561b6db..44049a1 100644
--- a/src/main/java/org/apache/commons/imaging/formats/pnm/PgmWriter.java
+++ b/src/main/java/org/apache/commons/imaging/formats/pnm/PgmWriter.java
@@ -20,7 +20,6 @@ import java.awt.image.BufferedImage;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
-import java.util.Map;
 
 import org.apache.commons.imaging.ImageWriteException;
 
@@ -33,7 +32,7 @@ class PgmWriter implements PnmWriter {
     }
 
     @Override
-    public void writeImage(final BufferedImage src, final OutputStream os, final Map<String, Object> params)
+    public void writeImage(final BufferedImage src, final OutputStream os, final PnmImagingParameters params)
             throws ImageWriteException, IOException {
         // System.out.println
         // (b1 == 0x50 && b2 == 0x36)
diff --git a/src/main/java/org/apache/commons/imaging/formats/pnm/PnmImageParser.java b/src/main/java/org/apache/commons/imaging/formats/pnm/PnmImageParser.java
index bcba04a..5dc2577 100644
--- a/src/main/java/org/apache/commons/imaging/formats/pnm/PnmImageParser.java
+++ b/src/main/java/org/apache/commons/imaging/formats/pnm/PnmImageParser.java
@@ -16,7 +16,6 @@
  */
 package org.apache.commons.imaging.formats.pnm;
 
-import static org.apache.commons.imaging.ImagingConstants.PARAM_KEY_FORMAT;
 import static org.apache.commons.imaging.common.BinaryFunctions.readByte;
 
 import java.awt.Dimension;
@@ -27,9 +26,7 @@ import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.StringTokenizer;
 
 import org.apache.commons.imaging.ImageFormat;
@@ -43,7 +40,7 @@ import org.apache.commons.imaging.common.ImageMetadata;
 import org.apache.commons.imaging.common.bytesource.ByteSource;
 import org.apache.commons.imaging.palette.PaletteFactory;
 
-public class PnmImageParser extends ImageParser {
+public class PnmImageParser extends ImageParser<PnmImagingParameters> {
     private static final String DEFAULT_EXTENSION = ".pnm";
     private static final String[] ACCEPTED_EXTENSIONS = { ".pbm", ".pgm",
             ".ppm", ".pnm", ".pam" };
@@ -223,13 +220,13 @@ public class PnmImageParser extends ImageParser {
     }
 
     @Override
-    public byte[] getICCProfileBytes(final ByteSource byteSource, final Map<String, Object> params)
+    public byte[] getICCProfileBytes(final ByteSource byteSource, final PnmImagingParameters params)
             throws ImageReadException, IOException {
         return null;
     }
 
     @Override
-    public Dimension getImageSize(final ByteSource byteSource, final Map<String, Object> params)
+    public Dimension getImageSize(final ByteSource byteSource, final PnmImagingParameters params)
             throws ImageReadException, IOException {
         final FileInfo info = readHeader(byteSource);
 
@@ -237,13 +234,13 @@ public class PnmImageParser extends ImageParser {
     }
 
     @Override
-    public ImageMetadata getMetadata(final ByteSource byteSource, final Map<String, Object> params)
+    public ImageMetadata getMetadata(final ByteSource byteSource, final PnmImagingParameters params)
             throws ImageReadException, IOException {
         return null;
     }
 
     @Override
-    public ImageInfo getImageInfo(final ByteSource byteSource, final Map<String, Object> params)
+    public ImageInfo getImageInfo(final ByteSource byteSource, final PnmImagingParameters params)
             throws ImageReadException, IOException {
         final FileInfo info = readHeader(byteSource);
 
@@ -296,7 +293,7 @@ public class PnmImageParser extends ImageParser {
     }
 
     @Override
-    public BufferedImage getBufferedImage(final ByteSource byteSource, final Map<String, Object> params)
+    public BufferedImage getBufferedImage(final ByteSource byteSource, final PnmImagingParameters params)
             throws ImageReadException, IOException {
         try (InputStream is = byteSource.getInputStream()) {
             final FileInfo info = readHeader(is);
@@ -314,20 +311,15 @@ public class PnmImageParser extends ImageParser {
     }
 
     @Override
-    public void writeImage(final BufferedImage src, final OutputStream os, Map<String, Object> params)
+    public void writeImage(final BufferedImage src, final OutputStream os, PnmImagingParameters params)
             throws ImageWriteException, IOException {
         PnmWriter writer = null;
         boolean useRawbits = true;
 
         if (params != null) {
-            final Object useRawbitsParam = params.get(PARAM_KEY_PNM_RAWBITS);
-            if (useRawbitsParam != null) {
-                if (useRawbitsParam.equals(PARAM_VALUE_PNM_RAWBITS_NO)) {
-                    useRawbits = false;
-                }
-            }
+            useRawbits = params.isRawBits();
 
-            final Object subtype = params.get(PARAM_KEY_FORMAT);
+            final ImageFormats subtype = params.getSubtype();
             if (subtype != null) {
                 if (subtype.equals(ImageFormats.PBM)) {
                     writer = new PbmWriter(useRawbits);
@@ -350,28 +342,6 @@ public class PnmImageParser extends ImageParser {
             }
         }
 
-        // make copy of params; we'll clear keys as we consume them.
-        if (params != null) {
-            params = new HashMap<>(params);
-        } else {
-            params = new HashMap<>();
-        }
-
-        // clear format key.
-        if (params.containsKey(PARAM_KEY_FORMAT)) {
-            params.remove(PARAM_KEY_FORMAT);
-        }
-
-        // clear rawbits key.
-        if (params.containsKey(PARAM_KEY_PNM_RAWBITS)) {
-            params.remove(PARAM_KEY_PNM_RAWBITS);
-        }
-
-        if (!params.isEmpty()) {
-            final Object firstKey = params.keySet().iterator().next();
-            throw new ImageWriteException("Unknown parameter: " + firstKey);
-        }
-
         writer.writeImage(src, os, params);
     }
 }
diff --git a/src/main/java/org/apache/commons/imaging/formats/pnm/PnmWriter.java b/src/main/java/org/apache/commons/imaging/formats/pnm/PnmImagingParameters.java
similarity index 56%
copy from src/main/java/org/apache/commons/imaging/formats/pnm/PnmWriter.java
copy to src/main/java/org/apache/commons/imaging/formats/pnm/PnmImagingParameters.java
index 5a73362..68f1e7c 100644
--- a/src/main/java/org/apache/commons/imaging/formats/pnm/PnmWriter.java
+++ b/src/main/java/org/apache/commons/imaging/formats/pnm/PnmImagingParameters.java
@@ -16,16 +16,34 @@
  */
 package org.apache.commons.imaging.formats.pnm;
 
-import java.awt.image.BufferedImage;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Map;
+import org.apache.commons.imaging.ImageFormats;
+import org.apache.commons.imaging.ImagingParameters;
 
-import org.apache.commons.imaging.ImageWriteException;
+/**
+ * Pnm format parameters.
+ * @since 1.0-alpha3
+ */
+public class PnmImagingParameters extends ImagingParameters {
+
+    private boolean rawBits = true;
+    /**
+     * Pnm format subtype (e.g. pam, pbm, etc).
+     */
+    private ImageFormats subtype = null;
+
+    public boolean isRawBits() {
+        return rawBits;
+    }
 
-interface PnmWriter {
+    public void setRawBits(boolean rawBits) {
+        this.rawBits = rawBits;
+    }
 
-    void writeImage(BufferedImage src, OutputStream os,
-            Map<String, Object> params) throws ImageWriteException, IOException;
+    public ImageFormats getSubtype() {
+        return subtype;
+    }
 
+    public void setSubtype(ImageFormats subtype) {
+        this.subtype = subtype;
+    }
 }
diff --git a/src/main/java/org/apache/commons/imaging/formats/pnm/PnmWriter.java b/src/main/java/org/apache/commons/imaging/formats/pnm/PnmWriter.java
index 5a73362..952cc9a 100644
--- a/src/main/java/org/apache/commons/imaging/formats/pnm/PnmWriter.java
+++ b/src/main/java/org/apache/commons/imaging/formats/pnm/PnmWriter.java
@@ -19,13 +19,12 @@ package org.apache.commons.imaging.formats.pnm;
 import java.awt.image.BufferedImage;
 import java.io.IOException;
 import java.io.OutputStream;
-import java.util.Map;
 
 import org.apache.commons.imaging.ImageWriteException;
 
 interface PnmWriter {
 
     void writeImage(BufferedImage src, OutputStream os,
-            Map<String, Object> params) throws ImageWriteException, IOException;
+            PnmImagingParameters params) throws ImageWriteException, IOException;
 
 }
diff --git a/src/main/java/org/apache/commons/imaging/formats/pnm/PpmWriter.java b/src/main/java/org/apache/commons/imaging/formats/pnm/PpmWriter.java
index 53aa6f7..74525c9 100644
--- a/src/main/java/org/apache/commons/imaging/formats/pnm/PpmWriter.java
+++ b/src/main/java/org/apache/commons/imaging/formats/pnm/PpmWriter.java
@@ -20,7 +20,6 @@ import java.awt.image.BufferedImage;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
-import java.util.Map;
 
 import org.apache.commons.imaging.ImageWriteException;
 
@@ -33,7 +32,7 @@ class PpmWriter implements PnmWriter {
     }
 
     @Override
-    public void writeImage(final BufferedImage src, final OutputStream os, final Map<String, Object> params)
+    public void writeImage(final BufferedImage src, final OutputStream os, final PnmImagingParameters params)
             throws ImageWriteException, IOException {
         // System.out.println
         // (b1 == 0x50 && b2 == 0x36)
diff --git a/src/main/java/org/apache/commons/imaging/formats/xpm/XpmImageParser.java b/src/main/java/org/apache/commons/imaging/formats/xpm/XpmImageParser.java
index a625923..24ccf4c 100644
--- a/src/main/java/org/apache/commons/imaging/formats/xpm/XpmImageParser.java
+++ b/src/main/java/org/apache/commons/imaging/formats/xpm/XpmImageParser.java
@@ -45,14 +45,13 @@ import org.apache.commons.imaging.ImageInfo;
 import org.apache.commons.imaging.ImageParser;
 import org.apache.commons.imaging.ImageReadException;
 import org.apache.commons.imaging.ImageWriteException;
-import org.apache.commons.imaging.common.BaseParameters;
 import org.apache.commons.imaging.common.BasicCParser;
 import org.apache.commons.imaging.common.ImageMetadata;
 import org.apache.commons.imaging.common.bytesource.ByteSource;
 import org.apache.commons.imaging.palette.PaletteFactory;
 import org.apache.commons.imaging.palette.SimplePalette;
 
-public class XpmImageParser extends ImageParser {
+public class XpmImageParser extends ImageParser<XpmImagingParameters> {
     private static final String DEFAULT_EXTENSION = ".xpm";
     private static final String[] ACCEPTED_EXTENSIONS = { ".xpm", };
     private static Map<String, Integer> colorNames;
@@ -126,13 +125,13 @@ public class XpmImageParser extends ImageParser {
     }
 
     @Override
-    public ImageMetadata getMetadata(final ByteSource byteSource, final BaseParameters params)
+    public ImageMetadata getMetadata(final ByteSource byteSource, final XpmImagingParameters params)
             throws ImageReadException, IOException {
         return null;
     }
 
     @Override
-    public ImageInfo getImageInfo(final ByteSource byteSource, final BaseParameters params)
+    public ImageInfo getImageInfo(final ByteSource byteSource, final XpmImagingParameters params)
             throws ImageReadException, IOException {
         final XpmHeader xpmHeader = readXpmHeader(byteSource);
         boolean transparent = false;
@@ -157,14 +156,14 @@ public class XpmImageParser extends ImageParser {
     }
 
     @Override
-    public Dimension getImageSize(final ByteSource byteSource, final BaseParameters params)
+    public Dimension getImageSize(final ByteSource byteSource, final XpmImagingParameters params)
             throws ImageReadException, IOException {
         final XpmHeader xpmHeader = readXpmHeader(byteSource);
         return new Dimension(xpmHeader.width, xpmHeader.height);
     }
 
     @Override
-    public byte[] getICCProfileBytes(final ByteSource byteSource, final BaseParameters params)
+    public byte[] getICCProfileBytes(final ByteSource byteSource, final XpmImagingParameters params)
             throws ImageReadException, IOException {
         return null;
     }
@@ -597,7 +596,7 @@ public class XpmImageParser extends ImageParser {
 
     @Override
     public final BufferedImage getBufferedImage(final ByteSource byteSource,
-            final BaseParameters params) throws ImageReadException, IOException {
+            final XpmImagingParameters params) throws ImageReadException, IOException {
         final XpmParseResult result = parseXpmHeader(byteSource);
         return readXpmImage(result.xpmHeader, result.cParser);
     }
@@ -643,7 +642,7 @@ public class XpmImageParser extends ImageParser {
     }
 
     @Override
-    public void writeImage(final BufferedImage src, final OutputStream os, BaseParameters params)
+    public void writeImage(final BufferedImage src, final OutputStream os, XpmImagingParameters params)
             throws ImageWriteException, IOException {
         final PaletteFactory paletteFactory = new PaletteFactory();
         final boolean hasTransparency = paletteFactory.hasTransparency(src, 1);
diff --git a/src/main/java/org/apache/commons/imaging/formats/pcx/PcxConstants.java b/src/main/java/org/apache/commons/imaging/formats/xpm/XpmImagingParameters.java
similarity index 54%
copy from src/main/java/org/apache/commons/imaging/formats/pcx/PcxConstants.java
copy to src/main/java/org/apache/commons/imaging/formats/xpm/XpmImagingParameters.java
index 7884153..36be857 100644
--- a/src/main/java/org/apache/commons/imaging/formats/pcx/PcxConstants.java
+++ b/src/main/java/org/apache/commons/imaging/formats/xpm/XpmImagingParameters.java
@@ -10,20 +10,14 @@
  *  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.
- *  under the License.
  */
-package org.apache.commons.imaging.formats.pcx;
 
+package org.apache.commons.imaging.formats.xpm;
 
-public final class PcxConstants {
-    public static final String PARAM_KEY_PCX_COMPRESSION = "PCX_COMPRESSION";
-    public static final int PCX_COMPRESSION_UNCOMPRESSED = 0;
-    public static final int PCX_COMPRESSION_RLE = 1;
+import org.apache.commons.imaging.ImagingParameters;
 
-    public static final String PARAM_KEY_PCX_BIT_DEPTH = "PCX_BIT_DEPTH";
-
-    public static final String PARAM_KEY_PCX_PLANES = "PCX_PLANES";
-
-    private PcxConstants() {
-    }
-}
+/**
+ * Xpm format parameters.
+ * @since 1.0-alpha3
+ */
+public class XpmImagingParameters extends ImagingParameters {}