I need my sprite to transition to one color to another and on and on... like blue tint then green then purple, but i cannot find any good actions for that and am wondering, should i use animations? or is there an incorporated action for this?
you can use CCTintTo action to change the color of the sprite
[sprite runAction:[CCTintTo actionWithDuration:2 red:255 green:0 blue:0]];
since i saw several questions about replacing pixel colours in sprites, and i did'nt see any good solution (all solution only tint the color, and none of them is able to change an array of colours without forcing you into creating multiple image layers which construct the final image you want, i.e: one layer for pans, other for show, other for shirt, another for hair colour... and it goes on - note that they do have their advantages like the ability to use accurate gradients)
my solution allows you to change array of colors, meaning you can have a single image with a known colors (you dont want any gradiants in this layer, only colours that you KNOW their values - PS this only applies to colors you intent to change, other pixels can have any colour you want)
if you need gradiants over the colours you change, create an additional image with only the shading and place it as a child of the sprite.
also be aware that i am super-new to cocos2d/x (3 days), and that this code is written for cocos2dx but can be ported to cocos2d easily.
also note that i didnt test it on android only on iOS, i am not sure how capable is android official gcc and how will it deal with the way i allocate _srcC and _dstC, but again, this is easily portable.
so here it goes:
cocos2d::CCSprite * spriteWithReplacedColors( const char * imgfilename, cocos2d::ccColor3B * srcColors, cocos2d::ccColor3B * dstColors, int numColors )
{
CCSprite *theSprite = NULL;
CCImage *theImage = new CCImage;
if( theImage->initWithImageFile( imgfilename ) )
{
//make a color array which is easier to work with
unsigned long _srcC [ numColors ];
unsigned long _dstC [ numColors ];
for( int c=0; c<numColors; c++ )
{
_srcC[c] = (srcColors[c].r << 0) | (srcColors[c].g << 8) | (srcColors[0].b << 16);
_dstC[c] = (dstColors[c].r << 0) | (dstColors[c].g << 8) | (dstColors[0].b << 16);
}
unsigned char * rawData = theImage->getData();
int width = theImage->getWidth();
int height = theImage->getHeight();
//replace the colors need replacing
unsigned int * b = (unsigned int *) rawData;
for( int pixel=0; pixel<width*height; pixel++ )
{
register unsigned int p = *b;
for( int c=0; c<numColors; c++ )
{
if( (p&0x00FFFFFF) == _srcC[c] )
{
*b = (p&0xFF000000) | _dstC[c];
break;
}
}
b++;
}
CCTexture2D *theTexture = new CCTexture2D();
if( theTexture->initWithData(rawData, kCCTexture2DPixelFormat_RGBA8888, width, height, CCSizeMake(width, height)) )
{
theSprite = CCSprite::spriteWithTexture(theTexture);
}
theTexture->release();
}
theImage->release();
return theSprite;
}
to use it just do the following:
ccColor3B src[] = { ccc3( 255,255,255 ), ccc3( 0, 0, 255 ) };
ccColor3B dst[] = { ccc3( 77,255,77 ), ccc3( 255, 0 0 ) };
//will change all whites to greens, and all blues to reds.
CCSprite * pSprite = spriteWithReplacedColors( "character_template.png", src, dst, sizeof(src)/sizeof(src[0]) );
of course if you need speed, you would create an extension for a sprite that create a pixel shader that does it hw accelerated at render time ;)
btw: this solution might cause some artefacts on the edges on some cases, so you can create a large image and scale it down, letting GL minimise the artefact.
you can also create "fix" layers with black outlines to hide the artefacts and place it on top etc.
also make sure you don't use these 'key' colors on the rest of the image you don't want the pixels changed.
also keep in mind that the fact that the alpha channel is not changed, and that if you use basic images with pure red/green/blue colors only, you can also optimize this function to eliminate all artefacts on edges automatically (and avoid in many cases, the need for an additional shade layer) and other cool stuff (multiplexing several images into a single bitmap - remember palette animation?)
enjoy ;)
In case someone wanted this for cocos2d-x here is the code:
somesprite->runAction(TintTo::create(float duration, Color3b &color));
Related
I created an app in which I want to display text on top of google maps. I chose to use custom markers, but they can only be images, so I decided to create an image from my text utilizing SkiaSharp.
private static ImageSource CreateImageSource(string text)
{
int numberSize = 20;
int margin = 5;
SKBitmap bitmap = new SKBitmap(30, numberSize + margin * 2, SKImageInfo.PlatformColorType, SKAlphaType.Premul);
SKCanvas canvas = new SKCanvas(bitmap);
SKPaint paint = new SKPaint
{
Style = SKPaintStyle.StrokeAndFill,
TextSize = numberSize,
Color = SKColors.Red,
StrokeWidth = 1,
};
canvas.DrawText(text.ToString(), 0, numberSize, paint);
SKImage skImage = SKImage.FromBitmap(bitmap);
SKData data = skImage.Encode(SKEncodedImageFormat.Png, 100);
return ImageSource.FromStream(data.AsStream);
}
The images I create however have ugly artifacts on the top of the resulting image and my feeling is that they get worse if I create multiple images.
I built an example app, that shows the artifacts and the code I used to draw the text. It can be found here:
https://github.com/hot33331/SkiaSharpExample
How can I get rid of those artifacts. Am I using skia wrong?
I got the following answer from Matthew Leibowitz on the SkiaSharp GitHub:
The chances are you are not clearing the canvas/bitmap first.
You can either do bitmap.Erase(SKColors.Transparent) or canvas.Clear(SKColors.Transparent) (you can use any color).
The reason for this is performance. When creating a new bitmap, the computer has no way of knowing what background color you want. So, if it was to go transparent and you wanted white, then there would be two draw operations to clear the pixels (and this may be very expensive for large images).
During the allocation of the bitmap, the memory is provided, but the actual data is untouched. If there was anything there previously (which there will be), this data appears as colored pixels.
When I've seen that before, it's been because the memory passed to SkiaSharp was not zeroed. As an optimization, though, Skia assumes that the memory block passed to it is pre zeroed. Resultingly, if your first operation is a clear, it will ignore that operation, because it thinks that the state is already clean. To resolve this issue, you can manually zero the memory passed to SkiaSharp.
public static SKSurface CreateSurface(int width, int height)
{
// create a block of unmanaged native memory for use as the Skia bitmap buffer.
// unfortunately, this may not be zeroed in some circumstances.
IntPtr buff = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(width * height * 4);
byte[] empty = new byte[width * height * 4];
// copy in zeroed memory.
// maybe there's a more sanctioned way to do this.
System.Runtime.InteropServices.Marshal.Copy(empty, 0, buff, width * height * 4);
// create the actual SkiaSharp surface.
var colorSpace = CGColorSpace.CreateDeviceRGB();
var bContext = new CGBitmapContext(buff, width, height, 8, width * 4, colorSpace, (CGImageAlphaInfo)bitmapInfo);
var surface = SKSurface.Create(width, height, SKColorType.Rgba8888, SKAlphaType.Premul, bitmap.Data, width * 4);
return surface;
}
Edit: btw, I assume this is a bug in SkiaSharp. The samples/apis that create the buffer for you should probably be zeroing it out. Depending on the platform it can be hard to repro as the memory alloc behaves differently. More or less likely to provide you untouched memory.
I'm trying to display a mesh inside the Unity UI. My class inherits from UI.Graphic. I managed to make it work for my vertices and colors, but it seems I can't set a texture to the mesh. I tried different things but nothing worked :
Tried to set the material field on the CanvasRenderer
Tried to set the material field on the Graphic
The documentation on all this is very bad, and lots of code samples I found (mostly on how to set vertices) were out of date.
Here is the code where I set my vertices :
protected override void OnPopulateMesh(VertexHelper vh)
{
base.OnPopulateMesh(vh);
vh.Clear();
if( m_SkinData != null )
{
int j = 0;
foreach( SkinVertex vert in m_SkinData.m_Vertices )
{
float channel = j / (float)m_SkinData.m_Vertices.Count;
Color32 col = new Color(channel, channel, channel);
vh.AddVert(m_Vertice[j], col, m_uv[j]);
j++;
}
for( int i = 0; i < m_SkinData.m_Indices.Count; i += 3 )
{
vh.AddTriangle(m_SkinData.m_Indices[i], m_SkinData.m_Indices[i + 1], m_SkinData.m_Indices[i + 2]);
}
}
}
Here is the result :
and here is the Material with the diffuse set by code :
I'm sure I'm missing something obvious, but I can't find what.
[Edit] The exact same code is working if it inherits from UI.Image instead of UI.Graphic. It does not work if it inherits from UI.MaskableGraphic (Child of UI.Graphic, parent of UI.Image). I don't even have to set a sprite or whatever. It's really odd and I'd still like to understand why and what should be the correct way. But for the time being it's a workaround.[/Edit]
Sorry, I'll try to explain better.
I want to make a sliding menu where you can select a character. I want the character that is at the center increases in size to know that this is the current character. The effect can be seen in the game of Crossy Road when you want to select a character.
Sorry but I can't upload a imagen because i am new in the forum
I think I might be able to help you without needing too much borrowed code. There are two possibilities here:
You have a perspective camera so the "selected" item can just be closer to the camera.
You have an orthographic camera so you will have to scale things.
For perspective:
List<GameObject> characters; //contains all character.
int selectedIndex = 0; //index to the selected character.
float spacing = 10; //space between chars
void Update()
{
if(Input.GetKeyDown("RightArrow"))
{
selectIndex++;
ApplyChanges();
}
if(Input.GetKeyDown("LeftArrow"))
{
selectIndex--;
ApplyChanges();
}
}
void ApllyChanges()
{
// Make sure the selected index is within range and then space the characters.
selectedIndex = selectedIndex % character.Count();
SpaceCharacters();
}
void SpaceCharacters()
{
for(int i = 0; i < characters.Count(); ++i)
{
// characters on the left will have a negative spacing and so will be placed to the left and vice versa for characters on the right.
int offset = i - selectedIndex;
characters[i].transform.position = new Vector3(offset * spacing, 0, 0);
}
// Move the selected character closer.
characters[selectedIndex].transform.position = new Vector3(0,0,spacing);
}
For orthographic camera you will need to set the select characters transform.scale to a larger vector.
This won't animate anything or look cool. This code will just snap your characters into position.
The solution I adopted was to attach objects to the transparent buttons in a scrool rect, so as to manage 3d objects with the convenient of scrool rect interface.
Here you can find the official documentation for use scrool rect: http://docs.unity3d.com/Manual/script-ScrollRect.html
Maybe my assets can serve you ;)
https://www.assetstore.unity3d.com/en/#!/content/60233
I am trying to track multiple objects with different color at the same time via a single web cam. Now I can do that for single color with single threshold:
IplImage* GetThresholdedImage(IplImage* imgHSV)
{
IplImage* imgThresh=cvCreateImage(cvGetSize(imgHSV),IPL_DEPTH_8U, 1);
cvInRangeS(imgHSV, cvScalar(170,160,60), cvScalar(180,2556,256), imgThresh);
return imgThresh;
}
I'm looking for some hints to do various threshold. Also if its possible, how many windows does it require? Do i need to assign different windows for different colors?
The easiest way to do this is create a thresholded image for each color you wish to track. Instead of hard-coding your threshold ranges, you could modify your function to take them as parameters. This lets you re-use the function for different objects.
The modified function might look like this:
IplImage* GetThresholdedImage(IplImage* imgHSV, CvScalar lower, CvScalar upper)
{
IplImage* imgThresh=cvCreateImage(cvGetSize(imgHSV),IPL_DEPTH_8U, 1);
cvInRangeS(imgHSV, lower, upper, imgThresh);
return imgThresh;
}
And then call it using different thresholds for different objects:
IplImage* hsv; /* Already initialized*/
/* Set thresholds for blue and green objects as an example. */
/*Obviously, set these to be whatever is necessary. */
CvScalar blue_lower = cvScalar(110,60,10);
CvScalar blue_upper = cvScalar(120,256,256);
CvScalar green_lower = cvScalar(40,60,10);
CvScalar green_upper = cvScalar(71,256,256);
/* Get the images thresholded for blue and green. */
IplImage* blue_mask = GetThresholdedImage(hsv, blue_lower, blue_upper);
IplImage* green_mask = GetThresholdedImage(hsv, green_lower, green_upper);
Wee.
So I finally figured out how the iIntegral member of TVITEMEX works. The MSDN docs didn't think to mention that setting it while inserting an item has no effect, but setting it after the item is inserted works. Yay!
However, when using the TVS_HASLINES style with items of variable height, the lines are only drawn for the top part of an item with iIntegral > 1. E.g. if I set TVS_HASLINES and TVS
Here's what it looks like (can't post images WTF?)
Should I manually draw more of the lines in response to NM_CUSTOMDRAW or something?
Yes, Windows doesn't do anything with the blank space obtained from changing the height.
From the MSDN:
The tree-view control does not draw in the
extra area, which appears below the
item content, but this space can be
used by the application for drawing
when using custom draw. Applications
that are not using custom draw should
set this value to 1, as otherwise the
behavior is undefined.
Alright, problem solved.
I failed to find an easy answer, but I did work around it the hard way. It's basically just drawing the extra line segments in custom draw:
// _cd is the NMTVCUSTOMDRAW structure
// ITEMHEIGHT is the fixed height set in TreeView_SetItemHeight
// linePen is HPEN of a suitable pen to draw the lines (PS_ALTERNATE etc.)
// indent is the indentation size returned from TreeView_GetIndent
case CDDS_ITEMPREPAINT : {
// Expand line because TreeView is buggy
RECT r = _cd->nmcd.rc;
HDC hdc = _cd->nmcd.hdc;
HTREEITEM hItem = (HTREEITEM) _cd->nmcd.dwItemSpec;
if( r.bottom - r.top > ITEMHEIGHT ) {
HGDIOBJ oldPen = SelectObject( hdc, linePen );
// Draw any lines left of current item
HTREEITEM hItemScan = hItem;
for( int i = _cd->iLevel; i >= 0; --i ) {
// Line should be drawn only if node has a next sibling to connect to
if( TreeView_GetNextSibling( getHWnd(), hItemScan ) ) {
// Lines seem to start 17 pixels from left edge of control. But no idea
// where that constant comes from or if it is really constant.
int x = 17 + indent * i;
MoveToEx( hdc, x, r.top + ITEMHEIGHT, 0 );
LineTo( hdc, x, r.bottom );
}
// Do the same for the parent
hItemScan = TreeView_GetParent( getHWnd(), hItemScan );
}
SelectObject( hdc, oldPen );
}
}
The pattern from the PS_ALTERNATE brush sometimes doesn't align perfectly with line drawn by the control, but that's hardly noticeable. What's worse is that even though I have the latest common controls and all the service packs and hotfixes installed, there are still bugs in TreeView documented way back in 2005. Specifically, the TreeView doesn't update its height correctly. The only workaround I've found for that is to force some collapsing/expanding of nodes and do a few calls to InvalidateRect.
If the variable-height nodes are at the root level, though, there doesn't appear to be anything you can do. Luckily I don't need that.