Beispiel #1
0
        /// <summary>
        /// Clears the button at the given index and moves it to the endIdx-1 position.
        /// </summary>
        /// <param name="screenIdx">The index of the button to clear.</param>
        public void ClearBtn(int screenIdx)
        {
            if (screenIdx < startIdx || screenIdx >= endIdx)
            {
                throw new IndexOutOfRangeException("Index for this button manager must be in [" + startIdx + ", " + (endIdx - 1) + "], was " + screenIdx);
            }

            try
            {
                clickLock.WaitOne();
                for (int i = screenIdx + 1; i < endIdx; i++)
                {
                    GetBtn(i).SetIndex(i - 1);
                }
                BtnProps btn = btns[screenIdx];
                btns.RemoveAt(screenIdx);
                btns.Insert(endIdx - 1, btn);
                btn.SetIndex(endIdx - 1);
                btn.SetImage(null);
            }
            finally
            {
                clickLock.ReleaseMutex();
            }
        }
Beispiel #2
0
        private static void CalculateNextDrawCommand(uint colSpan, uint rowSpan, BtnProps btn, int res, int[] line)
        {
            Bitmap img = btn.screenImg;

            for (uint col = 0; col < line.Length; col++)
            {
                int x = (int)(col * colSpan);

                if (colSpan == 1 && rowSpan == 1)
                {
                    // nothing to average for a 1x1 square of color
                    line[col] = ImageMagic.ColorConvertColorToInt(img.GetPixel(x, (int)btn.rowIdx[res]));
                }
                else if (colSpan * rowSpan < 625_000) // arbitrary 10MB limit
                {
                    // calculate the median color
                    List <Tuple <Color, int> > colorSwatch = new List <Tuple <Color, int> >((int)(colSpan * rowSpan));
                    colorSwatch.OrderBy(t => t.Item2);
                    for (int i = 0; i < colSpan; i++)
                    {
                        for (int j = 0; j < rowSpan; j++)
                        {
                            int   y         = (int)(btn.rowIdx[res] * rowSpan);
                            Color color     = img.GetPixel(x + i, y + j);
                            int   intensity = color.R + color.G + color.B;
                            colorSwatch.Add(new Tuple <Color, int>(color, intensity));
                        }
                    }
                    Color c = colorSwatch[colorSwatch.Count / 2].Item1;
                    line[col] = ImageMagic.ColorConvertColorToInt(c);
                }
                else
                {
                    // calculate the average color
                    ulong[] uc = new ulong[4];
                    for (int i = 0; i < colSpan; i++)
                    {
                        for (int j = 0; j < rowSpan; j++)
                        {
                            int   y     = (int)(btn.rowIdx[res] * rowSpan);
                            Color color = img.GetPixel(x + i, y + j);
                            uc[0] += color.R;
                            uc[1] += color.G;
                            uc[2] += color.B;
                        }
                    }
                    uint pixelCnt = colSpan * rowSpan;
                    uint r        = (uint)uc[0] / pixelCnt;
                    uint g        = (uint)uc[1] / pixelCnt;
                    uint b        = (uint)uc[2] / pixelCnt;
                    line[col] = ImageMagic.ColorConvertRGBTo24(r, g, b);
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// Queues up an image to be drawn to an Arduino screen.
        /// A low-res image (8x8) will be loaded first and in reverse queue order for speed,
        /// then a medium-res image (40x32) will be loaded in queue order,
        /// then finally a full-res image (160x128) will be loaded in queue order.
        ///
        /// Queueing up a new image for the same screen will
        /// restart the process imediately and the old image data
        /// will be forgotten.
        /// </summary>
        /// <param name="screenIdx">The screen to draw to.</param>
        /// <param name="updateLowRes">True to push the image for the given screen in low resolution to the <see cref="comm"/></param>
        /// <param name="updateMedRes">True to push the image for the given screen in medium resolution to the <see cref="comm"/></param>
        /// <param name="updateHighRes">True to push the image for the given screen in high resolution to the <see cref="comm"/></param>
        public void QueueImage(int screenIdx, bool updateLowRes = true, bool updateMedRes = true, bool updateHighRes = true)
        {
            try
            {
                queueMutex.WaitOne();

                // discard previous updates
                if (updateLowRes)
                {
                    lowResUpdates.Remove(screenIdx);
                }
                if (updateMedRes)
                {
                    medResUpdates.Remove(screenIdx);
                }
                if (updateHighRes)
                {
                    highResUpdates.Remove(screenIdx);
                }

                // add new update
                if (updateLowRes)
                {
                    lowResUpdates.Add(screenIdx);
                }
                if (updateMedRes)
                {
                    medResUpdates.Insert(0, screenIdx);
                }
                if (updateHighRes)
                {
                    highResUpdates.Insert(0, screenIdx);
                }

                // reset the rowIdx for the updates, as necessary
                BtnProps btn = btns[screenIdx];
                if (updateLowRes)
                {
                    btn.rowIdx[0] = 0;
                }
                if (updateMedRes)
                {
                    btn.rowIdx[1] = 0;
                }
                if (updateHighRes)
                {
                    btn.rowIdx[2] = 0;
                }
            }
            finally
            {
                queueMutex.ReleaseMutex();
            }
        }
Beispiel #4
0
        public BtnProps GetBtn(int screenIdx)
        {
            if (screenIdx < startIdx || screenIdx >= endIdx)
            {
                throw new IndexOutOfRangeException("Index for this button manager must be in [" + startIdx + ", " + (endIdx - 1) + "], was " + screenIdx);
            }
            BtnProps btn = btns[screenIdx];

            if (btn.idx != screenIdx)
            {
                throw new InvalidOperationException("The button at index " + screenIdx + " has an internally registered index of " + screenIdx);
            }
            return(btn);
        }
Beispiel #5
0
        /// <summary>
        /// Draws lines from the images waiting in the update stacks/queues.
        /// Only draws one line of the most low-res image to keep the thread responsive.
        /// </summary>
        /// <returns>True when an update succeeds or there is no update to do, false when an update fails.</returns>
        public bool Update()
        {
            uint     colSpan   = 0;
            uint     rowSpan   = 0;
            int      screenIdx = -1;
            BtnProps btn       = null;
            Bitmap   img       = null;
            int      res       = 0; // low (0), med (1), high (2)

            try
            {
                queueMutex.WaitOne();

                // get the quality to draw a line at
                screenIdx = -1;
                btn       = null;
                if (lowResUpdates.Count > 0)
                {
                    colSpan   = 20;
                    rowSpan   = 16;
                    screenIdx = lowResUpdates[0];
                    btn       = btns[screenIdx];
                    res       = 0;
                    if ((btn.rowIdx[res] + 1) * rowSpan == btn.screenHeight)
                    {
                        lowResUpdates.RemoveAt(0);
                    }
                    if (btn.rowIdx[res] == 0)
                    {
                        Console.WriteLine($"drawing low-rez {screenIdx}");
                    }
                }
                else if (medResUpdates.Count > 0)
                {
                    colSpan   = 4;
                    rowSpan   = 4;
                    screenIdx = medResUpdates[medResUpdates.Count - 1];
                    btn       = btns[screenIdx];
                    res       = 1;
                    if ((btn.rowIdx[res] + 1) * rowSpan == btn.screenHeight)
                    {
                        medResUpdates.RemoveAt(medResUpdates.Count - 1);
                    }
                    if (btn.rowIdx[res] == 0)
                    {
                        Console.WriteLine($"drawing med-rez {screenIdx}");
                    }
                }
                else if (highResUpdates.Count > 0)
                {
                    colSpan   = 1;
                    rowSpan   = 1;
                    screenIdx = highResUpdates[highResUpdates.Count - 1];
                    btn       = btns[screenIdx];
                    res       = 2;
                    if ((btn.rowIdx[res] + 1) * rowSpan == btn.screenHeight)
                    {
                        highResUpdates.RemoveAt(highResUpdates.Count - 1);
                    }
                    if (btn.rowIdx[res] == 0)
                    {
                        Console.WriteLine($"drawing high-rez {screenIdx}");
                    }
                }

                // for images we can't update, pretend like we did an update
                if (btn == null || btn.comm == null)
                {
                    if (btn != null)
                    {
                        btn.rowIdx[res]++;
                    }
                    return(true);
                }

                // get the image
                img = btn.screenImg;

                // calculate the line to draw
                int[] line = new int[btn.screenWidth / colSpan];
                CalculateNextDrawCommand(colSpan, rowSpan, btn, res, line);

                // draw the line
                if (btn.rowIdx[res] == 0)
                {
                    swFrame.Restart();
                }
                if (!btn.comm.SendImageRow(line, btn.rowIdx[res], rowSpan, btn))
                {
                    return(false);
                }

                // prepare for next time this function is called
                btn.rowIdx[res]++;
                if (btn.rowIdx[res] * rowSpan >= btn.screenHeight)
                {
                    btn.rowIdx[res] = 0;
                    Console.WriteLine(swFrame.ElapsedMilliseconds);
                }
            }
            finally
            {
                queueMutex.ReleaseMutex();
            }

            return(true);
        }
 /// <summary>
 /// Tries to send an image to the
 /// </summary>
 /// <param name="row">The row data to draw.</param>
 /// <param name="startRow">The starting y-index of the row (top).</param>
 /// <param name="rowSpan">The number of vertical pixels to draw this same row over (aka inverse of img resolution).</param>
 /// <returns>True on success, false on failure. If false, then the device is most likely disconnected.</returns>
 public abstract bool SendImageRow(int[] row, uint startRow, uint rowSpan, BtnProps btn);
        public override bool SendImageRow(int[] line, uint startRow, uint rowSpan, BtnProps btn)
        {
            Stopwatch timer = new Stopwatch();

            timer.Start();
            int idx = 0;

            // check that WIDTH is a multiple of the line length
            uint colSpan = screenWidth / (uint)line.Length;

            if (line.Length * colSpan != screenWidth)
            {
                throw new ArgumentException($"The display width ({screenWidth}) must be a multiple of the line length ({line.Length})");
            }

            // send packetized data
            if (startRow == 0)
            {
                byte[] restart = new byte[62];                // buffer to be sent
                idx            = BitBashing.UIntToDecimalBytes(ref restart, 0, 2, 0);
                restart[idx++] = Convert.ToByte(':');
                restart[idx++] = Convert.ToByte('R');         // "Reset Draw" command
                restart[idx++] = Convert.ToByte('L');
                if (waitingForAck)
                {
                    WaitForAck(1000);
                }
                if (!CheckedWrite(restart, 0, idx))
                {
                    return(false);
                }
                waitingForAck = true;
            }

            // send colSpan and rowSpan
            byte[] spans = new byte[62];                      // buffer to be sent
            idx          = BitBashing.SIntToDecimalBytes(ref spans, 0, 5, 0);
            spans[idx++] = Convert.ToByte(':');
            spans[idx++] = Convert.ToByte('S');               // "Span Size" command
            idx         += BitBashing.UIntToHexBytes(ref spans, idx, colSpan, 2, 2);
            idx         += BitBashing.UIntToHexBytes(ref spans, idx, rowSpan, 2, 2);
            if (waitingForAck)
            {
                WaitForAck(1000);
            }
            if (!CheckedWrite(spans, 0, idx))
            {
                return(false);
            }
            waitingForAck = true;

            // determine the necessity of a palette and send the palette
            bool usePalette = btn.palette != null && colSpan < 16 && rowSpan < 16;

            if (usePalette && startRow == 0 && btn.doUpdatePalette)
            {
                SendColorPalette(btn.palette);
                btn.SetDoUpdatePalette(false);
            }

            // send packetized line color data
            byte[] linePart = new byte[62];                   // buffer to be sent
            int    linePartLen;                               // packetized line message length
            int    bytesPerPixel = (usePalette) ? 1 : 2;

            linePartLen = (int)(56 / bytesPerPixel);
            char command = (usePalette) ? 'l' : 'L';

            for (int i = 0; i < line.Length; i += linePartLen)
            {
                int sendCnt = Math.Min(linePartLen, line.Length - i);

                // prepare the message header
                int pixelCnt = (usePalette) ? sendCnt : sendCnt * bytesPerPixel;
                idx             = BitBashing.SIntToDecimalBytes(ref linePart, 0, pixelCnt + 1, 0);
                linePart[idx++] = Convert.ToByte(':');
                linePart[idx++] = Convert.ToByte(command);    // "Line Part" or "line (palette) Part" command

                // add the color values
                for (int j = 0; j < sendCnt; j++)
                {
                    int    col     = idx + j * bytesPerPixel;
                    ushort color16 = ImageMagic.ColorConvert24To16(line[i + j]);
                    if (usePalette)
                    {
                        linePart[col] = btn.palette.GetPaletteColor(color16);
                    }
                    else
                    {
                        linePart[col + 1] = BitConverter.GetBytes(color16)[0];
                        linePart[col + 0] = BitConverter.GetBytes(color16)[1];
                    }
                }

                // send the message
                if (waitingForAck)
                {
                    WaitForAck(1000);
                }
                if (!CheckedWrite(linePart, 0, pixelCnt + idx))
                {
                    return(false);
                }
                waitingForAck = true;
            }

            //Console.WriteLine("c#: " + timer.ElapsedMilliseconds);

            return(true);
        }