O HTML5 Canvas não tem método para definir explicitamente um único pixel.
Pode ser possível definir um pixel usando uma linha muito curta, mas o antialising e os limites de linha podem interferir.
Outra maneira pode ser criar um ImageData
objeto pequeno e usar:
context.putImageData(data, x, y)
para colocá-lo no lugar.
Alguém pode descrever uma maneira eficiente e confiável de fazer isso?
fillRect()
semi-recentemente se tornou quase 10x mais rápido que os dados de imagem 1x1 no Chromev24. Então ... se a velocidade é crítica e você conhece seu público-alvo, não tome a palavra de uma resposta desatualizada (até a minha). Em vez disso: teste!Um método que não foi mencionado é usar getImageData e, em seguida, putImageData.
Este método é bom para quando você deseja desenhar muito de uma só vez, rápido.
http://next.plnkr.co/edit/mfNyalsAR2MWkccr
fonte
Eu não tinha considerado
fillRect()
, mas as respostas me levaram a compará-loputImage()
.Colocar 100.000 pixels de cores aleatórias em locais aleatórios, com o Chrome 9.0.597.84 em um (antigo) MacBook Pro, leva menos de 100ms
putImage()
, mas quase 900ms de usofillRect()
. (Código de referência em http://pastebin.com/4ijVKJcC ).Se, em vez disso, eu escolher uma única cor fora dos loops e apenas traçar essa cor em locais aleatórios,
putImage()
leva 59ms vs 102msfillRect()
.Parece que a sobrecarga de gerar e analisar uma especificação de cor CSS na
rgb(...)
sintaxe é responsável pela maior parte da diferença.Colocar valores RGB brutos diretamente em um
ImageData
bloco, por outro lado, não requer manipulação ou análise de string.fonte
fonte
putImageData()
depois dessa função ou o contexto será atualizado por referência?Como navegadores diferentes parecem preferir métodos diferentes, talvez faça sentido fazer um teste menor com todos os três métodos como parte do processo de carregamento para descobrir qual é o melhor uso e usá-lo em todo o aplicativo?
fonte
Parece estranho, mas, mesmo assim, o HTML5 suporta o desenho de linhas, círculos, retângulos e muitas outras formas básicas; ele não possui nada adequado para desenhar o ponto básico. A única maneira de fazer isso é simular o ponto com o que você tiver.
Então, basicamente, existem 3 soluções possíveis:
Cada um deles tem suas desvantagens
Linha
Lembre-se de que estamos seguindo na direção sudeste e, se esse for o limite, pode haver um problema. Mas você também pode desenhar em qualquer outra direção.
Retângulo
ou de maneira mais rápida usando fillRect, porque o mecanismo de renderização preencherá apenas um pixel.
Círculo
Um dos problemas com os círculos é que é mais difícil para um mecanismo processá-los
a mesma idéia do retângulo que você pode obter com o preenchimento.
Problemas com todas estas soluções:
Se você está se perguntando: "Qual é a melhor maneira de desenhar um ponto? ", Eu usaria um retângulo preenchido. Você pode ver meu jsperf aqui com testes de comparação .
fonte
Que tal um retângulo? Isso precisa ser mais eficiente do que criar um
ImageData
objeto.fonte
putImageData
lo, é 10 vezes mais rápido quefillRect
no Chrome. (Veja a minha resposta para mais.)Desenhe um retângulo como o sdleihssirhc disse!
^ - deve desenhar um retângulo 1x1 em x: 10, y: 10
fonte
Hmm, você também pode fazer uma linha de 1 pixel de largura com 1 pixel de comprimento e fazer com que a direção se mova ao longo de um único eixo.
fonte
Para completar a resposta completa do Phrogz, há uma diferença crítica entre
fillRect()
eputImageData()
.O primeiro contexto usos para desenhar sobre por adição de um retângulo (não um pixel), usando o fillStyle valor alfa eo contexto globalAlpha ea matriz de transformação , tampas de linha etc ..
O segundo substitui todo um conjunto de pixels (talvez uma, mas por ?)
O resultado é diferente, como você pode ver no jsperf .
Ninguém deseja definir um pixel de cada vez (ou seja, desenhá-lo na tela). É por isso que não há API específica para fazer isso (e com razão).
Em termos de desempenho, se o objetivo é gerar uma imagem (por exemplo, um software de rastreamento de raios), você sempre deseja usar uma matriz obtida pela
getImageData()
qual é um Uint8Array otimizado. Então você ligaputImageData()
UMA VEZ ou algumas vezes por segundo usandosetTimeout/seTInterval
.fonte
fillRect
foi doloroso porque a aceleração h / w do Chrome não consegue lidar com as chamadas individuais para a GPU que seriam necessárias. Acabei tendo que usar dados de pixel em 1: 1 e depois usar o dimensionamento CSS para obter a saída desejada. É feio :(get/putImageData
, mas 194.893 porfillRect
.1x1 image data
é 125.102 Ops / s. EntãofillRect
ganha de longe no Firefox. Então, as coisas mudaram muito entre 2012 e hoje. Como sempre, nunca confie nos resultados de benchmark antigos.Código de demonstração HTML rápido: Com base no que sei sobre a biblioteca de gráficos SFML C ++:
Salve isso como um arquivo HTML com codificação UTF-8 e execute-o. Sinta-se livre para refatorar, eu apenas gosto de usar variáveis japonesas porque elas são concisas e não ocupam muito espaço
Raramente você deseja definir UM pixel arbitrário e exibi-lo na tela. Então use o
método para desenhar vários pixels arbitrários em um buffer de fundo. (chamadas baratas)
Quando estiver pronto para mostrar, ligue para o
método para exibir as alterações. (chamada cara)
Código de arquivo .HTML completo abaixo:
fonte
Se você está preocupado com a velocidade, também pode considerar o WebGL.
fonte
HANDY e proposição da função colocar pixel (pp) (ES6) (ler pixel aqui ):
Mostrar snippet de código
Esta função usa
putImageData
e possui parte de inicialização (primeira linha longa). No início, em vezs='.myCanvas'
use o seletor CSS na tela.Se você deseja normalizar os parâmetros para valores de 0-1, deve alterar o valor padrão
a=255
paraa=1
e alinhar com:id.data.set([r,g,b,a]),ctx.putImageData(id, x, y)
paraid.data.set([r*255,g*255,b*255,a*255]),ctx.putImageData(id, x*c.width, y*c.height)
O código útil acima é bom para algoritmos gráficos de teste ad-hoc ou para fazer prova de conceito, mas não é bom usar na produção em que o código deve ser legível e claro.
fonte
putImageData
provavelmente é mais rápido quefillRect
nativamente. Eu acho que isso porque o quinto parâmetro pode ter maneiras diferentes de serem atribuídas (a cor do retângulo), usando uma string que deve ser interpretada.Suponha que você esteja fazendo isso:
Então a linha
é o mais pesado entre todos. O quinto argumento na
fillRect
chamada é uma string um pouco mais longa.fonte
context.fillStyle = ...
. developer.mozilla.org/pt-BR/docs/Web/API/…