1 /* GdkPixbuf library - Scaling and compositing functions
3 * Copyright (C) 1999 The Free Software Foundation
5 * Author: Owen Taylor <otaylor@redhat.com>
6 * Modified for YUV422 by Dan Dennedy <dan@dennedy.org>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
30 #define SUBSAMPLE_BITS 4
31 #define SUBSAMPLE (1 << SUBSAMPLE_BITS)
32 #define SUBSAMPLE_MASK ((1 << SUBSAMPLE_BITS)-1)
33 #define SCALE_SHIFT 16
35 typedef struct _PixopsFilter PixopsFilter
;
36 typedef struct _PixopsFilterDimension PixopsFilterDimension
;
38 struct _PixopsFilterDimension
47 PixopsFilterDimension x
;
48 PixopsFilterDimension y
;
52 typedef guchar
*( *PixopsLineFunc
) ( int *weights
, int n_x
, int n_y
,
53 guchar
*dest
, int dest_x
, guchar
*dest_end
,
55 int x_init
, int x_step
, int src_width
);
57 typedef void ( *PixopsPixelFunc
) ( guchar
*dest
, guint y1
, guint cr
, guint y2
, guint cb
);
60 /* mmx function declarations */
62 guchar
*pixops_scale_line_22_yuv_mmx ( guint32 weights
[ 16 ][ 8 ], guchar
*p
, guchar
*q1
, guchar
*q2
, int x_step
, guchar
*p_stop
, int x_init
, int destx
);
63 int pixops_have_mmx ( void );
67 get_check_shift ( int check_size
)
70 g_return_val_if_fail ( check_size
>= 0, 4 );
72 while ( !( check_size
& 1 ) )
82 pixops_scale_nearest ( guchar
*dest_buf
,
88 const guchar
*src_buf
,
96 register int x_step
= ( 1 << SCALE_SHIFT
) / scale_x
;
97 register int y_step
= ( 1 << SCALE_SHIFT
) / scale_y
;
98 register int x
, x_scaled
;
100 for ( i
= 0; i
< ( render_y1
- render_y0
); i
++ )
102 const guchar
*src
= src_buf
+ ( ( ( i
+ render_y0
) * y_step
+ ( y_step
>> 1 ) ) >> SCALE_SHIFT
) * src_rowstride
;
103 guchar
*dest
= dest_buf
+ i
* dest_rowstride
;
104 x
= render_x0
* x_step
+ ( x_step
>> 1 );
106 for ( j
= 0; j
< ( render_x1
- render_x0
); j
++ )
108 x_scaled
= x
>> SCALE_SHIFT
;
109 *dest
++ = src
[ x_scaled
<< 1 ];
110 *dest
++ = src
[ ( ( x_scaled
>> 1 ) << 2 ) + ( ( j
& 1 ) << 1 ) + 1 ];
117 static inline guchar
*
118 scale_line ( int *weights
, int n_x
, int n_y
,
119 guchar
*dest
, int dest_x
, guchar
*dest_end
,
121 int x_init
, int x_step
, int src_width
)
123 register int x
= x_init
;
124 register int i
, j
, x_scaled
, y_index
, uv_index
;
126 while ( dest
< dest_end
)
128 unsigned int y
= 0, uv
= 0;
129 int *pixel_weights
= weights
+ ( ( x
>> ( SCALE_SHIFT
- SUBSAMPLE_BITS
) ) & SUBSAMPLE_MASK
) * n_x
* n_y
;
131 x_scaled
= x
>> SCALE_SHIFT
;
132 y_index
= x_scaled
<< 1;
133 uv_index
= ( ( x_scaled
>> 1 ) << 2 ) + ( ( dest_x
& 1 ) << 1 ) + 1;
135 for ( i
= 0; i
< n_y
; i
++ )
137 int *line_weights
= pixel_weights
+ n_x
* i
;
138 guchar
*q
= src
[ i
];
140 for ( j
= 0; j
< n_x
; j
++ )
142 unsigned int ta
= line_weights
[ j
];
144 y
+= ta
* q
[ y_index
];
145 uv
+= ta
* q
[ uv_index
];
149 *dest
++ = ( y
+ 0xffff ) >> SCALE_SHIFT
;
150 *dest
++ = ( uv
+ 0xffff ) >> SCALE_SHIFT
;
160 static inline guchar
*
161 scale_line_22_yuv_mmx_stub ( int *weights
, int n_x
, int n_y
,
162 guchar
*dest
, int dest_x
, guchar
*dest_end
,
164 int x_init
, int x_step
, int src_width
)
166 guint32 mmx_weights
[ 16 ][ 8 ];
169 for ( j
= 0; j
< 16; j
++ )
171 mmx_weights
[ j
][ 0 ] = 0x00010001 * ( weights
[ 4 * j
] >> 8 );
172 mmx_weights
[ j
][ 1 ] = 0x00010001 * ( weights
[ 4 * j
] >> 8 );
173 mmx_weights
[ j
][ 2 ] = 0x00010001 * ( weights
[ 4 * j
+ 1 ] >> 8 );
174 mmx_weights
[ j
][ 3 ] = 0x00010001 * ( weights
[ 4 * j
+ 1 ] >> 8 );
175 mmx_weights
[ j
][ 4 ] = 0x00010001 * ( weights
[ 4 * j
+ 2 ] >> 8 );
176 mmx_weights
[ j
][ 5 ] = 0x00010001 * ( weights
[ 4 * j
+ 2 ] >> 8 );
177 mmx_weights
[ j
][ 6 ] = 0x00010001 * ( weights
[ 4 * j
+ 3 ] >> 8 );
178 mmx_weights
[ j
][ 7 ] = 0x00010001 * ( weights
[ 4 * j
+ 3 ] >> 8 );
181 return pixops_scale_line_22_yuv_mmx ( mmx_weights
, dest
, src
[ 0 ], src
[ 1 ], x_step
, dest_end
, x_init
, dest_x
);
185 static inline guchar
*
186 scale_line_22_yuv ( int *weights
, int n_x
, int n_y
,
187 guchar
*dest
, int dest_x
, guchar
*dest_end
,
189 int x_init
, int x_step
, int src_width
)
191 register int x
= x_init
;
192 register guchar
*src0
= src
[ 0 ];
193 register guchar
*src1
= src
[ 1 ];
194 register unsigned int p
;
195 register guchar
*q0
, *q1
;
196 register int w1
, w2
, w3
, w4
;
197 register int x_scaled
, x_aligned
, uv_index
;
199 while ( dest
< dest_end
)
201 int *pixel_weights
= weights
+ ( ( x
>> ( SCALE_SHIFT
- SUBSAMPLE_BITS
) ) & SUBSAMPLE_MASK
) * 4;
203 x_scaled
= x
>> SCALE_SHIFT
;
205 w1
= pixel_weights
[ 0 ];
206 w2
= pixel_weights
[ 1 ];
207 w3
= pixel_weights
[ 2 ];
208 w4
= pixel_weights
[ 3 ];
211 q0
= src0
+ ( x_scaled
<< 1 );
212 q1
= src1
+ ( x_scaled
<< 1 );
217 *dest
++ = ( p
+ 0x8000 ) >> SCALE_SHIFT
;
220 x_aligned
= ( ( x_scaled
>> 1 ) << 2 );
221 q0
= src0
+ x_aligned
;
222 uv_index
= ( ( dest_x
& 1 ) << 1 );
223 //printf( "scale_line_22_yuv: %d %d\n", x_aligned + uv_index, dest_x );
224 p
= w1
* q0
[ uv_index
+ 1 ];
225 p
+= w2
* q0
[ uv_index
+ 1 ];
228 x_scaled
= x
>> SCALE_SHIFT
;
231 x_aligned
= ( ( x_scaled
>> 1 ) << 2 );
232 q1
= src1
+ x_aligned
;
233 uv_index
= ( ( dest_x
& 1 ) << 1 ) + 1;
234 p
+= w3
* q1
[ uv_index
];
235 p
+= w4
* q1
[ uv_index
];
236 *dest
++ = ( p
+ 0x8000 ) >> SCALE_SHIFT
;
245 process_pixel ( int *weights
, int n_x
, int n_y
,
246 guchar
*dest
, int dest_x
, int dest_channels
,
247 guchar
**src
, int src_channels
,
248 int x_start
, int src_width
)
250 register unsigned int y
= 0, uv
= 0;
252 int uv_index
= ( ( dest_x
& 1 ) << 1 ) + 1;
254 for ( i
= 0; i
< n_y
; i
++ )
256 int *line_weights
= weights
+ n_x
* i
;
258 for ( j
= 0; j
< n_x
; j
++ )
260 unsigned int ta
= 0xff * line_weights
[ j
];
262 if ( x_start
+ j
< 0 )
264 y
+= ta
* src
[ i
][ 0 ];
265 uv
+= ta
* src
[ i
][ uv_index
];
267 else if ( x_start
+ j
< src_width
)
269 y
+= ta
* src
[ i
][ ( x_start
+ j
) << 1 ];
270 uv
+= ta
* src
[ i
][ ( ( ( x_start
+ j
) >> 1 ) << 2) + uv_index
];
274 y
+= ta
* src
[ i
][ ( src_width
- 1 ) << 1 ];
275 uv
+= ta
* src
[ i
][ ( ( ( src_width
- 1 ) >> 1 ) << 2) + uv_index
];
280 *dest
++ = ( y
+ 0xffffff ) >> 24;
281 *dest
++ = ( uv
+ 0xffffff ) >> 24;
286 correct_total ( int *weights
,
290 double overall_alpha
)
292 int correction
= ( int ) ( 0.5 + 65536 * overall_alpha
) - total
;
293 int remaining
, c
, d
, i
;
295 if ( correction
!= 0 )
297 remaining
= correction
;
298 for ( d
= 1, c
= correction
; c
!= 0 && remaining
!= 0; d
++, c
= correction
/ d
)
299 for ( i
= n_x
* n_y
- 1; i
>= 0 && c
!= 0 && remaining
!= 0; i
-- )
300 if ( *( weights
+ i
) + c
>= 0 )
302 *( weights
+ i
) += c
;
304 if ( ( 0 < remaining
&& remaining
< c
) ||
305 ( 0 > remaining
&& remaining
> c
) )
313 make_filter_table ( PixopsFilter
*filter
)
315 int i_offset
, j_offset
;
316 int n_x
= filter
->x
.n
;
317 int n_y
= filter
->y
.n
;
318 int *weights
= g_new ( int, SUBSAMPLE
* SUBSAMPLE
* n_x
* n_y
);
320 for ( i_offset
= 0; i_offset
< SUBSAMPLE
; i_offset
++ )
321 for ( j_offset
= 0; j_offset
< SUBSAMPLE
; j_offset
++ )
324 int *pixel_weights
= weights
+ ( ( i_offset
* SUBSAMPLE
) + j_offset
) * n_x
* n_y
;
328 for ( i
= 0; i
< n_y
; i
++ )
329 for ( j
= 0; j
< n_x
; j
++ )
331 weight
= filter
->x
.weights
[ ( j_offset
* n_x
) + j
] *
332 filter
->y
.weights
[ ( i_offset
* n_y
) + i
] *
333 filter
->overall_alpha
* 65536 + 0.5;
335 total
+= ( int ) weight
;
337 *( pixel_weights
+ n_x
* i
+ j
) = weight
;
340 correct_total ( pixel_weights
, n_x
, n_y
, total
, filter
->overall_alpha
);
348 pixops_process ( guchar
*dest_buf
,
355 gboolean dest_has_alpha
,
356 const guchar
*src_buf
,
361 gboolean src_has_alpha
,
369 PixopsFilter
*filter
,
370 PixopsLineFunc line_func
)
373 int x
, y
; /* X and Y position in source (fixed_point) */
375 guchar
**line_bufs
= g_new ( guchar
*, filter
->y
.n
);
376 int *filter_weights
= make_filter_table ( filter
);
378 int x_step
= ( 1 << SCALE_SHIFT
) / scale_x
; /* X step in source (fixed point) */
379 int y_step
= ( 1 << SCALE_SHIFT
) / scale_y
; /* Y step in source (fixed point) */
381 int check_shift
= check_size ?
get_check_shift ( check_size
) : 0;
383 int scaled_x_offset
= floor ( filter
->x
.offset
* ( 1 << SCALE_SHIFT
) );
385 /* Compute the index where we run off the end of the source buffer. The furthest
386 * source pixel we access at index i is:
388 * ((render_x0 + i) * x_step + scaled_x_offset) >> SCALE_SHIFT + filter->x.n - 1
390 * So, run_end_index is the smallest i for which this pixel is src_width, i.e, for which:
392 * (i + render_x0) * x_step >= ((src_width - filter->x.n + 1) << SCALE_SHIFT) - scaled_x_offset
395 #define MYDIV(a,b) ((a) > 0 ? (a) / (b) : ((a) - (b) + 1) / (b)) /* Division so that -1/5 = -1 */
397 int run_end_x
= ( ( ( src_width
- filter
->x
.n
+ 1 ) << SCALE_SHIFT
) - scaled_x_offset
);
398 int run_end_index
= MYDIV ( run_end_x
+ x_step
- 1, x_step
) - render_x0
;
399 run_end_index
= MIN ( run_end_index
, render_x1
- render_x0
);
401 y
= render_y0
* y_step
+ floor ( filter
->y
.offset
* ( 1 << SCALE_SHIFT
) );
402 for ( i
= 0; i
< ( render_y1
- render_y0
); i
++ )
405 int y_start
= y
>> SCALE_SHIFT
;
407 int *run_weights
= filter_weights
+
408 ( ( y
>> ( SCALE_SHIFT
- SUBSAMPLE_BITS
) ) & SUBSAMPLE_MASK
) *
409 filter
->x
.n
* filter
->y
.n
* SUBSAMPLE
;
411 guint32 tcolor1
, tcolor2
;
413 guchar
*outbuf
= dest_buf
+ dest_rowstride
* i
;
414 guchar
*outbuf_end
= outbuf
+ dest_channels
* ( render_x1
- render_x0
);
416 if ( ( ( i
+ check_y
) >> check_shift
) & 1 )
427 for ( j
= 0; j
< filter
->y
.n
; j
++ )
430 line_bufs
[ j
] = ( guchar
* ) src_buf
;
431 else if ( y_start
< src_height
)
432 line_bufs
[ j
] = ( guchar
* ) src_buf
+ src_rowstride
* y_start
;
434 line_bufs
[ j
] = ( guchar
* ) src_buf
+ src_rowstride
* ( src_height
- 1 );
440 x
= render_x0
* x_step
+ scaled_x_offset
;
441 x_start
= x
>> SCALE_SHIFT
;
443 while ( x_start
< 0 && outbuf
< outbuf_end
)
445 process_pixel ( run_weights
+ ( ( x
>> ( SCALE_SHIFT
- SUBSAMPLE_BITS
) ) & SUBSAMPLE_MASK
) * ( filter
->x
.n
* filter
->y
.n
),
446 filter
->x
.n
, filter
->y
.n
,
447 outbuf
, dest_x
, dest_channels
,
448 line_bufs
, src_channels
,
449 x
>> SCALE_SHIFT
, src_width
);
452 x_start
= x
>> SCALE_SHIFT
;
454 outbuf
+= dest_channels
;
457 new_outbuf
= ( *line_func
) ( run_weights
, filter
->x
.n
, filter
->y
.n
,
459 dest_buf
+ dest_rowstride
* i
+ run_end_index
* dest_channels
,
461 x
, x_step
, src_width
);
463 dest_x
+= ( new_outbuf
- outbuf
) / dest_channels
;
465 x
= ( dest_x
- check_x
+ render_x0
) * x_step
+ scaled_x_offset
;
468 while ( outbuf
< outbuf_end
)
470 process_pixel ( run_weights
+ ( ( x
>> ( SCALE_SHIFT
- SUBSAMPLE_BITS
) ) & SUBSAMPLE_MASK
) * ( filter
->x
.n
* filter
->y
.n
),
471 filter
->x
.n
, filter
->y
.n
,
472 outbuf
, dest_x
, dest_channels
,
473 line_bufs
, src_channels
,
474 x
>> SCALE_SHIFT
, src_width
);
478 outbuf
+= dest_channels
;
484 g_free ( line_bufs
);
485 g_free ( filter_weights
);
489 /* Compute weights for reconstruction by replication followed by
490 * sampling with a box filter
493 tile_make_weights ( PixopsFilterDimension
*dim
,
496 int n
= ceil ( 1 / scale
+ 1 );
497 double *pixel_weights
= g_new ( double, SUBSAMPLE
* n
);
503 dim
->weights
= pixel_weights
;
505 for ( offset
= 0; offset
< SUBSAMPLE
; offset
++ )
507 double x
= ( double ) offset
/ SUBSAMPLE
;
508 double a
= x
+ 1 / scale
;
510 for ( i
= 0; i
< n
; i
++ )
515 * ( pixel_weights
++ ) = ( MIN ( i
+ 1, a
) - x
) * scale
;
517 *( pixel_weights
++ ) = 0;
522 * ( pixel_weights
++ ) = ( MIN ( i
+ 1, a
) - i
) * scale
;
524 *( pixel_weights
++ ) = 0;
530 /* Compute weights for a filter that, for minification
531 * is the same as 'tiles', and for magnification, is bilinear
532 * reconstruction followed by a sampling with a delta function.
535 bilinear_magnify_make_weights ( PixopsFilterDimension
*dim
,
538 double * pixel_weights
;
543 if ( scale
> 1.0 ) /* Linear */
546 dim
->offset
= 0.5 * ( 1 / scale
- 1 );
550 n
= ceil ( 1.0 + 1.0 / scale
);
555 dim
->weights
= g_new ( double, SUBSAMPLE
* n
);
557 pixel_weights
= dim
->weights
;
559 for ( offset
= 0; offset
< SUBSAMPLE
; offset
++ )
561 double x
= ( double ) offset
/ SUBSAMPLE
;
563 if ( scale
> 1.0 ) /* Linear */
565 for ( i
= 0; i
< n
; i
++ )
566 *( pixel_weights
++ ) = ( ( ( i
== 0 ) ?
( 1 - x
) : x
) / scale
) * scale
;
570 double a
= x
+ 1 / scale
;
573 * ---------|--.-|----|--.-|------- SRC
574 * ------------|---------|--------- DEST
576 for ( i
= 0; i
< n
; i
++ )
581 * ( pixel_weights
++ ) = ( MIN ( i
+ 1, a
) - x
) * scale
;
583 *( pixel_weights
++ ) = 0;
588 * ( pixel_weights
++ ) = ( MIN ( i
+ 1, a
) - i
) * scale
;
590 *( pixel_weights
++ ) = 0;
597 /* Computes the integral from b0 to b1 of
599 * f(x) = x; 0 <= x < 1
600 * f(x) = 0; otherwise
602 * We combine two of these to compute the convolution of
603 * a box filter with a triangular spike.
606 linear_box_half ( double b0
, double b1
)
635 return 0.5 * ( x1
* x1
- x0
* x0
);
638 /* Compute weights for reconstructing with bilinear
639 * interpolation, then sampling with a box filter
642 bilinear_box_make_weights ( PixopsFilterDimension
*dim
,
645 int n
= ceil ( 1 / scale
+ 2.0 );
646 double *pixel_weights
= g_new ( double, SUBSAMPLE
* n
);
652 dim
->weights
= pixel_weights
;
654 for ( offset
= 0 ; offset
< SUBSAMPLE
; offset
++ )
656 double x
= ( double ) offset
/ SUBSAMPLE
;
657 double a
= x
+ 1 / scale
;
659 for ( i
= 0; i
< n
; i
++ )
661 w
= linear_box_half ( 0.5 + i
- a
, 0.5 + i
- x
);
662 w
+= linear_box_half ( 1.5 + x
- i
, 1.5 + a
- i
);
664 *( pixel_weights
++ ) = w
* scale
;
671 make_weights ( PixopsFilter
*filter
,
672 PixopsInterpType interp_type
,
676 switch ( interp_type
)
678 case PIXOPS_INTERP_NEAREST
:
679 g_assert_not_reached ();
682 case PIXOPS_INTERP_TILES
:
683 tile_make_weights ( &filter
->x
, scale_x
);
684 tile_make_weights ( &filter
->y
, scale_y
);
687 case PIXOPS_INTERP_BILINEAR
:
688 bilinear_magnify_make_weights ( &filter
->x
, scale_x
);
689 bilinear_magnify_make_weights ( &filter
->y
, scale_y
);
692 case PIXOPS_INTERP_HYPER
:
693 bilinear_box_make_weights ( &filter
->x
, scale_x
);
694 bilinear_box_make_weights ( &filter
->y
, scale_y
);
701 yuv422_scale ( guchar
*dest_buf
,
708 gboolean dest_has_alpha
,
709 const guchar
*src_buf
,
714 gboolean src_has_alpha
,
717 PixopsInterpType interp_type
)
720 PixopsLineFunc line_func
;
723 gboolean found_mmx
= pixops_have_mmx();
726 //g_return_if_fail ( !( dest_channels == 3 && dest_has_alpha ) );
727 //g_return_if_fail ( !( src_channels == 3 && src_has_alpha ) );
728 //g_return_if_fail ( !( src_has_alpha && !dest_has_alpha ) );
730 if ( scale_x
== 0 || scale_y
== 0 )
733 if ( interp_type
== PIXOPS_INTERP_NEAREST
)
735 pixops_scale_nearest ( dest_buf
, render_x0
, render_y0
, render_x1
, render_y1
,
737 src_buf
, src_width
, src_height
, src_rowstride
,
742 filter
.overall_alpha
= 1.0;
743 make_weights ( &filter
, interp_type
, scale_x
, scale_y
);
745 if ( filter
.x
.n
== 2 && filter
.y
.n
== 2 )
750 //fprintf( stderr, "rescale: using mmx\n" );
751 line_func
= scale_line_22_yuv_mmx_stub
;
756 line_func
= scale_line_22_yuv
;
759 line_func
= scale_line
;
761 pixops_process ( dest_buf
, render_x0
, render_y0
, render_x1
, render_y1
,
762 dest_rowstride
, dest_channels
, dest_has_alpha
,
763 src_buf
, src_width
, src_height
, src_rowstride
, src_channels
,
764 src_has_alpha
, scale_x
, scale_y
, 0, 0, 0, 0, 0,
765 &filter
, line_func
);
767 g_free ( filter
.x
.weights
);
768 g_free ( filter
.y
.weights
);