mirror of
https://github.com/cookiengineer/audacity
synced 2025-10-21 14:02:57 +02:00
Locate and position the current Audacity source code, and clear a variety of old junk out of the way into junk-branches
This commit is contained in:
418
src/ImageManipulation.cpp
Normal file
418
src/ImageManipulation.cpp
Normal file
@@ -0,0 +1,418 @@
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
ImageManipulation.cpp
|
||||
|
||||
Dominic Mazzoni
|
||||
James Crook
|
||||
|
||||
wxWidgets license (Dominic to confirm).
|
||||
|
||||
********************************************************************//*!
|
||||
|
||||
\file ImageManipulation.cpp
|
||||
|
||||
Provides Image Manipulation functions.
|
||||
|
||||
wxWidgets misses some important functions involving cutting and
|
||||
pasting bitmaps, and (in version 2.6.1) is patchy in support of alpha
|
||||
channel. This collection of functions fills that gap.
|
||||
|
||||
*//*********************************************************************/
|
||||
|
||||
|
||||
#include <wx/image.h>
|
||||
|
||||
#include "ImageManipulation.h"
|
||||
|
||||
/// This looks at the first pixel in the image, and shifts
|
||||
/// the entire image by the vector difference between that
|
||||
/// pixel and the dstColour. For better control, use
|
||||
/// ChangeImageColour(wxImage, wxColour*, wxColour*) below
|
||||
wxImage *ChangeImageColour(wxImage * srcImage, wxColour & dstColour)
|
||||
{
|
||||
unsigned char *src = srcImage->GetData();
|
||||
wxColour c;
|
||||
c.Set(src[0], src[1], src[2]);
|
||||
return ChangeImageColour(srcImage, c, dstColour);
|
||||
}
|
||||
|
||||
///This will explicitly shift the image color from
|
||||
///srcColour to dstColour.
|
||||
wxImage *ChangeImageColour(wxImage * srcImage,
|
||||
wxColour & srcColour,
|
||||
wxColour & dstColour)
|
||||
{
|
||||
// This function takes a source image, which it assumes to
|
||||
// be grayscale, and smoothly changes the overall color
|
||||
// to the specified color, and returns the result as a
|
||||
// new image. This works well for grayscale 3D images.
|
||||
// Audacity uses this routines to make the buttons
|
||||
// (skip-start, play, stop, record, skip-end) adapt to
|
||||
// the color scheme of the user.
|
||||
|
||||
unsigned char *src = srcImage->GetData();
|
||||
int width = srcImage->GetWidth();
|
||||
int height = srcImage->GetHeight();
|
||||
|
||||
wxImage * dstImage = new wxImage(width, height);
|
||||
unsigned char *dst = dstImage->GetData();
|
||||
|
||||
|
||||
//Get the source color
|
||||
int srcVal[3], srcOpp[3];
|
||||
srcVal[0] = srcColour.Red();
|
||||
srcVal[1] = srcColour.Green();
|
||||
srcVal[2] = srcColour.Blue();
|
||||
|
||||
int dstVal[3], dstOpp[3];
|
||||
dstVal[0] = dstColour.Red();
|
||||
dstVal[1] = dstColour.Green();
|
||||
dstVal[2] = dstColour.Blue();
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 3; i++) {
|
||||
srcOpp[i] = 256 - srcVal[i]; // avoid zero!
|
||||
dstOpp[i] = 255 - dstVal[i];
|
||||
}
|
||||
|
||||
int c = 0;
|
||||
for (i = 0; i < width * height * 3; i++) {
|
||||
int s = (int) *src;
|
||||
|
||||
if (s >= srcVal[c])
|
||||
*dst++ = dstVal[c] + dstOpp[c] * (s - srcVal[c]) / srcOpp[c];
|
||||
|
||||
else
|
||||
*dst++ = dstVal[c] * s / srcVal[c];
|
||||
src++;
|
||||
c = (c + 1) % 3;
|
||||
}
|
||||
|
||||
return dstImage;
|
||||
}
|
||||
|
||||
/// Takes a background image, foreground image, and mask
|
||||
/// (i.e. the alpha channel for the foreground), and
|
||||
/// returns an new image where the foreground has been
|
||||
/// overlaid onto the background using alpha-blending,
|
||||
/// at location (xoff, yoff).
|
||||
wxImage *OverlayImage(wxImage * background, wxImage * foreground,
|
||||
wxImage * mask, int xoff, int yoff)
|
||||
{
|
||||
unsigned char *bg = background->GetData();
|
||||
unsigned char *fg = foreground->GetData();
|
||||
unsigned char *mk = mask->GetData();
|
||||
|
||||
int bgWidth = background->GetWidth();
|
||||
int bgHeight = background->GetHeight();
|
||||
int fgWidth = foreground->GetWidth();
|
||||
int fgHeight = foreground->GetHeight();
|
||||
int mkWidth = mask->GetWidth();
|
||||
int mkHeight = mask->GetHeight();
|
||||
|
||||
|
||||
//Now, determine the dimensions of the images to be masked together
|
||||
//on top of the background. This should be equal to the area of the
|
||||
//smaller of the foreground and the mask, as long as it is
|
||||
//within the area of the background, given the offset.
|
||||
|
||||
//Make sure the foreground size is no bigger than the mask
|
||||
int wCutoff = (fgWidth < mkWidth) ? fgWidth : mkWidth;
|
||||
int hCutoff = (fgHeight < mkHeight) ? fgHeight : mkHeight;
|
||||
|
||||
|
||||
// If the masked foreground + offset is bigger than the background, masking
|
||||
// should only occur within these bounds of the foreground image
|
||||
wCutoff = (bgWidth - xoff > wCutoff) ? wCutoff : bgWidth - xoff;
|
||||
hCutoff = (bgHeight - yoff > hCutoff) ? hCutoff : bgHeight - yoff;
|
||||
|
||||
|
||||
//Make a new image the size of the background
|
||||
wxImage * dstImage = new wxImage(bgWidth, bgHeight);
|
||||
unsigned char *dst = dstImage->GetData();
|
||||
memcpy(dst, bg, bgWidth * bgHeight * 3);
|
||||
|
||||
|
||||
// Go through the foreground image bit by bit and mask it on to the
|
||||
// background, at an offset of xoff,yoff.
|
||||
// BUT...Don't go beyond the size of the background image,
|
||||
// the foreground image, or the mask
|
||||
int x, y;
|
||||
for (y = 0; y < hCutoff; y++) {
|
||||
|
||||
unsigned char *bkp = bg + 3 * ((y + yoff) * bgWidth + xoff);
|
||||
unsigned char *dstp = dst + 3 * ((y + yoff) * bgWidth + xoff);
|
||||
|
||||
for (x = 0; x < wCutoff; x++) {
|
||||
|
||||
int value = mk[3 * (y * mkWidth + x)];
|
||||
int opp = 255 - value;
|
||||
|
||||
for (int c = 0; c < 3; c++)
|
||||
dstp[x * 3 + c] =
|
||||
((bkp[x * 3 + c] * opp) +
|
||||
(fg[3 * (y * fgWidth + x) + c] * value)) / 255;
|
||||
}
|
||||
}
|
||||
return dstImage;
|
||||
}
|
||||
|
||||
/// Takes a background image, foreground image, and mask
|
||||
/// (i.e. the alpha channel for the foreground), and
|
||||
/// returns an new image where the foreground has been
|
||||
/// overlaid onto the background using alpha-blending,
|
||||
/// at location (xoff, yoff).
|
||||
wxImage *OverlayImage(teBmps eBack, teBmps eForeground,
|
||||
int xoff, int yoff)
|
||||
{
|
||||
wxImage imgBack(theTheme.Image( eBack ));
|
||||
wxImage imgFore(theTheme.Image( eForeground ));
|
||||
|
||||
|
||||
// TMP: dmazzoni - just so the code runs even though not all of
|
||||
// our images have transparency...
|
||||
if (!imgFore.HasAlpha())
|
||||
return new wxImage(imgBack);
|
||||
|
||||
|
||||
wxASSERT( imgFore.HasAlpha() );
|
||||
|
||||
unsigned char *bg = imgBack.GetData();
|
||||
unsigned char *fg = imgFore.GetData();
|
||||
unsigned char *mk = imgFore.GetAlpha();
|
||||
|
||||
int bgWidth = imgBack.GetWidth();
|
||||
int bgHeight = imgBack.GetHeight();
|
||||
int fgWidth = imgFore.GetWidth();
|
||||
int fgHeight = imgFore.GetHeight();
|
||||
|
||||
|
||||
//Now, determine the dimensions of the images to be masked together
|
||||
//on top of the background. This should be equal to the area of the
|
||||
//smaller of the foreground and the mask, as long as it is
|
||||
//within the area of the background, given the offset.
|
||||
|
||||
//Make sure the foreground size is no bigger than the mask
|
||||
int wCutoff = fgWidth;
|
||||
int hCutoff = fgHeight;
|
||||
|
||||
|
||||
// If the masked foreground + offset is bigger than the background, masking
|
||||
// should only occur within these bounds of the foreground image
|
||||
wCutoff = (bgWidth - xoff > wCutoff) ? wCutoff : bgWidth - xoff;
|
||||
hCutoff = (bgHeight - yoff > hCutoff) ? hCutoff : bgHeight - yoff;
|
||||
|
||||
//Make a new image the size of the background
|
||||
wxImage * dstImage = new wxImage(bgWidth, bgHeight);
|
||||
unsigned char *dst = dstImage->GetData();
|
||||
memcpy(dst, bg, bgWidth * bgHeight * 3);
|
||||
|
||||
// Go through the foreground image bit by bit and mask it on to the
|
||||
// background, at an offset of xoff,yoff.
|
||||
// BUT...Don't go beyond the size of the background image,
|
||||
// the foreground image, or the mask
|
||||
int x, y;
|
||||
for (y = 0; y < hCutoff; y++) {
|
||||
|
||||
unsigned char *bkp = bg + 3 * ((y + yoff) * bgWidth + xoff);
|
||||
unsigned char *dstp = dst + 3 * ((y + yoff) * bgWidth + xoff);
|
||||
|
||||
for (x = 0; x < wCutoff; x++) {
|
||||
|
||||
int value = mk[(y * fgWidth + x)];// Don't multiply by 3...
|
||||
int opp = 255 - value;
|
||||
|
||||
for (int c = 0; c < 3; c++)
|
||||
dstp[x * 3 + c] =
|
||||
((bkp[x * 3 + c] * opp) +
|
||||
(fg[3 * (y * fgWidth + x) + c] * value)) / 255;
|
||||
}
|
||||
}
|
||||
return dstImage;
|
||||
}
|
||||
|
||||
// Creates an image with a solid background color
|
||||
wxImage *CreateBackground(int width, int height, wxColour colour)
|
||||
{
|
||||
wxImage *i = new wxImage(width, height);
|
||||
unsigned char *ip;
|
||||
int srcVal[3];
|
||||
int x;
|
||||
|
||||
srcVal[0] = colour.Red();
|
||||
srcVal[1] = colour.Green();
|
||||
srcVal[2] = colour.Blue();
|
||||
|
||||
ip = i->GetData();
|
||||
for(x=0; x<width*height; x++) {
|
||||
*ip++ = srcVal[0];
|
||||
*ip++ = srcVal[1];
|
||||
*ip++ = srcVal[2];
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
// Creates an image with the Mac OS X Aqua stripes, to be used
|
||||
// as a background
|
||||
wxImage *CreateAquaBackground(int width, int height, int offset)
|
||||
{
|
||||
wxImage *image = new wxImage(width, height);
|
||||
unsigned char *ip = image->GetData();
|
||||
unsigned char val[4] = {231, 239, 255, 239};
|
||||
unsigned char v;
|
||||
int x, y;
|
||||
|
||||
for(y=0; y<height; y++) {
|
||||
v = val[(y+offset)%4];
|
||||
for(x=0; x<width*3; x++)
|
||||
*ip++ = v;
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
wxImage *CreateSysBackground(int width, int height, int offset,
|
||||
wxColour colour)
|
||||
{
|
||||
#ifdef USE_AQUA_THEME
|
||||
return CreateAquaBackground(width, height, offset);
|
||||
#else
|
||||
return CreateBackground(width, height, colour);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Pastes one image into another including the alpha channel.
|
||||
/// Differs from OverlayImage in that:
|
||||
/// Happens in place to existing background image.
|
||||
/// Pastes image on top; no blending with existing background is done.
|
||||
void PasteSubImage( wxImage * background, wxImage * foreground, int xoff, int yoff )
|
||||
{
|
||||
|
||||
unsigned char *bg = background->GetData();
|
||||
unsigned char *fg = foreground->GetData();
|
||||
unsigned char *bgAlpha = background->HasAlpha() ? background->GetAlpha() : NULL;
|
||||
unsigned char *fgAlpha = foreground->HasAlpha() ? foreground->GetAlpha() : NULL;
|
||||
// For testing... Set as if no alpha in foreground....
|
||||
// fgAlpha = NULL;
|
||||
|
||||
int bgWidth = background->GetWidth();
|
||||
int bgHeight = background->GetHeight();
|
||||
int fgWidth = foreground->GetWidth();
|
||||
int fgHeight = foreground->GetHeight();
|
||||
|
||||
int wCutoff = fgWidth;
|
||||
int hCutoff = fgHeight;
|
||||
|
||||
// If the masked foreground + offset is bigger than the background, masking
|
||||
// should only occur within these bounds of the foreground image
|
||||
wCutoff = (bgWidth - xoff > wCutoff) ? wCutoff : bgWidth - xoff;
|
||||
hCutoff = (bgHeight - yoff > hCutoff) ? hCutoff : bgHeight - yoff;
|
||||
|
||||
// Go through the foreground image bit by bit and place it on to the
|
||||
// background, at an offset of xoff,yoff.
|
||||
// Don't go beyond the size of the background image,
|
||||
// or the foreground image.
|
||||
int y;
|
||||
unsigned char *bkp;
|
||||
unsigned char *fgp;
|
||||
unsigned char *bgAlphap;
|
||||
unsigned char *fgAlphap;
|
||||
for (y = 0; y < hCutoff; y++) {
|
||||
// RGB bytes
|
||||
bkp = bg + 3 * ((y + yoff) * bgWidth + xoff);
|
||||
fgp = fg + 3 * ( y * fgWidth);
|
||||
memcpy( bkp, fgp, 3 * wCutoff );
|
||||
|
||||
// Alpha bytes.
|
||||
if( bgAlpha )
|
||||
{
|
||||
bgAlphap = bgAlpha + ((y+yoff) * bgWidth + xoff );
|
||||
if( fgAlpha )
|
||||
{
|
||||
fgAlphap = fgAlpha + (y * fgWidth );
|
||||
memcpy( bgAlphap, fgAlphap, wCutoff );
|
||||
}
|
||||
else
|
||||
{
|
||||
memset( bgAlphap, 255, wCutoff );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a rectangle from within another image, INCLUDING the alpha channel
|
||||
/// \bug in wxWidgets, wxImage::GetSubImage should do this itself.
|
||||
wxImage GetSubImageWithAlpha( const wxImage & Src, const wxRect &rect )
|
||||
{
|
||||
//First part of this code is lifted from wxImage::GetSubImage() source code.
|
||||
wxImage image;
|
||||
|
||||
wxCHECK_MSG( Src.Ok(), image, wxT("invalid image") );
|
||||
|
||||
wxCHECK_MSG( (rect.GetLeft()>=0) && (rect.GetTop()>=0) && (
|
||||
rect.GetRight()<=Src.GetWidth()) && (rect.GetBottom()<=Src.GetHeight()),
|
||||
image, wxT("invalid subimage size") );
|
||||
|
||||
int subwidth=rect.GetWidth();
|
||||
const int subheight=rect.GetHeight();
|
||||
|
||||
image.Create( subwidth, subheight, false );
|
||||
|
||||
unsigned char *subdata = image.GetData(), *data=Src.GetData();
|
||||
|
||||
wxCHECK_MSG( subdata, image, wxT("unable to create image") );
|
||||
|
||||
// JKC: Quick hack - don't deal with masks - need to understand macro M_IMGDATA first.
|
||||
// if (Src.M_IMGDATA->m_hasMask)
|
||||
// image.SetMaskColour( Src.M_IMGDATA->m_maskRed, Src.M_IMGDATA->m_maskGreen, Src.M_IMGDATA->m_maskBlue );
|
||||
|
||||
int subleft=3*rect.GetLeft();
|
||||
int width=3*Src.GetWidth();
|
||||
subwidth*=3;
|
||||
|
||||
data+=rect.GetTop()*width+subleft;
|
||||
|
||||
for (long j = 0; j < subheight; ++j)
|
||||
{
|
||||
memcpy( subdata, data, subwidth);
|
||||
subdata+=subwidth;
|
||||
data+=width;
|
||||
}
|
||||
|
||||
// OK, so we've copied the RGB data.
|
||||
// Now do the Alpha channel.
|
||||
wxASSERT( Src.HasAlpha() );
|
||||
image.InitAlpha();
|
||||
|
||||
subleft/=3;
|
||||
width/=3;
|
||||
subwidth/=3;
|
||||
|
||||
data =Src.GetAlpha();
|
||||
subdata =image.GetAlpha();
|
||||
|
||||
data+=rect.GetTop()*width+subleft;
|
||||
|
||||
for (long j = 0; j < subheight; ++j)
|
||||
{
|
||||
memcpy( subdata, data, subwidth);
|
||||
subdata+=subwidth;
|
||||
data+=width;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
// Indentation settings for Vim and Emacs and unique identifier for Arch, a
|
||||
// version control system. Please do not modify past this point.
|
||||
//
|
||||
// Local Variables:
|
||||
// c-basic-offset: 3
|
||||
// indent-tabs-mode: nil
|
||||
// End:
|
||||
//
|
||||
// vim: et sts=3 sw=3
|
||||
// arch-tag: 0857e920-f8b4-4647-b3d7-5baab3c60021
|
||||
|
Reference in New Issue
Block a user