I compared skia with gdi painting on windows. both drawing 98000 random lines. to my suprise that skia is far low efficiency than gdi(the skia painting cost 1600ms, while gdi cost 0ms). my testing code was paste below. any suggestions?
bool PaintCompare() {
//generate ramdon points
std::default_random_engine e(std::chrono::high_resolution_clock::now().time_since_epoch().count());
std::uniform_real_distribution<float> u(10, 500);
SkPoint pts[100];
for (int i = 0; i<100; i++)
pts[i].set(u(e), u(e));
SkPaint paint;
paint.setColor(SkColorSetRGB(255, 0, 0));
//create skia canvas
sk_sp<SkSurface> rasterSurface(
SkSurface::MakeRasterN32Premul(600, 600));
SkCanvas* canvas = rasterSurface->getCanvas();
//draw lines with skia
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i<1000; i++)
{
for (int j = 1; j<99; j++)
{
canvas->drawLine(pts[j].fX, pts[j].fY, pts[j + 1].fX, pts[j + 1].fY, paint);
}
}
auto cost = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - start);
sk_sp<SkImage> img(rasterSurface->makeImageSnapshot());
if (!img) { return false; }
SkBitmap skBmp;
if (!img->asLegacyBitmap(&skBmp, SkImage::kRO_LegacyBitmapMode)) {
return false;
}
//show bitmap on hdc
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(bmi));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = 600;
bmi.bmiHeader.biHeight = -600; // top-down image
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = 0;
HDC hdc = GetDC();
LPVOID pBits = NULL;
HBITMAP hBmp = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pBits, 0, 0);
skBmp.copyPixelsTo(pBits, skBmp.getSize());
CDC memdc;
memdc.CreateCompatibleDC(hdc);
memdc.SelectBitmap(hBmp);
BitBlt(hdc, 0, 0, 600, 600, memdc, 0, 0, SRCCOPY);
memdc.DeleteDC();
//draw with gdi
CPen pen;
pen.CreatePen(PS_SOLID, 1, RGB(0, 255, 0));
RECT rc{ 0,0,600,600 };
CBitmap bmp;
bmp.CreateCompatibleBitmap(hdc, 600, 600);
memdc.CreateCompatibleDC(hdc);
memdc.SelectBitmap(bmp);
memdc.FillSolidRect(&rc, RGB(0, 0, 0));
memdc.SelectPen(pen);
start = std::chrono::high_resolution_clock::now();
for (int i = 0; i<1000; i++)
{
for (int j = 1; j<99; j++)
{
memdc.MoveTo(pts[j].fX, pts[j].fY);
memdc.LineTo(pts[j + 1].fX, pts[j + 1].fY);
}
}
auto cost2 = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - start);
//copy bitmap to window
BitBlt(hdc, 700, 0, 600, 600, memdc, 0, 0, SRCCOPY);
ReleaseDC(hdc);
memdc.DeleteDC();
//wchar_t buf[256];
//wsprintf(buf, L"left cost=%I64d, right cost=%I64d", cost.count(), cost2.count());
//GetParent().SetWindowText(buf);
//cost == 1596615 microseconds
//cost2 == 107253 microseconds
}
I found the problem finally. i give the result in debug mode!
in debug mode that skia with raster backend is 20 times slower than gdi.
however in release mode skia with raster backend is 4-5 times slower than gdi.
i had another test that skia uses opengl as backend. the result shows skia and gdi spend almost the same time. skia is about 15% slower than gdi.
Related
Lot of code to post because I can't really tell where the issue is. I am trying to run this on an ESP32 chip and am writing the code in vscode with platformio.
The function
byte _previousPoint(){
for (byte point = 0; point < maxPoints; point ++){ //loop through active points.
if (_points[point + 1].getActive() != 1){
return point;
}
else {
long t1 = point_seconds(point);
long t2 = point_seconds(point + 1);
if (t1 <= currentTime){ //If current time is after this point.
if (t2 > currentTime){ //If current time is less than the next point (it is sandwiched).
return point;
}
}
}
}
return 0;
}
Is not behaving as I expect. And it seems the cause is that the function
bool getActive(){
return _active;
}
under the class point is returning not a bool but ascending numbers for each channel. point.getActive() should return a bool value, and that bool value should default to 0 (as it is set in the class definition), which is why this makes no sense. The point that is returning the erroneous values is the last one in the _points[maxPoints] array for each channel, and that value seems to match the channel numbers (for red it is 0, for green it is 1, for blue it is 2, etc.)
If I print the value _active upon running the point.setActive() function it comes out correctly. So something is wrong with "getting" it later.
Here is full code below. Let me know if you need some clarification because I know it's a lot. And thanks for anybody patient enough to help.
#include <Arduino.h>
long currentTime;
long lastUp;
byte totalChannels = 4;
const byte maxPoints = 3;
class point {
public:
point(){
}
void clear(){
_active = 0;
_day = 0;
_hour = 0;
_minute = 0;
_second = 0;
_intensity = 0;
_mode = 0;
}
bool getActive(){
return _active;
}
uint getDay(){
return _day;
}
byte getHour(){
return _hour;
}
byte getMinute(){
return _minute;
}
byte getSecond(){
return _second;
}
int getIntensity(){
return _intensity;
}
byte getMode(){
return _mode;
}
void setActive(bool active){
_active = active;
}
void setDay(uint day){
_day = day;
}
void setHour(byte hour){
_hour = hour;
}
void setMinute(byte minute){
_minute = minute;
}
void setSecond(byte second){
_second = second;
}
void setIntensity(byte intensity){
_intensity = intensity;
}
void setMode(byte mode){
_mode = mode;
}
private:
bool _active = 0;
uint _day = 0;
byte _hour = 0;
byte _minute = 0;
byte _second = 0;
int _intensity = 0;
byte _mode = 0;
};
class channel {
public:
channel(String color, byte pin){
this->_color = color;
this->_pin = pin;
init();
};
void setpoint(byte row, point &newPoint, uint day, byte hour, byte minute, byte second, byte intensity, byte mode){ //edits points (for debug)
newPoint.setDay(day);
newPoint.setHour(hour);
newPoint.setMinute(minute);
newPoint.setSecond(second);
newPoint.setIntensity(intensity);
newPoint.setMode(mode);
newPoint.setActive(1);
_points[row] = newPoint;
}
bool getPoint(byte point){
return _points[point + 1].getActive();
}
void clearAllPoints(){
for (byte point = 0; point < maxPoints; point ++ ){
_points[point].clear();
}
}
void setPin(byte pin){
_pin = pin;
}
byte getPin(){
return _pin;
}
uint getIntensity(){
byte point1 = _previousPoint();
byte point2 = _nextPoint(point1);
byte fade_mode = _points[point1].getMode();
uint intensity;
if (point2 != 0){
if (fade_mode == 0){
intensity = _interpolate_lin(point1, point2);
}
else if (fade_mode == 1){
intensity = _interpolate_sin(point1, point2);
}
}
else if (point2 != 1){
if (fade_mode == 0){
intensity = _interpolate_lin(point1, point2);
}
else if (fade_mode == 1){
intensity = _interpolate_sin(point1, point2);
}
}
return intensity;
}
private:
//class attributes
point _points[maxPoints]; //points maximum of 64 points per channel.
byte _pin; //PWM pin output for channel
String _color; //LED color
float _interpolate_lin(byte point1, byte point2){
float idiff = _points[point2].getIntensity() - _points[point1].getIntensity();
float tdiff = point_seconds(point2) - point_seconds(point1);
float m;
if (tdiff != 0){
m = idiff / tdiff;
}
else{
m = 0;
}
float t = currentTime - point_seconds(point1);
float b = _points[point1].getIntensity();
//linear equation result
float i = (m * t) + b;
return i;
}
float _interpolate_sin(byte point1, byte point2){
float amplitude = _points[point2].getIntensity() - _points[point1].getIntensity();
float tdiff = point_seconds(point2) - point_seconds(point1);
float a = (-0.5 * amplitude);
float b = (2 * PI) / (2 * tdiff);
float t = (currentTime - point_seconds(point1));
float d = 0.5 * abs(amplitude);
//cosine equation result
float i = (a * cos(b * t)) + d;
return i;
}
byte _previousPoint(){
for (byte point = 0; point < maxPoints; point ++){ //loop through active points.
if (_points[point + 1].getActive() != 1){
return point;
}
else {
long t1 = point_seconds(point);
long t2 = point_seconds(point + 1);
if (t1 <= currentTime){ //If current time is after this point.
if (t2 > currentTime){ //If current time is less than the next point (it is sandwiched).
return point;
}
}
}
}
return 0;
}
byte _nextPoint(byte point){
if (_points[point + 1].getActive() != 1){ //if next point is inactive, previous is last in cycle. Next point is 0.
return 0;
}
else if (_points[point + 1].getActive() == 1){ //if next point is active, return it as _nextPoint.
return point + 1;
}
return 0;
}
long point_seconds(byte point){
return ((_points[point].getHour() * 3600) + (_points[point].getMinute() * 60) + _points[point].getSecond());
}
};
//declaring channels and initializing channel array.
channel red("red", 0);
channel green("green", 1);
channel blue("blue", 2);
channel royal("royal blue", 3);
channel *channels[] = {
&red,
&green,
&blue,
&royal
};
void setIntensities(){
for (byte ch = 0; ch < totalChannels; ch ++){
channel ledChannel = *channels[ch];
byte intensity = ledChannel.getIntensity();
ledcWrite(ledChannel.getPin(), intensity);
}
}
void setup() {
Serial.begin(9600);
//set up LED outputs
ledcAttachPin(12, 0);
ledcAttachPin(13, 1);
ledcAttachPin(16, 2);
ledcSetup(0, 1000, 8);
ledcSetup(1, 1000, 8);
ledcSetup(2, 1000, 8);
//clear all points
for (byte ch = 0; ch < totalChannels; ch++){
channel ledChannel = *channels[ch];
ledChannel.clearAllPoints();
}
//Set points for testing purposes
point red1;
point red2;
point red3;
point green1;
point green2;
point green3;
point blue1;
point blue2;
point blue3;
point royal1;
point royal2;
point royal3;
red.setpoint(0, red1, 0, 0, 0, 0, 0, 0);
red.setpoint(1, red2, 0, 0, 0, 10, 255, 0);
red.setpoint(2, red3, 0, 0, 0, 20, 0, 0);
green.setpoint(0, green1, 0, 0, 0, 10, 0, 0);
green.setpoint(1, green2, 0, 0, 0, 20, 255, 0);
green.setpoint(2, green3, 0, 0, 0, 30, 0, 0);
blue.setpoint(0, blue1, 0, 0, 0, 20, 0, 0);
blue.setpoint(1, blue2, 0, 0, 0, 30, 255, 0);
blue.setpoint(2, blue3, 0, 0, 0, 40, 0, 0);
royal.setpoint(0, royal1, 0, 0, 0, 30, 0, 0);
royal.setpoint(1, royal2, 0, 0, 0, 40, 255, 0);
royal.setpoint(2, royal3, 0, 0, 0, 50, 0, 0);
Serial.print("red: "); // <----------These are to debug. The channels are returning ascending bool values for point 3 (which should all be 0)
for (byte point = 0; point < 3; point++){
Serial.print(red.getPoint(point));
}
Serial.print(" green: ");
for (byte point = 0; point < 3; point++){
Serial.print(green.getPoint(point));
}
Serial.print(" blue: ");
for (byte point = 0; point < 3; point++){
Serial.print(blue.getPoint(point));
}
Serial.print(" royal: ");
for (byte point = 0; point < 3; point++){
Serial.print(royal.getPoint(point));
}
}
void loop() {
currentTime = millis() / 1000;
if (currentTime > lastUp){
setIntensities();
lastUp = currentTime;
}
}
The problem is with your getPoint() method:
bool getPoint(byte point){
return _points[point + 1].getActive();
}
You're referencing the array with point + 1 instead of point so when you attempt to read the last item in the array you're actually reading off the end of the array and getting arbitary data.
Just change this to:
bool getPoint(byte point){
return _points[point].getActive();
}
You're also doing the same thing on your _previousPoint() method:
byte _previousPoint(){
for (byte point = 0; point < maxPoints; point ++){ //loop through active points.
if (_points[point + 1].getActive() != 1){
return point;
}
Again - I think you want to just replace [point + 1] with [point] to go through the list of points and prevent reading past the end of the array.
help please, at Arduino Uno I receive a signal from the sensor and build a graph using processing 2.2.1, but you need to scale up without losing proportions. My attempts failed, the proportion was crumbling(I tried to multiply the values) Code:
Serial myPort;
int xPos = 1;
int yPos = 100;
float yOld = 0;
float yNew = 0;
float inByte = 0;
int lastS = 0;
PFont f;
void setup () {
size(1200, 500);
println(Serial.list());
myPort = new Serial(this, Serial.list()[0], 9600);
myPort.bufferUntil('\n');
background(0xff);
}
void draw () {
int s = second();
PFont f = createFont("Arial",9,false);
textFont(f,9);
fill(0);
if (s != lastS){
stroke(0xcc, 0xcc, 0xcc);
line(xPos, yPos+10, xPos, yPos+30);
text(s + " Sec.", xPos+5, yPos+30);
lastS = s;
}
}
void mousePressed(){
save(lastS + "-heart.jpg");
}
void serialEvent (Serial myPort) {
String inString = myPort.readStringUntil('\n');
if (inString != null) {
inString = trim(inString);
if (inString.equals("!")) {
stroke(0, 0, 0xff); // blue
inByte = 1023;
} else {
stroke(0xff, 0, 0); //Set stroke to red ( R, G, B)
inByte = float(inString);
}
inByte = map(inByte, 0, 1023, 0, height);
yNew = inByte;
line(xPos-1, yPos-yOld, xPos, yPos-yNew);
yOld = yNew;
if (xPos >= width) {
xPos = 1;
yPos+=200;
if (yPos > height-200){
xPos = 1;
yPos=100;
background(0xff);
}
} else {
xPos++;
}
}
}
There are multiple ways to scale graphics.
A simple method to try is to simply scale() the rendering (drawing coordinate system).
Bare in mind currently the buffer is only cleared when the xPos reaches the right hand side of the screen.
The value from Arduino is mapped to Processing here:
inByte = map(inByte, 0, 1023, 0, height);
yNew = inByte;
you should try to map change height to a different value as you see fit.
This however will scale only the Y value. The x value is incremented here:
xPos++;
you might want to change this increment to a different value that works with the proportion you are trying maintain between x and y.
I have the following code which should draw a sine function and fill it's internals - everything between this function and zero level.
But the brush which I set up does not work. Should any additional function be called? Now I have only sine function and zero level which are drawn without any filling.
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
QPen myPen(Qt::black, 2, Qt::SolidLine);
painter.setPen(myPen);
painter.setBrush(QBrush(QColor(0, 0, 0, 20)));
QPointF* pointArray = new QPointF[251 * 2];
for (int i=0; i < 251; ++i)
{
pointArray[i].setX(i);
pointArray[i].setY(100*qSin(i/10.0));
}
for (int i = 251; i < 251*2; i++)
{
pointArray[i].setX(i - 251);
pointArray[i].setY(0);
}
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::HighQualityAntialiasing);
painter.drawPolyline(pointArray, 251*2);
}
I have a problem with filling a LinkedHashMap with GregorianCalendar-Objects.
I creat a GregorianCalendar starttime. Then I fill it into an ArrayList and add 200 Milliseconds 50 times. After that, I fill these values in a Map together with a double from another ArrayList. When I make a System out of the Map it only gives me the last value of the time list but all values from the double list.
starttime = new GregorianCalendar(2013, 0, 1, 13, 0, 0);
starttime.set(Calendar.MILLISECOND, 0);
GregorianCalendar time = new GregorianCalendar();
time.setTimeInMillis(starttime.getTimeInMillis());
for (int i = 0; i < 50; i++) {
time.add(Calendar.MILLISECOND, 200);
timeList.add(time.getTimeInMillis());
}
for (int i = 0; i < 50; i++) {
time.setTimeInMillis(timeList.get(i));
inputMap.put(time, valueList.get(i));
}
for (Entry<GregorianCalendar, Double> entry : inputMap.entrySet()) {
System.out.println(entry.getKey().getTime().toString()+" "
+entry.getKey().get(Calendar.MILLISECOND)+ " = " + entry.getValue());
}
I found a solution. The Problem is, that time is a reference in the inputMap. So you have to create a new GregorianCalendar in the filling loop.
for (int i = 0; i < 50; i++) {
GregorianCalendar gregCal = new GregorianCalendar();
gregCal.setTimeInMillis(starttime.getTimeInMillis());
inputMap.put(gregCal, valueList.get(i));
starttime.add(Calendar.MILLISECOND, 200);
}
I would like to apply a texture to a grid in 3d space using Processing, I've tried declaring a PImage and setting the .jpg file with loadimage with no results..
PImage tex;
void setup() {
size(800, 600, P3D);
tex=loadImage("ground104");
noCursor();
}
void draw()
{
directionalLight(255, 255, 255, 0, 0, -1);
background(180);
drawPlane();
}
void drawPlane()
{ //lights();
int s = 10000;
int ss = 500;
fill(0, 200, 20); stroke(200); for(int i = 0; i < s; i += ss) {
stroke(0,100,0);
strokeWeight(3);
beginShape(QUAD_STRIP);
for(int j = 0; j <= s; j += ss)
{
texture(tex);
vertex(j, i, 0);
vertex(j, i + ss, 0);
}
endShape(); } noStroke(); }
Do you have any advice?
Thanks!
looks like you need a file extension like .jpg. You can troubleshoot by running the code from the reference page:
http://www.processing.org/reference/texture_
size(100, 100, P3D);
noStroke();
PImage img = loadImage("laDefense.jpg");
beginShape();
texture(img);
vertex(10, 20, 0, 0);
vertex(80, 5, 100, 0);
vertex(95, 90, 100, 100);
vertex(40, 95, 0, 100);
endShape();
and replace laDefense.jpg with your image name. Also, it needs to be placed in the data directory in the sketch directory. If it works, then your problem is elsewhere. I am not sure what your plan is in the sketch, but you might find peasycam useful for troubleshooting in 3d.
http://mrfeinberg.com/peasycam/
another technique is to increment a float by 0.01 per frame and call a RotateX(), Y or Z or all of the above at the beginning of your draw() method / loop.
Thank you for your reply!
I've changed my code to this and now you can run it and see what I get (first download the grass.jpg from https://www.dropbox.com/s/fsda0tih67q8tll/grass.jpg?m). I'm close but I wonder why the grid is green when it was supposed to be a grass land...
PImage tex;
void setup()
{
size(800, 600, P3D);
tex=loadImage("grass.jpg");
noCursor();
}
void draw()
{
translate(width/2 , height/2 , 0); // center of screen
rotateX(QUARTER_PI * 1.0); // move camera up
rotateZ(QUARTER_PI * 1.8);
//rotateZ(camZ.val + offset); // rotate around turret
rotateZ(map(mouseX, mouseY, width, 2.5, -2.5));
translate(-1000, 0, -1000);
directionalLight(255, 255, 255, 0, 0, -1);
background(180);
drawPlane();
}
void drawPlane()
{ //lights();
int s = 10000;
int ss = 500;
fill(0, 200, 20); stroke(200); for(int i = 0; i < s; i += ss) {
stroke(0,100,0);
strokeWeight(3);
beginShape(QUAD_STRIP);
for(int j = 0; j <= s; j += ss)
{
texture(tex);
vertex(j, i, 0);
vertex(j, i + ss, 0);
}
endShape();
}
noStroke();
}