1 #include <string.h>
2 #include <vips/vips.h>
3 #include <gtkmm/main.h>
4
5 #include "../base/image.hh"
6
7 #include "../operations/image_reader.hh"
8 #include "../operations/invert.hh"
9 //#include "../operations/brightness_contrast.hh"
10 //#include "../operations/gradient.hh"
11
12 //#include "../gui/operations/brightness_contrast_config.hh"
13
14 /* Create a tiny VipsOperator ... photographic negative of a uchar image.
15 */
16
17 typedef struct _Negative {
18 VipsOperation parent_instance;
19
20 VipsImage *in;
21 VipsImage *out;
22
23 int image_max;
24
25 } Negative;
26
27 typedef struct _NegativeClass {
28 VipsOperationClass parent_class;
29
30 /* No new class members needed for this op.
31 */
32
33 } NegativeClass;
34
35 /* This defines a function called negative_get_type() which adds the new
36 * operation to the class hierarchy, and negative_parent_class, our
37 * superclass.
38 *
39 * You must define negative_class_init() to init a new class struct and
40 * negative_init() to init a new instance struct.
41 */
42
43 G_DEFINE_TYPE( Negative, negative, VIPS_TYPE_OPERATION );
44
45 static int
negative_generate(VipsRegion * oreg,void * vseq,void * a,void * b,gboolean * stop)46 negative_generate( VipsRegion *oreg,
47 void *vseq, void *a, void *b, gboolean *stop )
48 {
49 /* The area of the output region we have neen asked to make.
50 */
51 VipsRect *r = &oreg->valid;
52
53 /* The sequence value ... the thing returned by start_one.
54 */
55 VipsRegion *ir = (VipsRegion *) vseq;
56
57 Negative *negative = (Negative *) b;
58 int line_size = r->width * negative->in->Bands;
59
60 int x, y;
61 int max = negative->image_max;
62
63 /* Request matching part of input region.
64 */
65 if( vips_region_prepare( ir, r ) )
66 return( -1 );
67
68 for( y = 0; y < r->height; y++ ) {
69 unsigned char *p = (unsigned char *)
70 VIPS_REGION_ADDR( ir, r->left, r->top + y );
71 unsigned char *q = (unsigned char *)
72 VIPS_REGION_ADDR( oreg, r->left, r->top + y );
73
74 for( x = 0; x < line_size; x++ )
75 q[x] = max - p[x];
76 }
77
78 return( 0 );
79 }
80
81 static int
negative_build(VipsObject * object)82 negative_build( VipsObject *object )
83 {
84 VipsObjectClass *klass = VIPS_OBJECT_GET_CLASS( object );
85 Negative *negative = (Negative *) object;
86
87 /* Build all the superclasses. This will set @in and
88 * @image_max.
89 */
90 if( VIPS_OBJECT_CLASS( negative_parent_class )->build( object ) )
91 return( -1 );
92
93 /* We only work for uchar images.
94 */
95 if( vips_check_uncoded( klass->nickname, negative->in ) ||
96 vips_check_format( klass->nickname,
97 negative->in, VIPS_FORMAT_UCHAR ) )
98 return( -1 );
99
100 /* Get ready to write to @out. @out must be set via g_object_set() so
101 * that vips can see the assignment. It'll complain that @out hasn't
102 * been set otherwise.
103 */
104 g_object_set( object, "out", vips_image_new(), NULL );
105
106 /* Tell vips that @out depends on @in and prefers to work in
107 * scanlines.
108 */
109 if( vips_image_pipelinev( negative->out,
110 VIPS_DEMAND_STYLE_THINSTRIP, negative->in, NULL ) )
111 return( -1 );
112
113 /* This attaches callbacks to @out to calculate pixels on request.
114 * start_one and stop_one are a pair of start and stop functions which
115 * create a single input region on extra argument 1, handy for 1-ary
116 * operations.
117 */
118 if( vips_image_generate( negative->out,
119 vips_start_one,
120 negative_generate,
121 vips_stop_one,
122 negative->in, negative ) )
123 return( -1 );
124
125 return( 0 );
126 }
127
128 static void
negative_class_init(NegativeClass * klass)129 negative_class_init( NegativeClass *klass )
130 {
131 GObjectClass *gobject_class = G_OBJECT_CLASS( klass );
132 VipsObjectClass *object_class = (VipsObjectClass *) klass;
133
134 gobject_class->set_property = vips_object_set_property;
135 gobject_class->get_property = vips_object_get_property;
136
137 object_class->nickname = "negative";
138 object_class->description = "photographic negative";
139 object_class->build = negative_build;
140
141 VIPS_ARG_IMAGE( klass, "in", 1,
142 "Input",
143 "Input image",
144 VIPS_ARGUMENT_REQUIRED_INPUT,
145 G_STRUCT_OFFSET( Negative, in ) );
146
147 VIPS_ARG_IMAGE( klass, "out", 2,
148 "Output",
149 "Output image",
150 VIPS_ARGUMENT_REQUIRED_OUTPUT,
151 G_STRUCT_OFFSET( Negative, out ) );
152
153 VIPS_ARG_INT( klass, "image_max", 4,
154 "Image maximum",
155 "Maximum value in image: pivot about this",
156 VIPS_ARGUMENT_OPTIONAL_INPUT,
157 G_STRUCT_OFFSET( Negative, image_max ),
158 0, 255, 255 );
159
160 }
161
162 static void
negative_init(Negative * negative)163 negative_init( Negative *negative )
164 {
165 negative->image_max = 255;
166 }
167
168 /* A type-safe way to call nagative from C.
169 *
170 * You can also use
171 *
172 * vips_call( "negative", in, &out, "image_max", 128, NULL )
173 *
174 * but that won't have any compile-time checks, of course.
175 */
176 static int
negative(VipsImage * in,VipsImage ** out,...)177 negative( VipsImage *in, VipsImage **out, ... )
178 {
179 va_list ap;
180 int result;
181
182 va_start( ap, out );
183 result = vips_call_split( "negative", ap, in, out );
184 va_end( ap );
185
186 return( result );
187 }
188
189 /* We need C linkage for this.
190 */
191 #ifdef __cplusplus
192 extern "C" {
193 #endif /*__cplusplus*/
194
195 extern GType vips_layer_get_type( void );
196
197 #ifdef __cplusplus
198 }
199 #endif /*__cplusplus*/
200
201 int
main(int argc,char ** argv)202 main( int argc, char **argv )
203 {
204 //Gtk::Main kit(argc, argv);
205
206 VipsImage *in;
207 VipsImage *out;
208 VipsImage *temp;
209
210
211 if( vips_init( argv[0] ) )
212 vips_error_exit( "unable to init" );
213
214 im_concurrency_set( 1 );
215
216 /* Add negative to the class hierarchy. You'll now be able to use it
217 * exactly like any built-in vips operation and it'll appear in
218 * Python, nip2, C, C++ etc etc.
219 */
220 negative_get_type();
221 vips_layer_get_type();
222
223 if( argc != 4 )
224 vips_error_exit( "usage: %s vips/pf infile outfile", argv[0] );
225
226 bool check_vips = true;
227 if(!strcmp(argv[1],"pf")) check_vips = false;
228
229 if( check_vips ) {
230 if( !(in = vips_image_new_from_file( argv[2], NULL )) )
231 vips_error_exit( "unable to open" );
232
233 std::cout<<"in refcount after new_from_file: "<<G_OBJECT(in)->ref_count<<std::endl;
234
235 temp = in;
236
237 for(int i = 0; i < 1000; i++) {
238
239 std::cout<<std::endl;
240 std::cout<<"refcount before negative("<<i<<"): in("<<temp<<")="<<G_OBJECT(temp)->ref_count<<std::endl;
241 if( negative( temp, &out, "image_max", 128, NULL ) ) {
242 g_object_unref( temp );
243 vips_error_exit( "unable to invert" );
244 }
245 std::cout<<"refcount after negative("<<i<<"): in("<<temp<<")="<<G_OBJECT(temp)->ref_count
246 <<" out("<<out<<")="<<G_OBJECT(out)->ref_count<<std::endl;
247
248 /* We have a ref to out, out holds a ref to the negative operation,
249 * and the operation holds a ref to in. We can safely unref in and
250 * it'll be unreffed when we unref out.
251 */
252 g_object_unref( temp );
253 std::cout<<std::endl;
254 std::cout<<"refcount after unref(in): in("<<temp<<")="<<G_OBJECT(temp)->ref_count
255 <<" out("<<out<<")="<<G_OBJECT(out)->ref_count<<std::endl;
256 std::cout<<"-----------------------------------"<<std::endl;
257 temp = out;
258
259 }
260
261 printf("Writing output image...\n");
262
263 std::cout<<"refcount before write_to_file: in("<<in<<")="<<G_OBJECT(in)->ref_count
264 <<" out("<<out<<")="<<G_OBJECT(out)->ref_count<<std::endl;
265 if( vips_image_write_to_file( out, argv[3], NULL ) ) {
266 g_object_unref( out );
267 vips_error_exit( "unable to write" );
268 }
269
270 printf("...done\n");
271
272 std::cout<<"refcount after write_to_file: in("<<in<<")="<<G_OBJECT(in)->ref_count
273 <<" out("<<out<<")="<<G_OBJECT(out)->ref_count<<std::endl;
274 g_object_unref( out );
275 std::cout<<"refcount after unref(out): in("<<in<<")="<<G_OBJECT(in)->ref_count
276 <<" out("<<out<<")="<<G_OBJECT(out)->ref_count<<std::endl;
277 } else {
278
279 std::vector<VipsImage*> in;
280
281 PF::Processor<PF::ImageReaderPar,PF::ImageReader>* imgread =
282 new PF::Processor<PF::ImageReaderPar,PF::ImageReader>();
283 imgread->get_par()->set_file_name( argv[2] );
284
285 PF::Image* pf_image = new PF::Image();
286 PF::LayerManager& layer_manager = pf_image->get_layer_manager();
287
288 PF::Layer* limg = layer_manager.new_layer();
289 limg->set_processor( imgread );
290 limg->set_name( "input image" );
291 layer_manager.get_layers().push_back( limg );
292
293 for(int i = 0; i < 4; i++) {
294 PF::Layer* linv1 = layer_manager.new_layer();
295 linv1->set_processor( new PF::Processor<PF::InvertPar,PF::Invert>() );
296 linv1->set_name( "invert" );
297 layer_manager.get_layers().push_back( linv1 );
298 }
299
300 pf_image->add_pipeline( VIPS_FORMAT_UCHAR, 0 );
301 pf_image->do_update(NULL);
302
303 PF::Pipeline* pipeline = pf_image->get_pipeline( 0 );
304
305 VipsImage* image = pipeline->get_output();
306
307 printf("Writing output image...\n");
308
309 if( vips_image_write_to_file( pipeline->get_output(), argv[3], NULL ) ) {
310 //g_object_unref( out );
311 vips_error_exit( "unable to write" );
312 }
313
314 printf("...done\n");
315
316 delete pf_image;
317
318 }
319
320 return( 0 );
321 }
322