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
287// Creates an image with the Mac OS X Aqua stripes, to be used
288// as a background
289std::unique_ptr<wxImage> CreateAquaBackground(int width, int height, int offset)
290{
291 auto image = std::make_unique<wxImage>(width, height);
292 unsigned char *ip = image->GetData();
293 unsigned char val[4] = {231, 239, 255, 239};
294 unsigned char v;
295 int x, y;
296
297 for(y=0; y<height; y++) {
298 v = val[(y+offset)%4];
299 for(x=0; x<width*3; x++)
300 *ip++ = v;
301 }
302
303 return image;
304}
305
306std::unique_ptr<wxImage> CreateSysBackground
307#ifdef USE_AQUA_THEME
308 (int width, int height, int offset, wxColour colour)
309#else
310 (int width, int height, int WXUNUSED(offset), wxColour colour)
311#endif
312{
313 #ifdef USE_AQUA_THEME
314 return CreateAquaBackground(width, height, offset);
315 #else
316 return CreateBackground(width, height, colour);
317 #endif
318}
319
324void PasteSubImage( wxImage * background, wxImage * foreground, int xoff, int yoff )
325{
326
327 unsigned char *bg = background->GetData();
328 unsigned char *fg = foreground->GetData();
329 unsigned char *bgAlpha = background->HasAlpha() ? background->GetAlpha() : NULL;
330 unsigned char *fgAlpha = foreground->HasAlpha() ? foreground->GetAlpha() : NULL;
331 // For testing... Set as if no alpha in foreground....
332 // fgAlpha = NULL;
333
334 int bgWidth = background->GetWidth();
335 int bgHeight = background->GetHeight();
336 int fgWidth = foreground->GetWidth();
337 int fgHeight = foreground->GetHeight();
338
339 int wCutoff = fgWidth;
340 int hCutoff = fgHeight;
341
342 // If the masked foreground + offset is bigger than the background, masking
343 // should only occur within these bounds of the foreground image
344 wCutoff = (bgWidth - xoff > wCutoff) ? wCutoff : bgWidth - xoff;
345 hCutoff = (bgHeight - yoff > hCutoff) ? hCutoff : bgHeight - yoff;
346
347 // Go through the foreground image bit by bit and place it on to the
348 // background, at an offset of xoff,yoff.
349 // Don't go beyond the size of the background image,
350 // or the foreground image.
351 int y;
352 unsigned char *bkp;
353 unsigned char *fgp;
354 unsigned char *bgAlphap;
355 unsigned char *fgAlphap;
356 for (y = 0; y < hCutoff; y++) {
357 // RGB bytes
358 bkp = bg + 3 * ((y + yoff) * bgWidth + xoff);
359 fgp = fg + 3 * ( y * fgWidth);
360 memcpy( bkp, fgp, 3 * wCutoff );
361
362 // Alpha bytes.
363 if( bgAlpha )
364 {
365 bgAlphap = bgAlpha + ((y+yoff) * bgWidth + xoff );
366 if( fgAlpha )
367 {
368 fgAlphap = fgAlpha + (y * fgWidth );
369 memcpy( bgAlphap, fgAlphap, wCutoff );
370 }
371 else
372 {
373 memset( bgAlphap, 255, wCutoff );
374 }
375 }
376 }
377}
378
381wxImage GetSubImageWithAlpha( const wxImage & Src, const wxRect &rect )
382{
383 //First part of this code is lifted from wxImage::GetSubImage() source code.
384 wxImage image;
385
386 wxCHECK_MSG( Src.Ok(), image, wxT("invalid image") );
387
388 wxCHECK_MSG( (rect.GetLeft()>=0) && (rect.GetTop()>=0) && (
389 rect.GetRight()<=Src.GetWidth()) && (rect.GetBottom()<=Src.GetHeight()),
390 image, wxT("invalid subimage size") );
391
392 int subwidth=rect.GetWidth();
393 const int subheight=rect.GetHeight();
394
395 image.Create( subwidth, subheight, false );
396
397 unsigned char *subdata = image.GetData(), *data=Src.GetData();
398
399 wxCHECK_MSG( subdata, image, wxT("unable to create image") );
400
401 // JKC: Quick hack - don't deal with masks - need to understand macro M_IMGDATA first.
402// if (Src.M_IMGDATA->m_hasMask)
403// image.SetMaskColour( Src.M_IMGDATA->m_maskRed, Src.M_IMGDATA->m_maskGreen, Src.M_IMGDATA->m_maskBlue );
404
405 int subleft=3*rect.GetLeft();
406 int width=3*Src.GetWidth();
407 subwidth*=3;
408
409 data+=rect.GetTop()*width+subleft;
410
411 for (long j = 0; j < subheight; ++j)
412 {
413 memcpy( subdata, data, subwidth);
414 subdata+=subwidth;
415 data+=width;
416 }
417
418 image.InitAlpha();
419 if( !Src.HasAlpha() )
420 return image;
421 // OK, so we've copied the RGB data.
422 // Now do the Alpha channel.
423 //wxASSERT( Src.HasAlpha() );
424
425 subleft/=3;
426 width/=3;
427 subwidth/=3;
428
429 data =Src.GetAlpha();
430 subdata =image.GetAlpha();
431
432 data+=rect.GetTop()*width+subleft;
433
434 for (long j = 0; j < subheight; ++j)
435 {
436 memcpy( subdata, data, subwidth);
437 subdata+=subwidth;
438 data+=width;
439 }
440 return image;
441}
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 > CreateAquaBackground(int width, int height, int offset)
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)