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