Audacity  3.0.3
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 
37 std::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 
47 std::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 
111 std::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 
177 std::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
266 std::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
289 std::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 
306 std::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 
324 void 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 
381 wxImage 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 }
teBmps
int teBmps
Definition: ImageManipulation.h:48
AllThemeResources.h
CreateBackground
std::unique_ptr< wxImage > CreateBackground(int width, int height, wxColour colour)
Definition: ImageManipulation.cpp:266
OverlayImage
std::unique_ptr< wxImage > OverlayImage(wxImage *background, wxImage *foreground, wxImage *mask, int xoff, int yoff)
Definition: ImageManipulation.cpp:111
PasteSubImage
void PasteSubImage(wxImage *background, wxImage *foreground, int xoff, int yoff)
Definition: ImageManipulation.cpp:324
Theme.h
ThemeBase::Image
wxImage & Image(int iIndex)
Definition: Theme.cpp:1217
ChangeImageColour
std::unique_ptr< wxImage > ChangeImageColour(wxImage *srcImage, wxColour &dstColour)
Definition: ImageManipulation.cpp:37
theTheme
THEME_API Theme theTheme
Definition: Theme.cpp:79
CreateSysBackground
std::unique_ptr< wxImage > CreateSysBackground(int width, int height, int WXUNUSED(offset), wxColour colour)
Definition: ImageManipulation.cpp:310
GetSubImageWithAlpha
wxImage GetSubImageWithAlpha(const wxImage &Src, const wxRect &rect)
Definition: ImageManipulation.cpp:381
CreateAquaBackground
std::unique_ptr< wxImage > CreateAquaBackground(int width, int height, int offset)
Definition: ImageManipulation.cpp:289
ThemeBase::Colour
wxColour & Colour(int iIndex)
Definition: Theme.cpp:1189
ImageManipulation.h