1/* Copyright (C) 2008 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10** GNU General Public License for more details.
11*/
12#include "android/skin/scaler.h"
13#include <stdint.h>
14#include <math.h>
15
16struct SkinScaler {
17    double  scale;
18    double  xdisp, ydisp;
19    double  invscale;
20    int     valid;
21};
22
23static SkinScaler  _scaler0;
24
25SkinScaler*
26skin_scaler_create( void )
27{
28    _scaler0.scale    = 1.0;
29    _scaler0.xdisp    = 0.0;
30    _scaler0.ydisp    = 0.0;
31    _scaler0.invscale = 1.0;
32    return &_scaler0;
33}
34
35/* change the scale of a given scaler. returns 0 on success, or -1 in case of
36 * problem (unsupported scale) */
37int
38skin_scaler_set( SkinScaler*  scaler, double  scale, double xdisp, double ydisp )
39{
40    /* right now, we only support scales in the 0.5 .. 1.0 range */
41    if (scale < 0.1)
42        scale = 0.1;
43    else if (scale > 6.0)
44        scale = 6.0;
45
46    scaler->scale    = scale;
47    scaler->xdisp    = xdisp;
48    scaler->ydisp    = ydisp;
49    scaler->invscale = 1/scale;
50    scaler->valid    = 1;
51
52    return 0;
53}
54
55void
56skin_scaler_free( SkinScaler*  scaler )
57{
58    scaler=scaler;
59}
60
61typedef struct {
62    SDL_Rect    rd;         /* destination rectangle */
63    int         sx, sy;     /* source start position in 16.16 format */
64    int         ix, iy;     /* source increments in 16.16 format */
65    int         src_pitch;
66    int         src_w;
67    int         src_h;
68    int         dst_pitch;
69    uint8_t*    dst_line;
70    uint8_t*    src_line;
71    double      scale;
72} ScaleOp;
73
74
75#define  ARGB_SCALE_GENERIC       scale_generic
76#define  ARGB_SCALE_05_TO_10      scale_05_to_10
77#define  ARGB_SCALE_UP_BILINEAR   scale_up_bilinear
78/* #define  ARGB_SCALE_UP_QUICK_4x4  scale_up_quick_4x4 UNUSED */
79
80#include "android/skin/argb.h"
81
82
83void
84skin_scaler_get_scaled_rect( SkinScaler*  scaler,
85                             SkinRect*    srect,
86                             SkinRect*    drect )
87{
88    int sx = srect->pos.x;
89    int sy = srect->pos.y;
90    int sw = srect->size.w;
91    int sh = srect->size.h;
92    double scale = scaler->scale;
93
94    if (!scaler->valid) {
95        drect[0] = srect[0];
96        return;
97    }
98
99    drect->pos.x = (int)(sx * scale + scaler->xdisp);
100    drect->pos.y = (int)(sy * scale + scaler->ydisp);
101    drect->size.w = (int)(ceil((sx + sw) * scale + scaler->xdisp)) - drect->pos.x;
102    drect->size.h = (int)(ceil((sy + sh) * scale + scaler->ydisp)) - drect->pos.y;
103}
104
105void
106skin_scaler_scale( SkinScaler*   scaler,
107                   SDL_Surface*  dst_surface,
108                   SDL_Surface*  src_surface,
109                   int           sx,
110                   int           sy,
111                   int           sw,
112                   int           sh )
113{
114    ScaleOp   op;
115
116    if ( !scaler->valid )
117        return;
118
119    SDL_LockSurface( src_surface );
120    SDL_LockSurface( dst_surface );
121    {
122        op.scale     = scaler->scale;
123        op.src_pitch = src_surface->pitch;
124        op.src_line  = src_surface->pixels;
125        op.src_w     = src_surface->w;
126        op.src_h     = src_surface->h;
127        op.dst_pitch = dst_surface->pitch;
128        op.dst_line  = dst_surface->pixels;
129
130        /* compute the destination rectangle */
131        op.rd.x = (int)(sx * scaler->scale + scaler->xdisp);
132        op.rd.y = (int)(sy * scaler->scale + scaler->ydisp);
133        op.rd.w = (int)(ceil((sx + sw) * scaler->scale + scaler->xdisp)) - op.rd.x;
134        op.rd.h = (int)(ceil((sy + sh) * scaler->scale + scaler->ydisp)) - op.rd.y;
135
136        /* compute the starting source position in 16.16 format
137         * and the corresponding increments */
138        op.sx = (int)((op.rd.x - scaler->xdisp) * scaler->invscale * 65536);
139        op.sy = (int)((op.rd.y - scaler->ydisp) * scaler->invscale * 65536);
140
141        op.ix = (int)( scaler->invscale * 65536 );
142        op.iy = op.ix;
143
144        op.dst_line += op.rd.x*4 + op.rd.y*op.dst_pitch;
145
146        if (op.scale >= 0.5 && op.scale <= 1.0)
147            scale_05_to_10( &op );
148        else if (op.scale > 1.0)
149            scale_up_bilinear( &op );
150        else
151            scale_generic( &op );
152    }
153
154    // The optimized scale functions in argb.h assume the destination is ARGB.
155    // If that's not the case, do a channel reorder now.
156    if (dst_surface->format->Rshift != 16 ||
157        dst_surface->format->Gshift !=  8 ||
158        dst_surface->format->Bshift !=  0)
159    {
160        uint32_t rshift = dst_surface->format->Rshift;
161        uint32_t gshift = dst_surface->format->Gshift;
162        uint32_t bshift = dst_surface->format->Bshift;
163        uint32_t ashift = dst_surface->format->Ashift;
164        uint32_t amask  = dst_surface->format->Amask; // may be 0x00
165        int x, y;
166
167        for (y = 0; y < op.rd.h; y++)
168        {
169            uint32_t* line = (uint32_t*)(op.dst_line + y*op.dst_pitch);
170            for (x = 0; x < op.rd.w; x++) {
171                uint32_t r = (line[x] & 0x00ff0000) >> 16;
172                uint32_t g = (line[x] & 0x0000ff00) >>  8;
173                uint32_t b = (line[x] & 0x000000ff) >>  0;
174                uint32_t a = (line[x] & 0xff000000) >> 24;
175                line[x] = (r << rshift) | (g << gshift) | (b << bshift) |
176                          ((a << ashift) & amask);
177            }
178        }
179    }
180
181    SDL_UnlockSurface( dst_surface );
182    SDL_UnlockSurface( src_surface );
183
184    SDL_UpdateRects( dst_surface, 1, &op.rd );
185}
186