1/*
2 * cl_image_warp_handler.cpp - CL image warping handler
3 *
4 *  Copyright (c) 2016 Intel Corporation
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *      http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * Author: Zong Wei <wei.zong@intel.com>
19 */
20
21#include "cl_utils.h"
22#include "cl_image_warp_handler.h"
23
24namespace XCam {
25
26#define CL_IMAGE_WARP_WG_WIDTH   8
27#define CL_IMAGE_WARP_WG_HEIGHT  4
28
29
30static const XCamKernelInfo kernel_image_warp_info [] = {
31    {
32        "kernel_image_warp_8_pixel",
33#include "kernel_image_warp.clx"
34        , 0,
35    },
36    {
37        "kernel_image_warp_1_pixel",
38#include "kernel_image_warp.clx"
39        , 0,
40    }
41};
42
43CLImageWarpKernel::CLImageWarpKernel (
44    const SmartPtr<CLContext> &context,
45    const char *name,
46    uint32_t channel,
47    SmartPtr<CLImageHandler> &handler)
48    : CLImageKernel (context, name)
49    , _channel (channel)
50{
51    _handler = handler.dynamic_cast_ptr<CLImageWarpHandler> ();
52}
53
54XCamReturn
55CLImageWarpKernel::prepare_arguments (
56    CLArgList &args, CLWorkSize &work_size)
57{
58    SmartPtr<CLContext> context = get_context ();
59    SmartPtr<VideoBuffer> input = _handler->get_warp_input_buf ();
60    SmartPtr<VideoBuffer> output = _handler->get_output_buf ();
61
62    const VideoBufferInfo & video_info_in = input->get_video_info ();
63    const VideoBufferInfo & video_info_out = output->get_video_info ();
64
65    uint32_t info_index = 0;
66    if (_channel == CL_IMAGE_CHANNEL_Y) {
67        info_index = 0;
68    } else if (_channel == CL_IMAGE_CHANNEL_UV) {
69        info_index = 1;
70    }
71
72    CLImageDesc cl_desc_in, cl_desc_out;
73    cl_desc_in.format.image_channel_order = info_index == 0 ? CL_R : CL_RG;
74    cl_desc_in.format.image_channel_data_type = CL_UNORM_INT8;
75    cl_desc_in.width = video_info_in.width >> info_index;
76    cl_desc_in.height = video_info_in.height >> info_index;
77    cl_desc_in.row_pitch = video_info_in.strides[info_index];
78
79#if CL_IMAGE_WARP_WRITE_UINT
80    cl_desc_out.format.image_channel_data_type = info_index == 0 ? CL_UNSIGNED_INT16 : CL_UNSIGNED_INT32;
81    cl_desc_out.format.image_channel_order = CL_RGBA;
82    cl_desc_out.width = XCAM_ALIGN_DOWN (video_info_out.width >> info_index, 8) / 8;
83    cl_desc_out.height = video_info_out.height >> info_index;
84#else
85    cl_desc_out.format.image_channel_order = info_index == 0 ? CL_R : CL_RG;
86    cl_desc_out.format.image_channel_data_type = CL_UNORM_INT8;
87    cl_desc_out.width = video_info_out.width >> info_index;
88    cl_desc_out.height = video_info_out.height >> info_index;
89#endif
90
91    cl_desc_out.row_pitch = video_info_out.strides[info_index];
92    SmartPtr<CLImage> image_in = convert_to_climage (context, input, cl_desc_in, video_info_in.offsets[info_index]);
93
94    CLWarpConfig warp_config = _handler->get_warp_config ();
95    if ((warp_config.trim_ratio > 0.5f) || (warp_config.trim_ratio < 0.0f)) {
96        warp_config.trim_ratio = 0.0f;
97    }
98
99    float sample_rate_x = (float)warp_config.width / (float)video_info_in.width;
100    float sample_rate_y = (float)warp_config.height / (float)video_info_in.height;
101    XCAM_LOG_DEBUG ("warp analyze image sample rate(%fx%f)", sample_rate_x, sample_rate_y);
102    warp_config.proj_mat[2] = warp_config.proj_mat[2] / sample_rate_x;
103    warp_config.proj_mat[5] = warp_config.proj_mat[5] / sample_rate_y;
104    warp_config.proj_mat[6] = warp_config.proj_mat[6] * sample_rate_x;
105    warp_config.proj_mat[7] = warp_config.proj_mat[7] * sample_rate_y;
106
107    /*
108       For NV12 image (YUV420), UV plane has half horizontal & vertical coordinate size of Y plane,
109       need to adjust the projection matrix as:
110       H(uv) = [0.5, 0, 0; 0, 0.5, 0; 0, 0, 1] * H(y) * [2, 0, 0; 0, 2, 0; 0, 0, 1]
111    */
112    if (_channel == CL_IMAGE_CHANNEL_UV) {
113        warp_config.proj_mat[2] = 0.5 * warp_config.proj_mat[2];
114        warp_config.proj_mat[5] = 0.5 * warp_config.proj_mat[5];
115        warp_config.proj_mat[6] = 2.0 * warp_config.proj_mat[6];
116        warp_config.proj_mat[7] = 2.0 * warp_config.proj_mat[7];
117    }
118
119    /*
120      Trim image: shift toward origin then scale up
121      Trim Matrix (TMat)
122      TMat = [ scale_x, 0.0f,    shift_x;
123               0.0f,    scale_y, shift_y;
124               1.0f,    1.0f,    1.0f;   ]
125
126      Warp Perspective Matrix = TMat * HMat
127    */
128#if CL_IMAGE_WARP_WRITE_UINT
129    float shift_x = warp_config.trim_ratio * cl_desc_out.width * 8.0f;
130#else
131    float shift_x = warp_config.trim_ratio * cl_desc_out.width;
132#endif
133    float shift_y = warp_config.trim_ratio * cl_desc_out.height;
134    float scale_x = 1.0f - 2.0f * warp_config.trim_ratio;
135    float scale_y = 1.0f - 2.0f * warp_config.trim_ratio;
136
137    warp_config.proj_mat[0] = scale_x * warp_config.proj_mat[0] + shift_x * warp_config.proj_mat[6];
138    warp_config.proj_mat[1] = scale_x * warp_config.proj_mat[1] + shift_x * warp_config.proj_mat[7];
139    warp_config.proj_mat[2] = scale_x * warp_config.proj_mat[2] + shift_x * warp_config.proj_mat[8];
140    warp_config.proj_mat[3] = scale_y * warp_config.proj_mat[3] + shift_y * warp_config.proj_mat[6];
141    warp_config.proj_mat[4] = scale_y * warp_config.proj_mat[4] + shift_y * warp_config.proj_mat[7];
142    warp_config.proj_mat[5] = scale_y * warp_config.proj_mat[5] + shift_y * warp_config.proj_mat[8];
143
144    XCAM_LOG_DEBUG ("warp config image size(%dx%d)", warp_config.width, warp_config.height);
145    XCAM_LOG_DEBUG ("proj_mat[%d]=(%f, %f, %f, %f, %f, %f, %f, %f, %f);", warp_config.frame_id,
146                    warp_config.proj_mat[0], warp_config.proj_mat[1], warp_config.proj_mat[2],
147                    warp_config.proj_mat[3], warp_config.proj_mat[4], warp_config.proj_mat[5],
148                    warp_config.proj_mat[6], warp_config.proj_mat[7], warp_config.proj_mat[8]);
149
150    SmartPtr<CLImage> image_out = convert_to_climage (context, output, cl_desc_out, video_info_out.offsets[info_index]);
151    XCAM_FAIL_RETURN (
152        WARNING,
153        image_in->is_valid () && image_out->is_valid (),
154        XCAM_RETURN_ERROR_MEM,
155        "cl image kernel(%s) in/out memory not available", get_kernel_name ());
156
157    //set args;
158    work_size.dim = XCAM_DEFAULT_IMAGE_DIM;
159    work_size.local[0] = CL_IMAGE_WARP_WG_WIDTH;
160    work_size.local[1] = CL_IMAGE_WARP_WG_HEIGHT;
161    work_size.global[0] = XCAM_ALIGN_UP (cl_desc_out.width, work_size.local[0]);
162    work_size.global[1] = XCAM_ALIGN_UP(cl_desc_out.height, work_size.local[1]);
163
164    args.push_back (new CLMemArgument (image_in));
165    args.push_back (new CLMemArgument (image_out));
166    args.push_back (new CLArgumentT<CLWarpConfig> (warp_config));
167
168    return XCAM_RETURN_NO_ERROR;
169}
170
171CLImageWarpHandler::CLImageWarpHandler (const SmartPtr<CLContext> &context, const char *name)
172    : CLImageHandler (context, name)
173{
174}
175
176bool
177CLImageWarpHandler::is_ready ()
178{
179    bool ret = !_warp_config_list.empty ();
180    return ret && CLImageHandler::is_ready ();
181}
182
183XCamReturn
184CLImageWarpHandler::execute_done (SmartPtr<VideoBuffer> &output)
185{
186    XCAM_UNUSED (output);
187    if (!_warp_config_list.empty ()) {
188        _warp_config_list.pop_front ();
189    }
190
191    return XCAM_RETURN_NO_ERROR;
192}
193
194SmartPtr<VideoBuffer>
195CLImageWarpHandler::get_warp_input_buf ()
196{
197    return CLImageHandler::get_input_buf ();
198}
199
200bool
201CLImageWarpHandler::set_warp_config (const XCamDVSResult& config)
202{
203    CLWarpConfig warp_config;
204    warp_config.frame_id = config.frame_id;
205    warp_config.width = config.frame_width;
206    warp_config.height = config.frame_height;
207    for( int i = 0; i < 9; i++ ) {
208        warp_config.proj_mat[i] = config.proj_mat[i];
209    }
210    XCAM_LOG_DEBUG ("warp_mat{%d}=[%f, %f, %f; %f, %f, %f; %f, %f, %f]", warp_config.frame_id + 1,
211                    warp_config.proj_mat[0], warp_config.proj_mat[1], warp_config.proj_mat[2],
212                    warp_config.proj_mat[3], warp_config.proj_mat[4], warp_config.proj_mat[5],
213                    warp_config.proj_mat[6], warp_config.proj_mat[7], warp_config.proj_mat[8]);
214#if 0
215    printf ("warp_mat{%d}=[%f, %f, %f; %f, %f, %f; %f, %f, %f]; \n", warp_config.frame_id + 1,
216            warp_config.proj_mat[0], warp_config.proj_mat[1], warp_config.proj_mat[2],
217            warp_config.proj_mat[3], warp_config.proj_mat[4], warp_config.proj_mat[5],
218            warp_config.proj_mat[6], warp_config.proj_mat[7], warp_config.proj_mat[8]);
219#endif
220    _warp_config_list.push_back (warp_config);
221
222    return true;
223}
224
225CLWarpConfig
226CLImageWarpHandler::get_warp_config ()
227{
228    CLWarpConfig warp_config;
229
230    if (_warp_config_list.size () > 0) {
231        warp_config = *(_warp_config_list.begin ());
232    } else {
233        warp_config.frame_id = -1;
234        warp_config.proj_mat[0] = 1.0f;
235        warp_config.proj_mat[1] = 0.0f;
236        warp_config.proj_mat[2] = 0.0f;
237        warp_config.proj_mat[3] = 0.0f;
238        warp_config.proj_mat[4] = 1.0f;
239        warp_config.proj_mat[5] = 0.0f;
240        warp_config.proj_mat[6] = 0.0f;
241        warp_config.proj_mat[7] = 0.0f;
242        warp_config.proj_mat[8] = 1.0f;
243    }
244
245    return warp_config;
246}
247
248static SmartPtr<CLImageWarpKernel>
249create_kernel_image_warp (
250    const SmartPtr<CLContext> &context,
251    uint32_t channel,
252    SmartPtr<CLImageHandler> handler)
253{
254    SmartPtr<CLImageWarpKernel> warp_kernel;
255
256    const char *name = (channel == CL_IMAGE_CHANNEL_Y ? "kernel_image_warp_y" : "kernel_image_warp_uv");
257    char build_options[1024];
258    xcam_mem_clear (build_options);
259
260    snprintf (build_options, sizeof (build_options),
261              " -DWARP_Y=%d ",
262              (channel == CL_IMAGE_CHANNEL_Y ? 1 : 0));
263
264    warp_kernel = new CLImageWarpKernel (context, name, channel, handler);
265    XCAM_ASSERT (warp_kernel.ptr ());
266    XCAM_FAIL_RETURN (
267        ERROR, warp_kernel->build_kernel (kernel_image_warp_info[KernelImageWarp], build_options) == XCAM_RETURN_NO_ERROR,
268        NULL, "build image warp kernel failed");
269    XCAM_ASSERT (warp_kernel->is_valid ());
270
271    return warp_kernel;
272}
273
274SmartPtr<CLImageHandler>
275create_cl_image_warp_handler (const SmartPtr<CLContext> &context)
276{
277    SmartPtr<CLImageWarpHandler> warp_handler;
278    SmartPtr<CLImageKernel> warp_kernel;
279
280    warp_handler = new CLImageWarpHandler (context);
281    XCAM_ASSERT (warp_handler.ptr ());
282
283    warp_kernel = create_kernel_image_warp (context, CL_IMAGE_CHANNEL_Y, warp_handler);
284    XCAM_ASSERT (warp_kernel.ptr ());
285    warp_handler->add_kernel (warp_kernel);
286
287    warp_kernel = create_kernel_image_warp (context, CL_IMAGE_CHANNEL_UV, warp_handler);
288    XCAM_ASSERT (warp_kernel.ptr ());
289    warp_handler->add_kernel (warp_kernel);
290
291    return warp_handler;
292}
293
294};
295