Java を使用したプレゼンテーションで画像変換エフェクトを管理する

概要

Aspose.Slides は画像変換操作の順序付けられたコレクションとして画像調整を表現します。画像フレームの場合は、フレームの ISlidesPicture を取得し、ISlidesPicture.getImageTransform にアクセスします。返される IImageTransformOperationCollection を使用すると、元の画像バイト列を書き換えることなく、エフェクトの追加、列挙、検査、削除、クリアが可能です。

この記事では、明るさとコントラスト、カラー変換、ぼかし、透明度、順序付けられたエフェクトチェーン、実効値、削除、そして PPTX のラウンドトリップ検証の完全なワークフローを示します。

エフェクト所有権と画像の再利用の理解

画像リソースとそれを表示する画像は別々のオブジェクトです。

  • IPPImage はプレゼンテーションが所有するソース画像データを保持または参照します。
  • ISlidesPicture は画像塗りつぶしに属し、画像リソースを参照しつつ画像変換コレクションを保持します。
  • IPictureFrame はスライド上のシェイプで、関連する画像塗りつぶし、ジオメトリ、トリミング設定、その他フレームレベルの書式設定を所有します。

したがって、画像変換操作は IPPImage のバイト列を変更しません。同じ IPPImageIShapeCollection.addPictureFrame に複数回渡すと、各新しい画像フレームは独自の ISlidesPicture と独自の変換コレクションを受け取ります。あるフレームにグレースケールを適用しても、他のフレームがグレースケールになることはありません。すべてのフレームが同じ埋め込み画像リソースを再利用しているからです。

同じ ISlidesPicture.getImageTransform モデルは、シェイプやスライド背景など他の画像塗りつぶしでも使用されます。以下の例は画像フレームに焦点を当てています。

有効なパラメーター範囲と単位の使用

示されたメソッドは以下の意味的な範囲と単位を使用します。特定のライブラリ バージョンがすべての範囲外値を直ちに拒否しなくても、これらの範囲内に値を保ってください。保存時や PowerPoint がファイルを開く際に、対象のプレゼンテーション形式が正規化、除外、あるいは無効データを拒否する可能性があります。

操作 パラメーター 有効範囲と単位
addBrightnessContrastEffect brightness, contrast -100100、パーセント;0 は変更なし
addGrayScaleEffect なし 数値パラメーターなし。アルファは変更しません
addDuotoneEffect color1, color2 暗部と明部の 2 色。java.awt.Color の RGB とアルファは 0255
addTintEffect hue, amount 色相は 0(含む)〜360(除く)度、amount-100100 パーセント
addHSLEffect hue, saturation, luminance 色相は 0(含む)〜360(除く)度、彩度と輝度は -100100 パーセント
addColorReplaceEffect color 置換色は各チャンネルが 0255。既存のアルファは変更しません
addBlurEffect radius, grow 半径は非負でポイント単位、grow はブラー領域が元の境界を超えるかを制御するブール値
addAlphaModulateFixedEffect amount 非負パーセント。通常の不透明度スケーリングは 01000 は完全に透明、100 は既存アルファを保持)
addAlphaReplaceEffect alpha 0100 パーセントの不透明度
addAlphaBiLevelEffect threshold 0100 パーセントのアルファ閾値。閾値未満は透明、以上は不透明

固定アルファ変調の場合、透明度と不透明度は補完関係にあります。たとえば 35% の透明度はアルファ変調量 65% に相当します。

明るさとコントラストの適用

IImageTransformOperationCollection.addBrightnessContrastEffectIBrightnessContrast 操作を返します。スカラー設定は操作作成時に供給されます。IBrightnessContrast.getEffective は計算済みの読み取り専用値を返し、検査やログ出力に利用できます。

次の例は明るさを 15% 、コントラストを 20% 増加させ、埋め込み画像を変更せずにプレビューを描画します。

import com.aspose.slides.*;
import java.nio.file.Files;
import java.nio.file.Paths;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);

    byte[] imageData = Files.readAllBytes(Paths.get("photo.png"));
    IPPImage image = presentation.getImages().addImage(imageData);
    IPictureFrame pictureFrame = slide.getShapes().addPictureFrame(ShapeType.Rectangle, 50, 50, 400, 260, image);

    IImageTransformOperationCollection imageTransform = pictureFrame.getPictureFormat().getPicture().getImageTransform();
    IBrightnessContrast brightnessContrast = imageTransform.addBrightnessContrastEffect(15f, 20f);

    IBrightnessContrastEffectiveData effectiveValues = brightnessContrast.getEffective();
    System.out.println("Brightness: " + effectiveValues.getBrightness() + "%");
    System.out.println("Contrast: " + effectiveValues.getContrast() + "%");

    IImage preview = slide.getImage();
    try {
        preview.save("brightness-contrast-preview.png", ImageFormat.Png);
    } finally {
        preview.dispose();
    }
} finally {
    presentation.dispose();
}

BrightnessContrast は Office 2010 の画像エフェクト拡張であり、標準の DrawingML 輝度エフェクトほど移植性が高くありません。明るさとコントラストを PPTX のラウンドトリップ後も編集可能に保ちたい場合は、IImageTransformOperationCollection.addLuminanceEffect を使用し、再度ファイルを開いたときに結果を検証してください。フォーマット制限のセクションでこの違いを詳しく説明しています。

カラー変換の適用

カラーエフェクトは、同一画像リソースを再利用する複数の画像フレームに対して個別に適用できます。次の例は 5 つのフレームを作成し、グレースケール、デュオトーン、ティント、HSL 調整、カラー置換を適用します。

IDuotone には 2 つの独立した編集可能カラー パラメーターがあります: color1 が暗部ピクセルに、color2 が明部ピクセルにマッピングされます。これは単一スカラー値よりも設定が複雑なエフェクトの有用な例です。

import com.aspose.slides.*;
import java.awt.Color;
import java.nio.file.Files;
import java.nio.file.Paths;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);

    byte[] imageData = Files.readAllBytes(Paths.get("photo.png"));
    IPPImage image = presentation.getImages().addImage(imageData);

    IPictureFrame grayFrame = slide.getShapes().addPictureFrame(ShapeType.Rectangle, 20, 20, 180, 120, image);
    grayFrame.getPictureFormat().getPicture().getImageTransform().addGrayScaleEffect();

    IPictureFrame duotoneFrame = slide.getShapes().addPictureFrame(ShapeType.Rectangle, 220, 20, 180, 120, image);
    IDuotone duotone = duotoneFrame.getPictureFormat().getPicture().getImageTransform().addDuotoneEffect();
    duotone.getColor1().setColor(new Color(0, 0, 128));
    duotone.getColor2().setColor(new Color(255, 215, 0));

    IPictureFrame tintFrame = slide.getShapes().addPictureFrame(ShapeType.Rectangle, 420, 20, 180, 120, image);
    tintFrame.getPictureFormat().getPicture().getImageTransform().addTintEffect(210f, 35f);

    IPictureFrame hslFrame = slide.getShapes().addPictureFrame(ShapeType.Rectangle, 120, 170, 180, 120, image);
    hslFrame.getPictureFormat().getPicture().getImageTransform().addHSLEffect(30f, 20f, -10f);

    IPictureFrame replacementFrame = slide.getShapes().addPictureFrame(ShapeType.Rectangle, 320, 170, 180, 120, image);
    IColorReplace colorReplacement = replacementFrame.getPictureFormat().getPicture().getImageTransform().addColorReplaceEffect();
    colorReplacement.getColor().setColor(new Color(100, 149, 237));

    presentation.save("color-transformations.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

addColorReplaceEffect はすべてのピクセルの色を固定色に置き換え、アルファは保持します。これは addColorChangeEffect(ソース色を別の色にマッピングし、両方のカラー形式を公開)とは異なります。

ぼかし、透明度、アルファ効果の追加

addBlurEffect はすべてのカラー チャンネル(アルファ含む)に影響します。ぼかしエッジが元画像の境界を超える可能性がある場合は growtrue に設定してください。

均一な透明度を実現するには addAlphaModulateFixedEffect を使用します。既存のアルファ値をすべて乗算するため、部分的に透明なピクセルは比例的に異なるまま残ります。addAlphaReplaceEffect はすべてのピクセルに同一アルファ値を割り当て、addAlphaBiLevelEffect は閾値に基づいてアルファを 2 レベルに変換します。

import com.aspose.slides.*;
import java.nio.file.Files;
import java.nio.file.Paths;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);

    byte[] imageData = Files.readAllBytes(Paths.get("photo.png"));
    IPPImage image = presentation.getImages().addImage(imageData);

    IPictureFrame blurredFrame = slide.getShapes().addPictureFrame(ShapeType.Rectangle, 20, 20, 200, 140, image);
    IBlur blur = blurredFrame.getPictureFormat().getPicture().getImageTransform().addBlurEffect(4.5, true);
    blur.setRadius(5);

    IPictureFrame transparentFrame = slide.getShapes().addPictureFrame(ShapeType.Rectangle, 240, 20, 200, 140, image);
    IAlphaModulateFixed alphaModulate = transparentFrame.getPictureFormat().getPicture().getImageTransform().addAlphaModulateFixedEffect(65f);
    alphaModulate.setAmount(60f);

    IPictureFrame uniformAlphaFrame = slide.getShapes().addPictureFrame(ShapeType.Rectangle, 20, 180, 200, 140, image);
    uniformAlphaFrame.getPictureFormat().getPicture().getImageTransform().addAlphaReplaceEffect(55f);

    IPictureFrame binaryAlphaFrame = slide.getShapes().addPictureFrame(ShapeType.Rectangle, 240, 180, 200, 140, image);
    IAlphaBiLevel alphaBiLevel = binaryAlphaFrame.getPictureFormat().getPicture().getImageTransform().addAlphaBiLevelEffect(50f);
    alphaBiLevel.setThreshold(45f);
    binaryAlphaFrame.getPictureFormat().getPicture().getImageTransform().addAlphaInverseEffect();

    presentation.save("blur-and-alpha-effects.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

パラメーターなしの他のアルファ操作には、すべての非ゼロアルファを完全に不透明にする addAlphaCeilingEffect、アルファが 100% 未満のものを完全に透明にする addAlphaFloorEffect、および 100% - alpha に変換する addAlphaInverseEffect があります。

順序付けられたエフェクトチェーンの構築

すべての add...Effect メソッドは新しい操作をコレクションの末尾に追加します。レンダラはこのコレクションを順序付けられたパイプラインとして使用し、操作 0 の出力が操作 1 の入力となります。そのため、同じ操作でも順序が異なると異なる画像が生成されます。

例として、グレースケールの後にティントを適用すると最初に色相情報が除去され、続いて輝度結果に再着色されます。逆にティントの後にグレースケールを適用するとティントが再び除去されます。同様に、アルファ置換は以前の操作で計算されたアルファ値を上書きし、アルファ変調は相対的な差を保持します。

次の例は 4 つの操作からなるチェーンを構築し、PPTX として保存した後にプレゼンテーションを再度開き、操作のタイプと順序を確認し、再オープンした結果をレンダリングします。

import com.aspose.slides.*;
import java.nio.file.Files;
import java.nio.file.Paths;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);
    byte[] imageData = Files.readAllBytes(Paths.get("photo.png"));
    IPPImage image = presentation.getImages().addImage(imageData);
    IPictureFrame pictureFrame = slide.getShapes().addPictureFrame(ShapeType.Rectangle, 50, 50, 400, 260, image);

    IImageTransformOperationCollection imageTransform = pictureFrame.getPictureFormat().getPicture().getImageTransform();
    imageTransform.addGrayScaleEffect();
    imageTransform.addTintEffect(220f, 25f);
    imageTransform.addBlurEffect(2.5, false);
    imageTransform.addAlphaModulateFixedEffect(80f);

    presentation.save("image-transform-chain.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

Presentation reopenedPresentation = new Presentation("image-transform-chain.pptx");
try {
    IShape reopenedShape = reopenedPresentation.getSlides().get_Item(0).getShapes().get_Item(0);

    if (reopenedShape instanceof IPictureFrame) {
        IPictureFrame reopenedFrame = (IPictureFrame) reopenedShape;
        IImageTransformOperationCollection reopenedTransform = reopenedFrame.getPictureFormat().getPicture().getImageTransform();
        boolean orderIsPreserved = reopenedTransform.size() == 4 && 
                reopenedTransform.get_Item(0) instanceof IGrayScale && 
                reopenedTransform.get_Item(1) instanceof ITint && 
                reopenedTransform.get_Item(2) instanceof IBlur && 
                reopenedTransform.get_Item(3) instanceof IAlphaModulateFixed;
        System.out.println(orderIsPreserved ? "The effect chain was preserved." : "The effect chain changed during the round trip.");

        IImage renderedSlide = reopenedPresentation.getSlides().get_Item(0).getImage();
        try {
            renderedSlide.save("reopened-effect-chain.png", ImageFormat.Png);
        } finally {
            renderedSlide.dispose();
        }
    } else {
        System.out.println("The reopened shape is not a picture frame.");
    }
} finally {
    reopenedPresentation.dispose();
}

コレクションはカラー、アルファ、ぼかし操作を別々のチェーンに制限する互換性マトリックスを課しません。組み合わせは可能ですが、常に有用とは限りません。たとえば固定カラー置換は以前のカラーエフェクトで生成された RGB のばらつきを除去し、デュオトーンの後にグレースケールを適用すると 2 つの選択色が失われます。アルファの天井、床、置換、二段階操作は以前に作成されたアルファのディテールを破棄する可能性があります。チェーンは「順序付けられたピクセル処理シーケンス」に基づいて構築し、無秩序な書式フラグとして扱わないでください。

編集可能値と実効値の検査

編集可能な操作は ISlidesPicture.getImageTransform に格納されているオブジェクトです。エフェクトに応じて書き込み可能メンバーが直接公開されます。例として、IBlur は書き込み可能な radiusgrow を、IAlphaModulateFixed は書き込み可能な amount を、IAlphaBiLevel は書き込み可能な threshold を公開します。IDuotone のようなカラーエフェクトは変更可能な IColorFormat オブジェクトを公開します。

IBrightnessContrastIHSLITintIAlphaReplace などのインターフェイスは作成時のスカラーを書き込み可能プロパティとして公開しません。設定を変更するには、該当操作を削除し、目的の位置に新しい操作を追加してください。

getEffective() が返す実効データは計算済みで読み取り専用です。テーマ依存カラーの解決やレンダラが使用する正規化値の取得に有用ですが、別の編集対象ではありません。以下の例はチェーンを列挙し、対応する API が提供する実効値を検査します。

import com.aspose.slides.*;

Presentation presentation = new Presentation("image-transform-chain.pptx");
try {
    IPictureFrame pictureFrame = null;

    for (IShape shape : presentation.getSlides().get_Item(0).getShapes()) {
        if (shape instanceof IPictureFrame) {
            pictureFrame = (IPictureFrame) shape;
            break;
        }
    }

    if (pictureFrame != null) {
        IImageTransformOperationCollection imageTransform = pictureFrame.getPictureFormat().getPicture().getImageTransform();

        for (int index = 0; index < imageTransform.size(); index++) {
            IImageTransformOperation operation = imageTransform.get_Item(index);
            System.out.println(index + ": " + operation.getClass().getSimpleName());

            if (operation instanceof IBrightnessContrast) {
                IBrightnessContrastEffectiveData data = ((IBrightnessContrast) operation).getEffective();
                System.out.println("  Brightness: " + data.getBrightness());
                System.out.println("  Contrast: " + data.getContrast());
            } else if (operation instanceof ILuminance) {
                ILuminanceEffectiveData data = ((ILuminance) operation).getEffective();
                System.out.println("  Brightness: " + data.getBrightness());
                System.out.println("  Contrast: " + data.getContrast());
            } else if (operation instanceof IDuotone) {
                IDuotoneEffectiveData data = ((IDuotone) operation).getEffective();
                System.out.println("  Dark color: " + data.getColor1());
                System.out.println("  Light color: " + data.getColor2());
            } else if (operation instanceof IColorReplace) {
                IColorReplaceEffectiveData data = ((IColorReplace) operation).getEffective();
                System.out.println("  Replacement color: " + data.getColor());
            } else if (operation instanceof IHSL) {
                IHSLEffectiveData data = ((IHSL) operation).getEffective();
                System.out.println("  HSL: " + data.getHue() + ", " + data.getSaturation() + ", " + data.getLuminance());
            } else if (operation instanceof ITint) {
                ITintEffectiveData data = ((ITint) operation).getEffective();
                System.out.println("  Tint: " + data.getHue() + ", " + data.getAmount());
            } else if (operation instanceof IBlur) {
                IBlurEffectiveData data = ((IBlur) operation).getEffective();
                System.out.println("  Blur radius: " + data.getRadius() + " pt");
            } else if (operation instanceof IAlphaModulateFixed) {
                IAlphaModulateFixedEffectiveData data = ((IAlphaModulateFixed) operation).getEffective();
                System.out.println("  Alpha amount: " + data.getAmount() + "%");
            } else if (operation instanceof IAlphaReplace) {
                IAlphaReplaceEffectiveData data = ((IAlphaReplace) operation).getEffective();
                System.out.println("  Replacement alpha: " + data.getAlpha() + "%");
            } else if (operation instanceof IAlphaBiLevel) {
                IAlphaBiLevelEffectiveData data = ((IAlphaBiLevel) operation).getEffective();
                System.out.println("  Alpha threshold: " + data.getThreshold() + "%");
            }
        }
    }
} finally {
    presentation.dispose();
}

グレースケールやアルファ天井、アルファインバースなど、パラメーターなしのエフェクトでも実効データオブジェクトは存在しますが、出力すべきスカラー設定はありません。コレクション内での存在と位置が重要な情報です。

画像変換の削除またはクリア

インデックスで単一操作を削除するには IImageTransformOperationCollection.removeAt を使用します。削除後はインデックスがシフトするため、まず対象を検索し、列挙後に削除してください。全チェーンを削除するには ImageTransformOperationCollection.clear を使用します。

import com.aspose.slides.*;

Presentation presentation = new Presentation("image-transform-chain.pptx");
try {
    IPictureFrame pictureFrame = null;

    for (IShape shape : presentation.getSlides().get_Item(0).getShapes()) {
        if (shape instanceof IPictureFrame) {
            pictureFrame = (IPictureFrame) shape;
            break;
        }
    }

    if (pictureFrame != null) {
        IImageTransformOperationCollection imageTransform = pictureFrame.getPictureFormat().getPicture().getImageTransform();
        int blurIndex = -1;

        for (int index = 0; index < imageTransform.size(); index++) {
            if (imageTransform.get_Item(index) instanceof IBlur) {
                blurIndex = index;
                break;
            }
        }

        if (blurIndex >= 0) {
            imageTransform.removeAt(blurIndex);
            System.out.println("The blur operation was removed.");
        }

        imageTransform.clear();
        System.out.println("Remaining operations: " + imageTransform.size());
        presentation.save("image-transforms-cleared.pptx", SaveFormat.Pptx);
    }
} finally {
    presentation.dispose();
}

変換を削除またはクリアしても、画像の書式設定のみが変更されます。IPPImage リソース自体は削除、再圧縮、あるいは変更されません。

プレゼンテーション形式とエクスポート先の考慮

画像変換は DrawingML から由来しているため、エフェクトチェーンの編集可能形式としては PPTX が推奨されます。PPTX でもすべての操作が同等に移植可能というわけではありません。

  • 標準 DrawingML 操作(輝度、グレースケール、デュオトーン、ティント、HSL、ぼかし、一般的なアルファ操作)は PPTX のラウンドトリップで残存する可能性が最も高いです。保存後は必ずファイルを再度開き、コレクションを検査してください。
  • BrightnessContrast は Office 2010 の拡張で、標準 DrawingML 輝度操作ではありません。インメモリ描画には使用可能ですが、保存・再オープン後に編集可能な IBrightnessContrast が残る保証はありません。永続的な明るさ・コントラスト調整には addLuminanceEffect を推奨します。
  • バイナリ PPT 形式は完全な DrawingML エフェクトモデルが導入される前のものです。PPT に保存すると未サポートの操作が除外されたり、チェーンがサポート対象のサブセットに縮小されたり、外観が近似されることがあります。複雑な編集可能チェーンの検証形式として PPT を使用しないでください。
  • PNG、JPEG、TIFF、PDF、SVG、HTML などのビジュアル出力は、サポートされたチェーンを描画結果に適用します。これらの出力には編集可能な IImageTransformOperationCollection が含まれず、ラスタ形式は結果をピクセルにフラット化し、文書/ベクタエクスポートは独自の描画表現を保持します。
  • エフェクトはリンク画像を自己完結型にしません。リンク画像をレンダリングする際は、プレゼンテーション読み込み時にリンクリソースが利用可能である必要があります。

複数のアルファやカラー量子化操作を組み合わせた場合、異なるプレゼンテーション ビューアがエッジケースを異なって描画することがあります。重要な出力については、編集可能なラウンドトリップと最終エクスポート形式の両方を、実稼働環境で使用している Aspose.Slides の同一バージョンでテストしてください。

FAQ

画像変換エフェクトは埋め込み画像データを変更しますか?

いいえ。操作は画像塗りつぶしで使用される ISlidesPicture に属し、基礎となる IPPImage バイト列は変更されません。

同じ画像を再利用している複数の画像フレームはエフェクトを共有しますか?

いいえ。IPPImage の再利用は画像データの重複を防ぎますが、各画像フレームは通常別々の ISlidesPicture と画像変換コレクションを持ちます。

カラー、ぼかし、アルファエフェクトは組み合わせ可能ですか?

はい。コレクションは1つの順序付けられたチェーンとして受け入れます。置換や閾値操作は以前のカラーやアルファのディテールを破棄する可能性があるため、各操作が前の出力に与える影響を考慮してください。

実効値が読み取り専用なのはなぜですか?

実効データはレンダリングに使用される計算済み値(解決済みカラーを含む)を表し、編集対象ではありません。書き込み可能メンバーがある操作はそのオブジェクトを直接編集し、そうでない場合は操作を削除して新しい作成パラメーターで置き換えてください。

どの形式を使用すれば変換チェーンを保持できますか?

PPTX を使用し、保存後に再度開いてファイルを検証してください。レガシー PPT は完全な DrawingML エフェクトモデルを表現できず、レンダリング出力形式は外観のみを保持し、編集可能な変換操作は保持されません。