Audacity 3.2.0
ImageManipulation.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 ImageManipulation.cpp
6
7 Dominic Mazzoni
8 James Crook
9
10 wxWidgets license (Dominic to confirm).
11
12********************************************************************//*********************************************************************/
23
24
25#include "ImageManipulation.h"
26
27#include <wx/colour.h>
28#include <wx/image.h>
29
30#include "AllThemeResources.h"
31#include "Theme.h"
32
37std::unique_ptr<wxImage> ChangeImageColour(wxImage * srcImage, wxColour & dstColour)
38{
39 unsigned char *src = srcImage->GetData();
40 wxColour c;
41 c.Set(src[0], src[1], src[2]);
42 return ChangeImageColour(srcImage, c, dstColour);
43}
44
47std::unique_ptr<wxImage> ChangeImageColour(wxImage * srcImage,
48 wxColour & srcColour,
49 wxColour & dstColour)
50{
51 // This function takes a source image, which it assumes to
52 // be grayscale, and smoothly changes the overall color
53 // to the specified color, and returns the result as a
54 // NEW image. This works well for grayscale 3D images.
55 // Audacity uses this routines to make the buttons
56 // (skip-start, play, stop, record, skip-end) adapt to
57 // the color scheme of the user.
58
59 unsigned char *src = srcImage->GetData();
60 int width = srcImage->GetWidth();
61 int height = srcImage->GetHeight();
62
63 auto dstImage = std::make_unique<wxImage>(width, height);
64 unsigned char *dst = dstImage->GetData();
65
66
67 //Get the source color
68 int srcVal[3], srcOpp[3];
69 srcVal[0] = srcColour.Red();
70 srcVal[1] = srcColour.Green();
71 srcVal[2] = srcColour.Blue();
72
73 int dstVal[3], dstOpp[3];
74 dstVal[0] = dstColour.Red();
75 dstVal[1] = dstColour.Green();
76 dstVal[2] = dstColour.Blue();
77
78 int i;
79 for (i = 0; i < 3; i++) {
80 srcOpp[i] = 256 - srcVal[i]; // avoid zero!
81 dstOpp[i] = 255 - dstVal[i];
82 }
83
84 int c = 0;
85 for (i = 0; i < width * height * 3; i++) {
86 int s = (int) *src;
87
88 if (s >= srcVal[c])
89 *dst++ = dstVal[c] + dstOpp[c] * (s - srcVal[c]) / srcOpp[c];
90
91 else
92 *dst++ = dstVal[c] * s / srcVal[c];
93 src++;
94 c = (c + 1) % 3;
95 }
96
97 if (srcImage->HasAlpha()) {
98 // Preserve transparencies
99 dstImage->InitAlpha();
100 memcpy(dstImage->GetAlpha(), srcImage->GetAlpha(), width * height);
101 }
102
103 return dstImage;
104}
105
111std::unique_ptr<wxImage> OverlayImage(wxImage * background, wxImage * foreground,
112 wxImage * mask, int xoff, int yoff)
113{
114 unsigned char *bg = background->GetData();
115 unsigned char *fg = foreground->GetData();
116 unsigned char *mk = mask->GetData();
117
118 int bgWidth = background->GetWidth();
119 int bgHeight = background->GetHeight();
120 int fgWidth = foreground->GetWidth();
121 int fgHeight = foreground->GetHeight();
122 int mkWidth = mask->GetWidth();
123 int mkHeight = mask->GetHeight();
124
125
126 //Now, determine the dimensions of the images to be masked together
127 //on top of the background. This should be equal to the area of the
128 //smaller of the foreground and the mask, as long as it is
129 //within the area of the background, given the offset.
130
131 //Make sure the foreground size is no bigger than the mask
132 int wCutoff = (fgWidth < mkWidth) ? fgWidth : mkWidth;
133 int hCutoff = (fgHeight < mkHeight) ? fgHeight : mkHeight;
134
135
136 // If the masked foreground + offset is bigger than the background, masking
137 // should only occur within these bounds of the foreground image
138 wCutoff = (bgWidth - xoff > wCutoff) ? wCutoff : bgWidth - xoff;
139 hCutoff = (bgHeight - yoff > hCutoff) ? hCutoff : bgHeight - yoff;
140
141
142 //Make a NEW image the size of the background
143 auto dstImage = std::make_unique<wxImage>(bgWidth, bgHeight);
144 unsigned char *dst = dstImage->GetData();
145 memcpy(dst, bg, bgWidth * bgHeight * 3);
146
147
148 // Go through the foreground image bit by bit and mask it on to the
149 // background, at an offset of xoff,yoff.
150 // BUT...Don't go beyond the size of the background image,
151 // the foreground image, or the mask
152 int x, y;
153 for (y = 0; y < hCutoff; y++) {
154
155 unsigned char *bkp = bg + 3 * ((y + yoff) * bgWidth + xoff);
156 unsigned char *dstp = dst + 3 * ((y + yoff) * bgWidth + xoff);
157
158 for (x = 0; x < wCutoff; x++) {
159
160 int value = mk[3 * (y * mkWidth + x)];
161 int opp = 255 - value;
162
163 for (int c = 0; c < 3; c++)
164 dstp[x * 3 + c] =
165 ((bkp[x * 3 + c] * opp) +
166 (fg[3 * (y * fgWidth + x) + c] * value)) / 255;
167 }
168 }
169 return dstImage;
170}
171
177std::unique_ptr<wxImage> OverlayImage(teBmps eBack, teBmps eForeground,
178 int xoff, int yoff)
179{
180 wxImage imgBack(theTheme.Image( eBack ));
181 wxImage imgFore(theTheme.Image( eForeground ));
182
183
184 // TMP: dmazzoni - just so the code runs even though not all of
185 // our images have transparency...
186 if (!imgFore.HasAlpha())
187 return std::make_unique<wxImage>(imgBack);
188
189
190 wxASSERT( imgFore.HasAlpha() );
191
192 unsigned char *bg = imgBack.GetData();
193 unsigned char *fg = imgFore.GetData();
194 unsigned char *mk = imgFore.GetAlpha();
195
196 int bgWidth = imgBack.GetWidth();
197 int bgHeight = imgBack.GetHeight();
198 int fgWidth = imgFore.GetWidth();
199 int fgHeight = imgFore.GetHeight();
200
201
202 //Now, determine the dimensions of the images to be masked together
203 //on top of the background. This should be equal to the area of the
204 //smaller of the foreground and the mask, as long as it is
205 //within the area of the background, given the offset.
206
207 //Make sure the foreground size is no bigger than the mask
208 int wCutoff = fgWidth;
209 int hCutoff = fgHeight;
210
211
212 // If the masked foreground + offset is bigger than the background, masking
213 // should only occur within these bounds of the foreground image
214 wCutoff = (bgWidth - xoff > wCutoff) ? wCutoff : bgWidth - xoff;
215 hCutoff = (bgHeight - yoff > hCutoff) ? hCutoff : bgHeight - yoff;
216
217 //Make a NEW image the size of the background
218 auto dstImage = std::make_unique<wxImage>(bgWidth, bgHeight);
219 unsigned char *dst = dstImage->GetData();
220 memcpy(dst, bg, bgWidth * bgHeight * 3);
221
222 // If background image has tranparency, then we want to blend with the
223 // current background colour.
224 if( imgBack.HasAlpha() ){
225 unsigned char *pAlpha = imgBack.GetAlpha();
226 wxColour c = theTheme.Colour( clrMedium );
227 unsigned char onePixImage[3];
228 // GetData() guarantees RGB order [wxWidgets does the ocnversion]
229 onePixImage[ 0 ] = c.Red();
230 onePixImage[ 1 ] = c.Green();
231 onePixImage[ 2 ] = c.Blue();
232 for( int i=0;i< bgWidth*bgHeight;i++){
233 unsigned char * pPix = &dst[ 3*i];
234 float alpha = 1.0 - (pAlpha[i]/255.0);
235 pPix[0] = pPix[0] + alpha *( (int)onePixImage[0]-(int)pPix[0]);
236 pPix[1] = pPix[1] + alpha *( (int)onePixImage[1]-(int)pPix[1]);
237 pPix[2] = pPix[2] + alpha *( (int)onePixImage[2]-(int)pPix[2]);
238 }
239 }
240
241 // Go through the foreground image bit by bit and mask it on to the
242 // background, at an offset of xoff,yoff.
243 // BUT...Don't go beyond the size of the background image,
244 // the foreground image, or the mask
245 int x, y;
246 for (y = 0; y < hCutoff; y++) {
247
248 unsigned char *bkp = bg + 3 * ((y + yoff) * bgWidth + xoff);
249 unsigned char *dstp = dst + 3 * ((y + yoff) * bgWidth + xoff);
250
251 for (x = 0; x < wCutoff; x++) {
252
253 int value = mk[(y * fgWidth + x)];// Don't multiply by 3...
254 int opp = 255 - value;
255
256 for (int c = 0; c < 3; c++)
257 dstp[x * 3 + c] =
258 ((bkp[x * 3 + c] * opp) +
259 (fg[3 * (y * fgWidth + x) + c] * value)) / 255;
260 }
261 }
262 return dstImage;
263}
264
265// Creates an image with a solid background color
266std::unique_ptr<wxImage> CreateBackground(int width, int height, wxColour colour)
267{
268 auto i = std::make_unique<wxImage>(width, height);
269 unsigned char *ip;
270 int srcVal[3];
271 int x;
272
273 srcVal[0] = colour.Red();
274 srcVal[1] = colour.Green();
275 srcVal[2] = colour.Blue();
276
277 ip = i->GetData();
278 for(x=0; x<width*height; x++) {
279 *ip++ = srcVal[0];
280 *ip++ = srcVal[1];
281 *ip++ = srcVal[2];
282 }
283
284 return i;
285}
286
287std::unique_ptr<wxImage> CreateSysBackground
288 (int width, int height, int WXUNUSED(offset), wxColour colour)
289{
290 return CreateBackground(width, height, colour);
291}
292
297void PasteSubImage( wxImage * background, wxImage * foreground, int xoff, int yoff )
298{
299
300 unsigned char *bg = background->GetData();
301 unsigned char *fg = foreground->GetData();
302 unsigned char *bgAlpha = background->HasAlpha() ? background->GetAlpha() : NULL;
303 unsigned char *fgAlpha = foreground->HasAlpha() ? foreground->GetAlpha() : NULL;
304 // For testing... Set as if no alpha in foreground....
305 // fgAlpha = NULL;
306
307 int bgWidth = background->GetWidth();
308 int bgHeight = background->GetHeight();
309 int fgWidth = foreground->GetWidth();
310 int fgHeight = foreground->GetHeight();
311
312 int wCutoff = fgWidth;
313 int hCutoff = fgHeight;
314
315 // If the masked foreground + offset is bigger than the background, masking
316 // should only occur within these bounds of the foreground image
317 wCutoff = (bgWidth - xoff > wCutoff) ? wCutoff : bgWidth - xoff;
318 hCutoff = (bgHeight - yoff > hCutoff) ? hCutoff : bgHeight - yoff;
319
320 // Go through the foreground image bit by bit and place it on to the
321 // background, at an offset of xoff,yoff.
322 // Don't go beyond the size of the background image,
323 // or the foreground image.
324 int y;
325 unsigned char *bkp;
326 unsigned char *fgp;
327 unsigned char *bgAlphap;
328 unsigned char *fgAlphap;
329 for (y = 0; y < hCutoff; y++) {
330 // RGB bytes
331 bkp = bg + 3 * ((y + yoff) * bgWidth + xoff);
332 fgp = fg + 3 * ( y * fgWidth);
333 memcpy( bkp, fgp, 3 * wCutoff );
334
335 // Alpha bytes.
336 if( bgAlpha )
337 {
338 bgAlphap = bgAlpha + ((y+yoff) * bgWidth + xoff );
339 if( fgAlpha )
340 {
341 fgAlphap = fgAlpha + (y * fgWidth );
342 memcpy( bgAlphap, fgAlphap, wCutoff );
343 }
344 else
345 {
346 memset( bgAlphap, 255, wCutoff );
347 }
348 }
349 }
350}
351
354wxImage GetSubImageWithAlpha( const wxImage & Src, const wxRect &rect )
355{
356 //First part of this code is lifted from wxImage::GetSubImage() source code.
357 wxImage image;
358
359 wxCHECK_MSG( Src.Ok(), image, wxT("invalid image") );
360
361 wxCHECK_MSG( (rect.GetLeft()>=0) && (rect.GetTop()>=0) && (
362 rect.GetRight()<=Src.GetWidth()) && (rect.GetBottom()<=Src.GetHeight()),
363 image, wxT("invalid subimage size") );
364
365 int subwidth=rect.GetWidth();
366 const int subheight=rect.GetHeight();
367
368 image.Create( subwidth, subheight, false );
369
370 unsigned char *subdata = image.GetData(), *data=Src.GetData();
371
372 wxCHECK_MSG( subdata, image, wxT("unable to create image") );
373
374 // JKC: Quick hack - don't deal with masks - need to understand macro M_IMGDATA first.
375// if (Src.M_IMGDATA->m_hasMask)
376// image.SetMaskColour( Src.M_IMGDATA->m_maskRed, Src.M_IMGDATA->m_maskGreen, Src.M_IMGDATA->m_maskBlue );
377
378 int subleft=3*rect.GetLeft();
379 int width=3*Src.GetWidth();
380 subwidth*=3;
381
382 data+=rect.GetTop()*width+subleft;
383
384 for (long j = 0; j < subheight; ++j)
385 {
386 memcpy( subdata, data, subwidth);
387 subdata+=subwidth;
388 data+=width;
389 }
390
391 image.InitAlpha();
392 if( !Src.HasAlpha() )
393 return image;
394 // OK, so we've copied the RGB data.
395 // Now do the Alpha channel.
396 //wxASSERT( Src.HasAlpha() );
397
398 subleft/=3;
399 width/=3;
400 subwidth/=3;
401
402 data =Src.GetAlpha();
403 subdata =image.GetAlpha();
404
405 data+=rect.GetTop()*width+subleft;
406
407 for (long j = 0; j < subheight; ++j)
408 {
409 memcpy( subdata, data, subwidth);
410 subdata+=subwidth;
411 data+=width;
412 }
413 return image;
414}
wxImage(22, 22)
wxT("CloseDown"))
wxImage GetSubImageWithAlpha(const wxImage &Src, const wxRect &rect)
std::unique_ptr< wxImage > CreateSysBackground(int width, int height, int WXUNUSED(offset), wxColour colour)
std::unique_ptr< wxImage > OverlayImage(wxImage *background, wxImage *foreground, wxImage *mask, int xoff, int yoff)
void PasteSubImage(wxImage *background, wxImage *foreground, int xoff, int yoff)
std::unique_ptr< wxImage > CreateBackground(int width, int height, wxColour colour)
std::unique_ptr< wxImage > ChangeImageColour(wxImage *srcImage, wxColour &dstColour)
int teBmps
THEME_API Theme theTheme
Definition: Theme.cpp:82
wxColour & Colour(int iIndex)
wxImage & Image(int iIndex)