-
Notifications
You must be signed in to change notification settings - Fork 84
Expand file tree
/
Copy pathjpegloader_common.cpp
More file actions
213 lines (179 loc) · 6.6 KB
/
jpegloader_common.cpp
File metadata and controls
213 lines (179 loc) · 6.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "jpegloader.h"
#include "tier0/dbg.h"
#include "tier1/utlvector.h"
#include "tier0/vprof.h"
#include "jpeglib/jpeglib.h"
//-----------------------------------------------------------------------------
// Purpose: Takes a RGBA image buffer and resizes it using linear interpolation.
//
// Params: bufRGBA should contain the current image, nWidth and nHeight should describe
// the current images dimensions. nNewWidth and nNewHeight should be the new target size,
// one, but not both, of these may be -1 which will indicate to preserve aspect ratio
// and size that dimension to match the aspect ratio adjustment applied to the other
// dimension which must then be specified explicitly. nNewWidth and nNewHeight may
// be modified by the function and will specify the final width/height after the
// function returns succesfully. If both nNewWidth and nNewHeight are specified
// the content will be scaled without changing the aspect ratio and black letterboxing
// will be added if appropriate
//-----------------------------------------------------------------------------
bool BResizeImageInternal( CUtlBuffer &bufImage, int nWidth, int nHeight, int &nNewWidth, int &nNewHeight, bool bIsRGBA = true )
{
VPROF_BUDGET( "BResizeImageRGBA", VPROF_BUDGETGROUP_OTHER_VGUI );
CUtlBuffer bufImageOut;
if ( nWidth == 0 || nHeight == 0 )
return false;
// Must specify at least one, then we'll compute the other to preserve aspect ratio if it's set to -1
if ( nNewWidth == - 1 && nNewHeight == -1 )
return false;
if ( nNewHeight == -1 )
{
float flAspect = (float)nNewWidth/(float)nWidth;
nNewHeight = (int)(flAspect*nHeight);
}
else if ( nNewWidth == -1 )
{
float flAspect = (float)nNewHeight/(float)nHeight;
nNewWidth = (int)(flAspect*nWidth);
}
if ( nNewWidth == 0 || nNewHeight == 0 )
return false;
int nNewContentHeight = nNewHeight;
int nNewContentWidth = nNewWidth;
// Calculate the width/height of the actual content
if ( nWidth/(float)nHeight > nNewContentWidth/(float)nNewContentHeight )
nNewContentHeight = ( nNewContentWidth * nHeight )/nWidth;
else
nNewContentWidth = ( nNewContentHeight * nWidth )/nHeight;
int bytesPerPixel = bIsRGBA ? 4 : 3;
bufImageOut.EnsureCapacity( nNewWidth*nNewHeight*bytesPerPixel );
bufImageOut.SeekPut( CUtlBuffer::SEEK_HEAD, nNewWidth*nNewHeight*bytesPerPixel );
// Letterboxing
int nPaddingTop = (nNewHeight - nNewContentHeight)/2;
int nPaddingBottom = nNewHeight - nNewContentHeight - nPaddingTop;
int nPaddingLeft = (nNewWidth - nNewContentWidth)/2;
int nPaddingRight = nNewWidth - nNewContentWidth - nPaddingLeft;
Assert( nPaddingTop + nPaddingBottom + nNewContentHeight == nNewHeight );
Assert( nPaddingLeft + nPaddingRight + nNewContentWidth == nNewWidth );
if ( nPaddingLeft > 0 ||
nPaddingRight > 0 ||
nPaddingTop > 0 ||
nPaddingBottom > 0 )
{
Q_memset( bufImageOut.Base(), 0, nNewWidth*nNewHeight*bytesPerPixel );
}
byte *pBits = (byte*)bufImageOut.Base();
int nOriginalStride = nWidth;
int nTargetStride = nNewWidth;
float flXRatio = (float)(nWidth-1)/(float)nNewContentWidth;
float flYRatio = (float)(nHeight-1)/(float)nNewContentHeight;
byte *pSrcBits = (byte*)bufImage.Base();
for( int yNew=0; yNew<nNewContentHeight; ++yNew )
{
int y = (int)(flYRatio * yNew);
float yDiff = (flYRatio * yNew) - y;
for ( int xNew=0; xNew<nNewContentWidth; ++xNew )
{
int x = (int)(flXRatio * xNew);
float xDiff = (flXRatio * xNew) - x;
int aOffset = (x+(y*nOriginalStride))*bytesPerPixel;
int bOffset = aOffset+bytesPerPixel;
int cOffset = aOffset + (nOriginalStride*bytesPerPixel);
int dOffset = cOffset+bytesPerPixel;
byte red = (byte)(
pSrcBits[aOffset]*(1-xDiff)*(1-yDiff)
+pSrcBits[bOffset]*(xDiff)*(1-yDiff)
+pSrcBits[cOffset]*(yDiff)*(1-xDiff)
+pSrcBits[dOffset]*(xDiff)*(yDiff)
);
byte green = (byte)(
pSrcBits[aOffset+1]*(1-xDiff)*(1-yDiff)
+pSrcBits[bOffset+1]*(xDiff)*(1-yDiff)
+pSrcBits[cOffset+1]*(yDiff)*(1-xDiff)
+pSrcBits[dOffset+1]*(xDiff)*(yDiff)
);
byte blue = (byte)(
pSrcBits[aOffset+2]*(1-xDiff)*(1-yDiff)
+pSrcBits[bOffset+2]*(xDiff)*(1-yDiff)
+pSrcBits[cOffset+2]*(yDiff)*(1-xDiff)
+pSrcBits[dOffset+2]*(xDiff)*(yDiff)
);
byte alpha = 0;
if ( bytesPerPixel == 4 )
{
alpha = (byte)(
pSrcBits[aOffset+3]*(1-xDiff)*(1-yDiff)
+pSrcBits[bOffset+3]*(xDiff)*(1-yDiff)
+pSrcBits[cOffset+3]*(yDiff)*(1-xDiff)
+pSrcBits[dOffset+3]*(xDiff)*(yDiff)
);
}
int targetOffset = (nPaddingLeft+xNew+((nPaddingTop+yNew)*nTargetStride))*bytesPerPixel;
pBits[targetOffset] = red;
pBits[targetOffset+1] = green;
pBits[targetOffset+2] = blue;
if ( bytesPerPixel == 4 )
pBits[targetOffset+3] = alpha;
}
}
bufImage.Swap( bufImageOut );
return true;
}
// Resize an RGB image using linear interpolation
bool BResizeImageRGB( CUtlBuffer &bufRGB, int nWidth, int nHeight, int &nNewWidth, int &nNewHeight )
{
return BResizeImageInternal( bufRGB, nWidth, nHeight, nNewWidth, nNewHeight, false );
}
// Resize an RGBA image using linear interpolation
bool BResizeImageRGBA( CUtlBuffer &bufRGB, int nWidth, int nHeight, int &nNewWidth, int &nNewHeight )
{
return BResizeImageInternal( bufRGB, nWidth, nHeight, nNewWidth, nNewHeight, true );
}
// Convert an RGB image to RGBA with 100% opacity
bool BConvertRGBToRGBA( CUtlBuffer &bufRGB, int nWidth, int nHeight )
{
CUtlBuffer bufRGBA;
bufRGBA.EnsureCapacity( nWidth*nHeight*4 );
byte *pBits = (byte*)bufRGB.Base();
byte *pBitsOut = (byte*)bufRGBA.Base();
for( int y=0; y<nHeight; ++y )
{
for( int x=0; x<nWidth; ++x )
{
*pBitsOut = *pBits;
*(pBitsOut+1) = *(pBits+1);
*(pBitsOut+2) = *(pBits+2);
*(pBitsOut+3) = 255;
pBitsOut += 4;
pBits += 3;
}
}
bufRGB.Swap( bufRGBA );
return true;
}
// Convert an RGBA image to RGB using opacity against a given solid background
bool BConvertRGBAToRGB( CUtlBuffer &bufRGBA, int nWidth, int nHeight, Color colorBG )
{
CUtlBuffer bufRGB;
bufRGB.EnsureCapacity( nWidth*nHeight*3 );
byte *pBits = (byte*)bufRGBA.Base();
byte *pBitsOut = (byte*)bufRGB.Base();
for( int y=0; y<nHeight; ++y )
{
for( int x=0; x<nWidth; ++x )
{
float fOpacity = *(pBits+3) / 255.0;
*pBitsOut = *pBits * fOpacity + (1.0 - fOpacity) * colorBG.r();
*(pBitsOut+1) = *(pBits+1) * fOpacity + (1.0 - fOpacity) * colorBG.g();
*(pBitsOut+2) = *(pBits+2) * fOpacity + (1.0 - fOpacity) * colorBG.b();
pBitsOut += 3;
pBits += 4;
}
}
bufRGB.Swap( bufRGBA );
return true;
}