1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.filterpacks.imageproc;
18
19import android.filterfw.core.Filter;
20import android.filterfw.core.FilterContext;
21import android.filterfw.core.Frame;
22import android.filterfw.core.FrameFormat;
23import android.filterfw.core.FrameManager;
24import android.filterfw.core.GenerateFieldPort;
25import android.filterfw.core.KeyValueMap;
26import android.filterfw.core.MutableFrameFormat;
27import android.filterfw.core.Program;
28import android.filterfw.core.ShaderProgram;
29import android.filterfw.format.ImageFormat;
30
31public class ImageSlicer extends Filter {
32
33    @GenerateFieldPort(name = "xSlices")
34    private int mXSlices;
35
36    @GenerateFieldPort(name = "ySlices")
37    private int mYSlices;
38
39    @GenerateFieldPort(name = "padSize")
40    private int mPadSize;
41
42    // The current slice index from 0 to xSlices * ySlices
43    private int mSliceIndex;
44
45    private Frame mOriginalFrame;
46
47    private Program mProgram;
48
49    private int mInputWidth;
50    private int mInputHeight;
51
52    private int mSliceWidth;
53    private int mSliceHeight;
54
55    private int mOutputWidth;
56    private int mOutputHeight;
57
58    public ImageSlicer(String name) {
59        super(name);
60        mSliceIndex = 0;
61    }
62
63    @Override
64    public void setupPorts() {
65        addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
66                                                       FrameFormat.TARGET_GPU));
67        addOutputBasedOnInput("image", "image");
68    }
69
70    @Override
71    public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
72        return inputFormat;
73    }
74
75    private void calcOutputFormatForInput(Frame frame) {
76
77        // calculate the output size based on input size, xSlices, and ySlices
78        mInputWidth = frame.getFormat().getWidth();
79        mInputHeight = frame.getFormat().getHeight();
80
81        mSliceWidth = (mInputWidth + mXSlices - 1) / mXSlices;
82        mSliceHeight = (mInputHeight + mYSlices - 1)/ mYSlices;
83
84        mOutputWidth = mSliceWidth + mPadSize * 2;
85        mOutputHeight = mSliceHeight + mPadSize * 2;
86    }
87
88
89    @Override
90    public void process(FilterContext context) {
91
92        // Get input frame
93        if (mSliceIndex == 0) {
94            mOriginalFrame = pullInput("image");
95            calcOutputFormatForInput(mOriginalFrame);
96        }
97
98        FrameFormat inputFormat = mOriginalFrame.getFormat();
99        MutableFrameFormat outputFormat = inputFormat.mutableCopy();
100        outputFormat.setDimensions(mOutputWidth, mOutputHeight);
101
102        // Create output frame
103        Frame output = context.getFrameManager().newFrame(outputFormat);
104
105        // Create the program if not created already
106        if (mProgram == null) {
107            mProgram = ShaderProgram.createIdentity(context);
108        }
109
110        // Calculate the four corner of the source region
111        int xSliceIndex = mSliceIndex % mXSlices;
112        int ySliceIndex = mSliceIndex / mXSlices;
113
114        // TODO(rslin) : not sure shifting by 0.5 is needed.
115        float x0 = (xSliceIndex * mSliceWidth - mPadSize) / ((float) mInputWidth);
116        float y0 = (ySliceIndex * mSliceHeight - mPadSize) / ((float) mInputHeight);
117
118        ((ShaderProgram) mProgram).setSourceRect(x0, y0,
119                                                 ((float) mOutputWidth) / mInputWidth,
120                                                 ((float) mOutputHeight) / mInputHeight);
121
122        // Process
123        mProgram.process(mOriginalFrame, output);
124        mSliceIndex++;
125
126        if (mSliceIndex == mXSlices * mYSlices) {
127            mSliceIndex = 0;
128            mOriginalFrame.release();
129            setWaitsOnInputPort("image", true);
130        } else {
131            // Retain the original frame so it can be used next time.
132            mOriginalFrame.retain();
133            setWaitsOnInputPort("image", false);
134        }
135
136        // Push output
137        pushOutput("image", output);
138
139        // Release pushed frame
140        output.release();
141    }
142}
143