//Funkcja wywoływana dla każdego obrazu. Odpowiada za skalowanie public void interpolate(object param) { //Rzutowanie typu - otrzymanie parametrów funkcji Polecenie paramP = param as Polecenie; string src = paramP.inputFile; string dst = paramP.outputFile; //Podział ścieżki źródłowej na segmenty foldery i pliki string[] split = src.Split(new[] { "\\" }, StringSplitOptions.None); //Wyłuskanie rozszerzenia pliku z ostatniego segmentu ścieżki źródłowej string extension = split.Last().Split(new char[] { '.' }).Last().ToLower(); //Ustawienie formatu pliku wynikowego ImageFormat format; switch (extension) { case "jpg": { format = ImageFormat.Jpeg; break; } case "bmp": { format = ImageFormat.Bmp; break; } case "png": { format = ImageFormat.Png; break; } default: format = ImageFormat.Jpeg; break; } //Dodanie do ścieżki docelowej nazwy pliku z rozszerzeniem dst = dst + "\\" + split.Last(); //Tylko 1 wątek na raz może podjąć próbę zaalokowania pamięci memoryAllocation.WaitOne(); Image srcImage = null; Bitmap srcBitmap = null; Bitmap dstBitmap = null; //Tak długo, jak nie udało się zaalokować pamięci na obraz źródłowy, //jego bitmapę i bitmapę docelową... while (srcBitmap == null || dstBitmap == null || srcImage == null) { if (srcImage == null) { try { //Załaduj obraz z dysku do pamięci srcImage = Image.FromFile(src); } catch (OutOfMemoryException) { //Jeżeli się nie udało, przypisz null srcImage = null; } } if (srcImage != null && srcBitmap == null) { try { //Załaduj bitmapę z obrazu źródłowego srcBitmap = new Bitmap(srcImage); } catch (Exception) { //Jeżeli się nie udało, przypisz null srcBitmap = null; } } if (dstBitmap == null) { try { //Utworz bitmapę na obraz docelowy dstBitmap = new Bitmap(dstWidth, dstHeight); } catch (Exception) { //Jeżeli się nie udało, przypisz null dstBitmap = null; } } //Jeżeli nie udało się zaalokować pamięci na obraz źródłowy i bitmapy if (srcBitmap == null || dstBitmap == null || srcImage == null) { //"Zabierz" dla siebie semaforę oczekującą na zwolniene pamięci awaitingMemory.WaitOne(); //Czekaj, aż inny wątek zwolni semaforę (zrobi to po zwolnieniu używanych zasobów) //Górny limit czasu oczekiwania wynosi 5 minut awaitingMemory.WaitOne(300000); //Zwolnij zabraną semaforę try { awaitingMemory.Release(); } catch (SemaphoreFullException) { } } } //Po udanej alokacji pamięci, zwolnij semaforę memoryAllocation.Release(); //Zablokuj bitmapy do użytku przez niezarządzalny kod BitmapData srcData = srcBitmap.LockBits( new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); BitmapData dstData = dstBitmap.LockBits( new Rectangle(0, 0, dstBitmap.Width, dstBitmap.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); unsafe { //Wskaźniki na tablice pikseli byte *srcPtr = (byte *)srcData.Scan0; byte *dstPtr = (byte *)dstData.Scan0; //Wybór funkcji asm/C if (language == 0) { interpolateAsm(srcPtr, dstPtr, srcImage.Width, srcImage.Height, dstWidth, dstHeight); } else { interpolateC(srcPtr, dstPtr, srcImage.Width, srcImage.Height, dstWidth, dstHeight); } } //Odblokowanie bitmap po ich wykorzystaniu przez funkcje interpolacji srcBitmap.UnlockBits(srcData); dstBitmap.UnlockBits(dstData); try { //Zapis docelowego obrazu do pliku dstBitmap.Save(dst, format); } catch (ExternalException) { MessageBox.Show("Nie mozna zapisac do pliku\n" + dst); } //Zwolnienie zasobów dstBitmap.Dispose(); srcBitmap.Dispose(); srcImage.Dispose(); dstBitmap = null; srcBitmap = null; srcImage = null; //Tylko 1 wątek na raz może poinformować o zwolnieniu swoich zasobów checkingAwaitingMemory.WaitOne(); //Zwolnij semaforę, informując wątek oczekujący na zasoby o ich zwolnieniu awaitingMemory.WaitOne(0); awaitingMemory.Release(); //Zwolnij semaforę - kolejny wątek może informować o zwolnieniu zasobów checkingAwaitingMemory.Release(); this.progressBar1.Invoke(new Action(() => { //Zwiększ postęp na pasku postępu progressBar1.PerformStep(); //Jeżeli pasek się wypełnił, to zadanie zostało wykonane. //Wykonaj podsumowanie if (progressBar1.Value == progressBar1.Maximum) { //Skończ liczyć czas watch.Stop(); //Uaktywnij przyciski w GUI convertBtn.Enabled = true; destBtn.Enabled = true; sourceSelectBtn.Enabled = true; //Informacja tekstowa o zakończonej pracy infoLabel.Text = "Skalowanie zakończone!"; //Okno informujące o czasie trwania operacji MessageBox.Show("Konwersja trwala :" + watch.ElapsedMilliseconds.ToString() + "ms."); } })); }
//Funkcja wywoływana po naciśnięciu przycisku "Skaluj" private void convertBtn_Click(object sender, EventArgs e) { //Jeżeli nie wybrano obrazow if (selectedImages.Count == 0) { //Nie rób nic return; } //Ustawienie liczby pracujących wątków na dostępne wątki ThreadPool.SetMinThreads(1, 1); ThreadPool.SetMaxThreads(threadCount, threadCount); ThreadPool.SetMinThreads(threadCount, threadCount); //Przypisanie do zmiennej wyboru użytkownika dot. funkcji skalującej if (asmRadioBtn.Checked == true) { language = 0; } else { language = 1; } //Zablokowanie możliwości klikania przycisków convertBtn.Enabled = false; destBtn.Enabled = false; sourceSelectBtn.Enabled = false; //Utworzenie semafor memoryAllocation = new Semaphore(1, 1); awaitingMemory = new Semaphore(1, 1); checkingAwaitingMemory = new Semaphore(1, 1); //Pobranie z kontrolek rozdzielczości docelowej zdjęć dstWidth = (int)widthBox.Value; dstHeight = (int)heightBox.Value; infoLabel.Text = "Sprawdzanie zasobów..."; //Tymczasowa bitmapa Bitmap tempDst; try { //Sprawdzenie, czy można zaalokować wystarczająco pamięci na obrazek docelowy tempDst = new Bitmap((int)widthBox.Value, (int)heightBox.Value); } catch (Exception) { MessageBox.Show("Nie można zaalokować pamięci na obrazek docelowy."); //Jeżeli nie można, nie ma sensu liczyć dalej return; } //Struktura na pliki, które będą obsługiwane, tj. wystarczy pamięci na ich skalowanie List <string> imagesToProcess = new List <string>(); //Ustawienie paska postępu progressBar1.Maximum = selectedImages.Count; progressBar1.Value = 0; new Thread(() => { //Dla każdego obrazka foreach (string imgPath in selectedImages) { try { //Spróbuj załadować do pamięci i utworzyć z niego bitmapę Image srcImg = Image.FromFile(imgPath); Bitmap bmp = new Bitmap(srcImg); //Jeżeli udało się zarezerwować zasoby, dodaj obrazek do listy imagesToProcess.Add(imgPath); //Zwolnij użyte zasoby bmp.Dispose(); srcImg.Dispose(); progressBar1.Invoke(new Action(() => { //Aktualizuj postęp progressBar1.PerformStep(); //Po przetworzeniu wszystkich zdjęć if (progressBar1.Value == progressBar1.Maximum) { //Przygotuj i uruchom zegar zliczający czas trwania skalowania watch = new System.Diagnostics.Stopwatch(); watch.Start(); infoLabel.Text = "Sprawdzono pomyślnie. Trwa skalowanie..."; //Jeżeli nie wszystkie obrazy uda się przetworzyć, wyświetl komunikat if (imagesToProcess.Count != selectedImages.Count) { MessageBox.Show("Zasoby sprzetowe pozwolą obsłużyć " + imagesToProcess.Count + " z " + selectedImages.Count + " wybranych zdjęć."); } //Ustawienie paska postępu progressBar1.Value = 0; progressBar1.Maximum = imagesToProcess.Count; progressBar1.Step = 1; //Jeżeli katalog źródłowy = katalog docelowy if (createOut) { createOut = false; //Dodaj do katalogu docelowego końcówkę "out" dstDir += "\\out"; try { //Spróbuj utworzyć katalog docelowy System.IO.Directory.CreateDirectory(dstDir); } catch (UnauthorizedAccessException) { //Jeżeli jest to niemożliwe, wyświetl odpowiedni komunikat i zakończ MessageBox.Show("Nie mam uprawnien do utworzenia katalogu out..."); destBtn.Invoke(new Action(() => { destBtn.Enabled = true; sourceSelectBtn.Enabled = true; convertBtn.Enabled = true; })); return; } } //Dla każdego obrazu do przetworzenia foreach (string image in imagesToProcess) { //Przygotuj obiekt zawierający argumenty funkcji Polecenie param = new Polecenie(); param.inputFile = image; param.outputFile = dstDir; //Kolejkuj zdjęcie ThreadPool.QueueUserWorkItem(new WaitCallback(interpolate), param); } } })); } catch (Exception) //łapiemy out of memory i przy tworzeniu bitmapy { //nic nie robimy, po prostu na koniec obsluzymy mniej obrazow; } } }).Start(); }